#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

from __future__ import annotations;
from lark import Tree as larkTree;
from typing import Generator;
from typing import List;

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBALE KONSTANTEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# KLASSE: Syntaxbaum
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class SyntaxBaum(object):
    kind: str;
    expr: str;
    valence: int;
    children: List[SyntaxBaum];

    def __init__(self, fml: larkTree):
        self.kind = fml.data;
        self.children = [];
        self.valence = 0;
        expr_parts = []
        for child in fml.children:
            if isinstance(child, str):
                expr_parts.append(child);
            ## subfml is instance larkTree:
            elif child.data == 'symb':
                symb = str(child.children[0]);
                expr_parts.append(symb);
            else:
                subtree = SyntaxBaum(child);
                self.children.append(subtree);
                self.valence += 1;
                expr_parts.append(subtree.expr);
        self.expr = ' '.join(expr_parts);
        if self.valence > 1:
            self.expr = '(' + self.expr + ')';
        return;

    def __str__(self):
        return self.expr;

    def __iter__(self) -> Generator[SyntaxBaum, None, None]:
        for child in self.children:
            yield child;

    @property
    def child(self) -> SyntaxBaum:
        return self.children[0];

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # METHOD: Pretty
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def pretty(self, preindent: str = '', tab: str = '  ', prepend: str = '', depth: int = 0) -> str:
        indent = preindent + tab*depth;
        if self.valence == 0 and self.kind in [ 'atom', 'generic' ]:
            return indent + prepend + self.kind + ' ' + self.expr;
        return '\n'.join(
            [indent + prepend + self.kind] \
            + [subtree.pretty(preindent, tab, '|__ ', depth+1) for subtree in self.children]
        );

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # METHODS: Erkennung von Formeltypen
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def isIrreducible(self) -> bool:
        return self.valence == 0;

    def isAtom(self) -> bool:
        return self.kind == 'atom';

    def isLiteral(self) -> bool:
        return self.isAtom() or (self.isNegation() and self.child.isAtom());

    def isGeneric(self) -> bool:
        return self.kind == 'generic';

    def isTautologySymbol(self) -> bool:
        return self.kind == 'taut';

    def isContradictionSymbol(self) -> bool:
        return self.kind == 'contradiction';

    def isConnective(self) -> bool:
        return self.valence > 0;

    def isNegation(self) -> bool:
        return self.kind == 'not';

    def isConjunction2(self) -> bool:
        return self.kind == 'and2';

    def isConjunction(self) -> bool:
        return self.kind in ['and', 'and2'];

    def isDisjunction2(self) -> bool:
        return self.kind == 'or2';

    def isDisjunction(self) -> bool:
        return self.kind in ['or', 'or2'];

    def isImplication(self) -> bool:
        return self.kind == 'implies';
