#

_STATE_REGISTRY = {}


class State(object):
    __slots__ = ('__value', '__grant', '__inheritable', '__propagatable')

    def __new__(
        cls, value: bool, *args, grant: bool = False, inheritable: bool = True, propagatable: bool = True, **kwargs
    ):
        key = (value, grant, inheritable, propagatable)
        instance = _STATE_REGISTRY.get(key)
        if instance is None:
            instance = super(State, cls).__new__(cls, *args, **kwargs)
            _STATE_REGISTRY[key] = instance
        return instance

    def __init__(
        self, value: bool, *args, grant: bool = False, inheritable: bool = True, propagatable: bool = True, **kwargs
    ):
        super().__init__(*args, **kwargs)
        if grant and not value:  # grant force value=True
            raise ValueError('grant is True but value is not True')
        self.__value = value
        self.__grant = grant
        self.__inheritable = inheritable
        self.__propagatable = propagatable

    def __bool__(self):
        return self.__value

    def __repr__(self):
        if self.__grant:
            text = 'GRANT'
        elif self.__value:
            text = 'True'
        else:
            text = 'False'
        flags = ('+I' if self.__inheritable else '-I') + ('+P' if self.__propagatable else '-P')
        return f'[{text}{flags}]'

    def with_value(self, value):
        return self.__class__(
            value=value,
            grant=self.__grant and value,
            inheritable=self.__inheritable,
            propagatable=self.__propagatable,
        )

    def with_grant(self, grant):
        return self.__class__(
            value=self.__value or grant,
            grant=grant,
            inheritable=self.__inheritable,
            propagatable=self.__propagatable,
        )

    def with_inheritable(self, inheritable):
        return self.__class__(
            value=self.__value,
            grant=self.__grant,
            inheritable=inheritable,
            propagatable=self.__propagatable,
        )

    def with_propagatable(self, propagatable):
        return self.__class__(
            value=self.__value,
            grant=self.__grant,
            inheritable=self.__inheritable,
            propagatable=propagatable,
        )

    @property
    def value(self):
        return self.__value

    @property
    def grant(self):
        return self.__grant

    @property
    def inheritable(self):
        return self.__inheritable

    @property
    def propagatable(self):
        return self.__propagatable

    @classmethod
    def normalize(cls, value, inheritable=True):
        if isinstance(value, cls):
            if inheritable is False:
                return value.with_inheritable(False)
            return value
        elif value is None:
            return None
        elif isinstance(value, bool):
            return cls(value, inheritable=inheritable)


# ----

TRUE = State(True, inheritable=True)
FALSE = State(False, inheritable=True)
GRANT = State(True, grant=True, inheritable=True)

TRUE_NI = TRUE.with_inheritable(False)
FALSE_NI = FALSE.with_inheritable(False)
GRANT_NI = GRANT.with_inheritable(False)

TRUE_NP = TRUE.with_propagatable(False)
FALSE_NP = FALSE.with_propagatable(False)
GRANT_NP = GRANT.with_propagatable(False)

GRANT_NINP = GRANT_NI.with_propagatable(False)
