class BossException(Exception):

    inner_exceptions = {}

    def __init__(
        self,           # type: BossException
        error_code,     # type: int
        error_message,  # type: str
    ):
        # type: (...) -> None

        self.error_code = error_code
        self.error_message = error_message
        super(BossException, self).__init__("{} - {}".format(error_code, error_message))

    @classmethod
    def get_exception(
        cls,
        error_code,  # type: int
    ):
        # type: (...) -> BossException

        cls.get_inner_exceptions()
        exception_class = cls.inner_exceptions.get(error_code, UnexpectedError)
        return exception_class()

    @classmethod
    def get_inner_exceptions(
        cls,
    ):
        # type: (...) -> None

        for subclass in cls.__subclasses__():
            if hasattr(subclass, "error_code") and hasattr(subclass, "error_message"):
                cls.inner_exceptions[subclass.error_code] = subclass


class UnexpectedError(BossException):

    error_code = -1
    error_message = "Unexpected error"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class NoData(BossException):

    error_code = -2
    error_message = "No data"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class NoUtf8Data(BossException):

    error_code = -3
    error_message = "User data was not utf-8"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class InvalidDataFormat(BossException):

    error_code = -4
    error_message = "User data was not utf-8"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class NoUser(BossException):

    error_code = -5
    error_message = "User not found"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class NoPassword(BossException):

    error_code = -6
    error_message = "Invalid password data"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class AuthenticationFailed(BossException):

    error_code = -7
    error_message = "Authentication failed"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class AccessDenied(BossException):

    error_code = -8
    error_message = "Access denied"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )


class MissingMandatoryField(BossException):

    error_code = -9
    error_message = "Missing mandatory field"

    def __init__(
        self,  # type: BossException
    ):
        # type: (...) -> None

        BossException.__init__(
            self,
            error_code=self.error_code,
            error_message=self.error_message,
        )
