"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""

import abc
from typing import Any

from ..checker._interface import TypeCheckerInterface
from ..converter import ValueConverterInterface
from ..error import TypeConversionError


class AbstractType(TypeCheckerInterface, ValueConverterInterface):
    __slots__ = (
        "_data",
        "_strict_level",
        "_params",
        "__checker",
        "__converter",
        "__is_type_result",
    )

    @abc.abstractproperty
    def typecode(self):  # pragma: no cover
        pass

    @abc.abstractmethod
    def _create_type_checker(self):  # pragma: no cover
        pass

    @abc.abstractmethod
    def _create_type_converter(self):  # pragma: no cover
        pass

    @property
    def typename(self) -> str:
        return self.typecode.name

    def __init__(self, value: Any, strict_level: int, **kwargs) -> None:
        self._data = value
        self._strict_level = strict_level
        self._params = kwargs

        self.__checker = self._create_type_checker()
        self.__converter = self._create_type_converter()

        self.__is_type_result = None

    def __repr__(self) -> str:
        return ", ".join(
            [
                "value={}".format(self._data),
                "typename={}".format(self.typename),
                "strict_level={}".format(self._strict_level),
                "is_type={}".format(self.is_type()),
                "try_convert={}".format(self.try_convert()),
            ]
        )

    def is_type(self) -> bool:
        """
        :return:
        :rtype: bool
        """

        if self.__is_type_result is not None:
            return self.__is_type_result

        self.__is_type_result = self.__is_type()

        return self.__is_type_result

    def __is_type(self):
        if self.__checker.is_type():
            return True

        if self.__checker.is_exclude_instance():
            return False

        try:
            self._converted_value = self.__converter.force_convert()
        except TypeConversionError:
            return False

        if not self.__checker.is_valid_after_convert(self._converted_value):
            return False

        return True

    def validate(self, error_message=None):
        """
        :raises TypeError:
            If the value is not matched the type that the class represented.
        """

        if self.is_type():
            return

        if not error_message:
            error_message = "invalid value type"

        raise TypeError(
            "{}: expected={}, actual={}".format(error_message, self.typename, type(self._data))
        )

    def convert(self):
        """
        :return: Converted value.
        :raises typepy.TypeConversionError:
            If the value cannot convert.
        """

        if self.is_type():
            return self.force_convert()

        raise TypeConversionError(
            "failed to convert from {} to {}".format(type(self._data).__name__, self.typename)
        )

    def force_convert(self):
        """
        :return: Converted value.
        :raises typepy.TypeConversionError:
            If the value cannot convert.
        """

        return self.__converter.force_convert()

    def try_convert(self):
        """
        :return: Converted value. |None| if failed to convert.
        """

        try:
            return self.convert()
        except TypeConversionError:
            return None

@@ -99,17 +99,60 @@
 port Any
-%0A
+, Optional%0A%0Afrom .._typecode import Typecode
 %0Afrom ..
@@ -557,24 +557,36 @@
 pecode(self)
+ -%3E Typecode
 :  # pragma:
@@ -1199,16 +1199,40 @@
 t = None
+  # type: Optional%5Bbool%5D
 %0A%0A    de
@@ -1886,24 +1886,32 @@
 s_type(self)
+ -%3E bool
 :%0A        if
@@ -2345,14 +2345,39 @@
 sage
-=None)
+: Optional%5Bstr%5D = None) -%3E None
 :%0A  

Add type annotations