from copy import deepcopy
from enum import Enum
from dataclasses import dataclass


_DEOLINGO_ATOM_PREFIX = "deolingo_"


@dataclass
class DeonticAtom:

    short_name: str
    long_name: str
    is_tel_next: bool = False
    is_tel_prev: bool = False
    is_tel_init: bool = False

    @property
    def name(self):
        return self.long_name

    def prefixed(self):
        pre_name = self.prefix(self.name)
        if self.is_tel_prev:
            return "'" + pre_name
        elif self.is_tel_next:
            return pre_name + "'"
        elif self.is_tel_init:
            return "_" + pre_name
        return pre_name

    def prefixed_short_name(self):
        return self.prefix(self.short_name)

    def prefixed_long_name(self):
        return self.prefix(self.long_name)

    def wrap(self, atom):
        return f"{self.prefixed()}({atom})"

    @staticmethod
    def prefix(atom):
        """Returns a string representing the prefix for the atoms generated by deolingo."""
        return _DEOLINGO_ATOM_PREFIX + atom

    @staticmethod
    def unprefix(atom):
        """Returns the atom without the prefix 'deolingo_'."""
        return atom[len(_DEOLINGO_ATOM_PREFIX):]


class DeonticAtoms(Enum):
    """
    Enumeration of the deontic atoms.
    """
    OBLIGATORY = DeonticAtom("ob", "obligatory")
    FORBIDDEN = DeonticAtom("fb", "forbidden")
    OMISSIBLE = DeonticAtom("om", "omissible")
    PERMITTED = DeonticAtom("pm", "permitted")
    OPTIONAL = DeonticAtom("op", "optional")
    PERMITTED_BY_DEFAULT = DeonticAtom("pm_d", "permitted_by_default")
    OMISSIBLE_BY_DEFAULT = DeonticAtom("om_d", "omissible_by_default")
    HOLDS = DeonticAtom("h", "holds")
    DEONTIC = DeonticAtom("deo", "deontic")
    PERMITTED_IMPLICITLY = DeonticAtom("pm_i", "permitted_implicitly")
    OMISSIBLE_IMPLICITLY = DeonticAtom("om_i", "omissible_implicitly")
    VIOLATED = DeonticAtom("viol", "violated")
    FULFILLED = DeonticAtom("ful", "fulfilled")
    VIOLATED_OBLIGATION = DeonticAtom("ob_v", "violated_obligation")
    FULFILLED_OBLIGATION = DeonticAtom("ob_f", "fulfilled_obligation")
    NON_VIOLATED_OBLIGATION = DeonticAtom("ob_nv", "non_violated_obligation")
    NON_FULFILLED_OBLIGATION = DeonticAtom("ob_nf", "non_fulfilled_obligation")
    UNDETERMINED_OBLIGATION = DeonticAtom("ob_u", "undetermined_obligation")
    DEFAULT_OBLIGATION = DeonticAtom("ob_d", "default_obligation")
    VIOLATED_PROHIBITION = DeonticAtom("fb_v", "violated_prohibition")
    FULFILLED_PROHIBITION = DeonticAtom("fb_f", "fulfilled_prohibition")
    NON_VIOLATED_PROHIBITION = DeonticAtom("fb_nv", "non_violated_prohibition")
    NON_FULFILLED_PROHIBITION = DeonticAtom("fb_nf", "non_fulfilled_prohibition")
    UNDETERMINED_PROHIBITION = DeonticAtom("fb_u", "undetermined_prohibition")
    DEFAULT_PROHIBITION = DeonticAtom("fb_d", "default_prohibition")

    @classmethod
    def get_all(cls):
        """Returns all the deontic atoms from the enum."""
        return [atom for atom in DeonticAtoms]

    @classmethod
    def get_all_names(cls):
        """Returns all the names of the deontic atoms from the enum."""
        return [atom.value.name for atom in cls.get_all()]

    @classmethod
    def with_name(cls, name):
        """Returns the deontic atom with the given name."""
        is_tel_prev = name.startswith("'")
        is_tel_next = name.endswith("'")
        is_tel_init = name.startswith("_")
        if is_tel_prev or is_tel_next:
            name = name.replace("'", "")
        if is_tel_init:
            name = name.lstrip("_")
        for atom in cls.get_all():
            if atom.value.name == name:
                new_atom = deepcopy(atom.value)
                new_atom.is_tel_prev = is_tel_prev
                new_atom.is_tel_next = is_tel_next
                new_atom.is_tel_init = is_tel_init
                return new_atom
        return None

    @classmethod
    def with_prefixed_name(cls, name):
        """Returns the deontic atom with the given prefixed name."""
        for atom in cls.get_all():
            if atom.value.prefixed() == name:
                return atom
        return None


