#!/usr/bin/env python3
"""
YACE returns errors except for the truly exceptional cases.
"""
from pydantic import BaseModel


class Error(BaseModel):
    message: str

    def __str__(self):
        return f"error.message: {self.message}"


class ParseError(Error):
    filename: str
    line: int
    column: int

    @classmethod
    def from_exception(cls, exception, cursor):
        errors = "\n".join(
            [f"{count}: {error}" for count, error in enumerate(exception.errors())]
        )

        return cls(
            message=errors,
            filename=cursor.location.file.name if cursor.location.file else "unknown",
            line=cursor.location.line if cursor.location.line else -1,
            column=cursor.location.column if cursor.location.column else -1,
        )

    @classmethod
    def from_cursor(cls, message, cursor):
        return cls(
            message=message,
            filename=cursor.location.file.name if cursor.location.file else "unknown",
            line=cursor.location.line if cursor.location.line else -1,
            column=cursor.location.column if cursor.location.column else -1,
        )

    def __str__(self):
        return (
            f"{self.filename}; line: {self.line}, "
            f"column: {self.column}, error: {self.message}"
        )


class UnsupportedDatatype(ParseError):
    @classmethod
    def from_cursor(cls, cursor, message=None):
        if not message:
            message = f"Unsupported datatype({cursor.type.spelling})"

        return cls(
            message=message,
            filename=cursor.location.file.name if cursor.location.file else "unknown",
            line=cursor.location.line if cursor.location.line else -1,
            column=cursor.location.column if cursor.location.column else -1,
        )


class TransformationError(Exception):
    """Something went wrong during a yid-transformation"""

    pass


class ToolError(Exception):
    """Exception raised when a tool exists with non-zero returncode"""

    pass


class InvalidModelData(Exception):
    """The data given to entity instantion is not valid"""

    pass


class CSubsetIntegrityError(ParseError):
    """
    To provide meaningful error messages when a C file uses unsupported language
    features or violates Yace conventions, errors are captured by C Subset Integrity
    Errors, which inherit from this class.

    This error itself is typically not instantiated directly; instead, errors inherit
    from it. The error message is generated by reusing the description from the
    docstring.

    Integrity errors inherit from ParseError and use the docstring to provide the error
    message.
    """

    @classmethod
    def from_cursor(cls, message, cursor):
        return cls.from_cursor(cls.__doc__, cursor)


class InvalidFunctionPointerSymbol(CSubsetIntegrityError):
    """
    Function pointers must be named with a suffix of: '_cb', '_callback', '_fn', '_fun',
    '_func', or '_function'. The function-pointer found here breaks this convention.
    """

    pass


class MissingFunctionPointerArgumentSymbol(CSubsetIntegrityError):
    """
    Function pointers arguments must have symbol names. The function-pointer found here
    break this convention.
    """

    pass


class MissingDocstring(CSubsetIntegrityError):
    """
    To provide usable APIs and their bindings, proper documentation of data structures,
    magic values, functions, and their arguments is required. This error occurs when a
    docstring for the entity in question is missing.
    """

    pass
