from typing import Iterable, List
from abc import abstractmethod
from draw import code_to_image as cti

class Iinstruction:
    """Base class for all instructions"""

    def __init__(self, instruction_text: str) -> None:
        self.instruction_text = instruction_text

    @abstractmethod
    def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]:
        pass

    @abstractmethod
    def getblksize(self) -> float:
        return self._getblksize()

    @abstractmethod
    def __str__(self) -> str:
        pass

    def _getblksize(self) -> float:
        return cti.get_text_size(self.instruction_text)[1]


class generic_instruction(Iinstruction):
    """Any instruction that is not a control structure"""

    def __init__(self, instruction_text: str) -> None:
        Iinstruction.__init__(self, instruction_text)

    def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]:
        return cti.draw_generic_instruction(self.instruction_text, x, y, x_sz, self.getblksize())

    def __str__(self) -> str:
        return self.instruction_text

class if_instruction(Iinstruction):
    """Conditional structure
        NOT.
        A.
        LOOP
    """

    def __init__(self, instruction_text: str, true_case: List[Iinstruction], false_case: List[Iinstruction]=None) -> None:
        Iinstruction.__init__(self, instruction_text)
        self.true_case = true_case
        self.false_case = false_case

    def get_truesize(self) -> float:
        sz = 0.0
        for inst in self.true_case:
            sz += inst.getblksize()
        return sz

    def get_falsesize(self) -> float:
        sz = 0.0
        if self.false_case:
            for inst in self.false_case:
                sz += inst.getblksize()
        return sz

    def getblksize(self) -> float:
        return self._getblksize() + max(self.get_truesize(), self.get_falsesize())
    
    def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]:
        true_x, true_y, true_sz_x, _, false_x, false_y, false_sz_x, _ = cti.draw_if_statement(
            self.instruction_text, x, y, x_sz, self.getblksize()
        )
        self.draw_true_case(true_x, true_y, true_sz_x)
        self.draw_false_case(false_x, false_y, false_sz_x)
        blk_size = self.getblksize()
        return x, y + blk_size

    def draw_true_case(self, x: float, y:float, x_sz:float):
        for instruction in self.true_case:
            x, y = instruction.to_image(x, y, x_sz)

    def draw_false_case(self, x: float, y:float, x_sz:float):
        if self.false_case:
            for instruction in self.false_case:
                x, y = instruction.to_image(x, y, x_sz)

    def __str__(self) -> str:
        res = f"if({self.instruction_text}) {'{'}\n"
        for inst in self.true_case:
            res += '\t'+str(inst)+";\n"
        res += "}"
        if self.false_case:
            res += " else {"
            for inst in self.true_case:
                res += '\t'+str(inst)+";\n"
            res += "}"
        return res

#TODO
# class switch_instruction(Iinstruction):
#     """Switch structure"""

#     def __init__(self, instruction_text: str, cases: List[List[Iinstruction]]) -> None:
#         Iinstruction.__init__(self, instruction_text)
#         self.child_cases = cases
    
#     def to_image(self, x:int, y:int, x_sz: int, y_sz: int) -> Iterable[float]:
#         """TODO: implement"""
#         return []

#     def draw_children(self, x:float, y:float, x_sz:float, y_sz:float) -> float:
#         """TODO: implement"""
#         return 0.0



class while_instruction_front(Iinstruction):

    def __init__(self, condition: str, instructions: List[Iinstruction]) -> None:
        Iinstruction.__init__(self, condition)
        self.child_instructions = instructions

    def get_children_size(self) -> float:
        children_sz = 0
        for inst in self.child_instructions:
            children_sz += inst.getblksize()
        return children_sz

    def getblksize(self) -> float:
        return self._getblksize() + self.get_children_size()
    
    def to_image(self, x:int, y:int, x_sz: int) -> Iterable[float]:
        children_x, children_y, children_sz_x = cti.draw_while_loop_front(self.instruction_text, x, y, x_sz, self.getblksize())
        self.draw_children(children_x, children_y, children_sz_x)

        return x, y + self.getblksize()


    def draw_children(self, x:float, y:float, x_sz:float):
        for inst in self.child_instructions:
            x, y = inst.to_image(x, y, x_sz)
        return self.get_children_size()

    def __str__(self) -> str:
        res = "while(" + self.instruction_text + "){\n"
        for inst in self.child_instructions:
            res += '\t'+str(inst)+";\n"
        res += '}'
        return res


class while_instruction_back(while_instruction_front):
    def __init__(self, condition: str, instructions: List[Iinstruction]) -> None:
        while_instruction_front.__init__(self, condition, instructions)
    
    def to_image(self, x:int, y:int, x_sz: int):
        children_x, children_y, children_sz_x = cti.draw_while_loop_back(self.instruction_text, x, y, x_sz, self.getblksize())
        self.draw_children(children_x, children_y, children_sz_x)
        return x, y + self.getblksize()

    def __str__(self) -> str:
        res = "do{\n"
        for inst in self.child_instructions:
            res += '\t' +str(inst) + ";\n"
        res += f"{'}'}while({self.instruction_text});"
        return res