def deontic(deontic_atom):
    return DeonticAtoms.DEONTIC.value.wrap(deontic_atom)


def violated(deontic_atom):
    return DeonticAtoms.VIOLATED.value.wrap(deontic_atom)


def fulfilled(deontic_atom):
    return DeonticAtoms.FULFILLED.value.wrap(deontic_atom)


def obligatory(deontic_atom):
    return DeonticAtoms.OBLIGATORY.value.wrap(deontic_atom)


def forbidden(deontic_atom):
    return DeonticAtoms.FORBIDDEN.value.wrap(deontic_atom)


def optional(deontic_atom):
    return DeonticAtoms.OPTIONAL.value.wrap(deontic_atom)


def holds(deontic_atom):
    return DeonticAtoms.HOLDS.value.wrap(deontic_atom)


def permitted_implicitly(deontic_atom):
    return DeonticAtoms.PERMITTED_IMPLICITLY.value.wrap(deontic_atom)


def omissible_implicitly(deontic_atom):
    return DeonticAtoms.OMISSIBLE_IMPLICITLY.value.wrap(deontic_atom)


def omissible(deontic_atom):
    return DeonticAtoms.OMISSIBLE.value.wrap(deontic_atom)


def permitted(deontic_atom):
    return DeonticAtoms.PERMITTED.value.wrap(deontic_atom)


def permitted_by_default(deontic_atom):
    return DeonticAtoms.PERMITTED_BY_DEFAULT.value.wrap(deontic_atom)


def omissible_by_default(deontic_atom):
    return DeonticAtoms.OMISSIBLE_BY_DEFAULT.value.wrap(deontic_atom)


def violated_obligation(deontic_atom):
    return DeonticAtoms.VIOLATED_OBLIGATION.value.wrap(deontic_atom)


def fulfilled_obligation(deontic_atom):
    return DeonticAtoms.FULFILLED_OBLIGATION.value.wrap(deontic_atom)


def non_violated_obligation(deontic_atom):
    return DeonticAtoms.NON_VIOLATED_OBLIGATION.value.wrap(deontic_atom)


def non_fulfilled_obligation(deontic_atom):
    return DeonticAtoms.NON_FULFILLED_OBLIGATION.value.wrap(deontic_atom)


def undetermined_obligation(deontic_atom):
    return DeonticAtoms.UNDETERMINED_OBLIGATION.value.wrap(deontic_atom)


def default_obligation(deontic_atom):
    return DeonticAtoms.DEFAULT_OBLIGATION.value.wrap(deontic_atom)


def violated_prohibition(deontic_atom):
    return DeonticAtoms.VIOLATED_PROHIBITION.value.wrap(deontic_atom)


def fulfilled_prohibition(deontic_atom):
    return DeonticAtoms.FULFILLED_PROHIBITION.value.wrap(deontic_atom)


def non_violated_prohibition(deontic_atom):
    return DeonticAtoms.NON_VIOLATED_PROHIBITION.value.wrap(deontic_atom)


def non_fulfilled_prohibition(deontic_atom):
    return DeonticAtoms.NON_FULFILLED_PROHIBITION.value.wrap(deontic_atom)


def undetermined_prohibition(deontic_atom):
    return DeonticAtoms.UNDETERMINED_PROHIBITION.value.wrap(deontic_atom)


def default_prohibition(deontic_atom):
    return DeonticAtoms.DEFAULT_PROHIBITION.value.wrap(deontic_atom)
