# configuration_base.py
#
# Project: AutoArchive
# License: GNU GPLv3
#
# Copyright (C) 2003 - 2025 Róbert Čerňanský



__all__ = ["ConfigurationBase"]



from abc import abstractmethod, ABCMeta
from typing import Any

from . import OptionsUtils, Option
from ._option_value import _OptionValue



class ConfigurationBase(metaclass = ABCMeta):
    "Provides access to configuration options."

    @abstractmethod
    def __init__(self):

        self.options_: dict[Option, _OptionValue] = {}



    def __getitem__(self, option: Option) -> Any | None:
        """Gets consolidated value of a configuration option.

        Method takes into account values of other option forms such as :term:`negation form <negation option form>` and
        :term:`force form <force option form>`.  For example if the raw value of the option FOO is 10 and raw value of
        FORCE_FOO is 5 then value 5 will be returned.

        The precedence order of option forms is following: force form > negation form > normal form; with force form
        as the highest and normal form as the lowest priority form.

        For options of type ``bool`` value ``None`` is never returned; it is converted to ``False``.

        :param option:  The option in the *normal form* for which the value should be returned.
        :type option: :class:`.Option`

        :return: The merged value of the passed ``option`` (can be ``None``).
        :rtype: ``object``

        :raise KeyError: If ``option`` does not exist."""

        optionForceForm = OptionsUtils.tryGetForceForm(option)
        if optionForceForm is not None:
            if self.isOptionPresent(optionForceForm):
                return self.getRawValue(optionForceForm)

        optionNegationForm = OptionsUtils.tryGetNegationForm(option)
        if optionNegationForm is not None:
            negationValue = self.getRawValue(optionNegationForm)
            if negationValue:
                return False

        # bool-type options can not return None
        if option._optType is bool:
            return self.getRawValue(option) or False

        return self.getRawValue(option)



    def __setitem__(self, option: Option, value: Any):
        if option in self.options_:
            defaultValue = self.options_[option].defaultValue
        else:
            defaultValue = None
        self.options_[option] = _OptionValue(value, defaultValue)



    def getRawValue(self, option: Option) -> Any | None:
        """Gets the raw value of a configuration option or ``None``.

        Unlike the :meth:`__getitem__()` this method returns the real raw value or default value of the ``option``.

        :param option: The option for which the value should be returned.
        :type option: :class:`.Option`

        :return: The raw value of the passed ``option`` (can be ``None``).
        :rtype: ``object``

        :raise KeyError: If ``option`` does not exist."""

        if self.options_[option].value is not None:
            return self.options_[option].value
        else:
            return self.options_[option].defaultValue



    def isOptionPresent(self, option: Option) -> bool:
        """Determines whether ``option`` is specified (e. g. in command line or a configuration file).

        :param option: The option which presence shall be determined.
        :type option: :class:`.Option`

        :return: `True` if `option` is present, `False` otherwise.
        :rtype: `bool`

        :raise KeyError: If ``option`` does not exists."""

        return self.options_[option].value is not None
