from enum import Enum
from typing import Callable
from utils_evolution_tools import *

class Shape:

    class Shapes(Enum):
        SQUARE = 0,
        RECTANGLE = 1,
        RHOMBOID = 2,
        CIRCLE = 3,
        ELLIPSE = 4,
        # LINE = 5

    _square_like = set([Shapes.SQUARE, Shapes.CIRCLE])

    def __init__(self, shape:Shapes, x, y, a, b, a_off=0, b_off=0) -> None:
        self.shape = shape
        self.x = x
        self.y = y
        self.a = a
        self.b = b
        self.a_off = a_off
        self.b_off = b_off
    
    def __eq__(self, value: object) -> bool:
        if isinstance(value, Shape):
            return (
                self.shape == value.shape and 
                self.x == value.x and
                self.y == value.y and
                self.a == value.a and
                self.b == value.b and
                self.a_off == value.a_off and
                self.b_off == value.b_off
            )
        else:
            return False
        
    def __hash__(self) -> int:
        return hash((
            self.shape,
            self.x,
            self.y,
            self.a,
            self.b,
            self.a_off,
            self.b_off
        ))    
    
    def get_shape_a(self):
        if self.shape in Shape._square_like:
            return min(self.a,self.b)
        else:
            return self.a
    
    def get_shape_b(self):
        if self.shape in Shape._square_like:
            return min(self.a,self.b)
        else:
            return self.b
    
    def area(self):
        return self.get_shape_a()*self.get_shape_b()

class RoofArguments:
    def __init__(self, mode=None, a_x=0, a_z=0, b_x=0, b_z=0) -> None:
        self.mode = mode
        self.x = a_x
        self.z = a_z
        self.cx = b_x
        self.cz = b_z
        
    def get_args(self):
        return self.x, self.z, self.cx, self.cz, self.mode
    
    def __str__(self) -> str:
        return f"x={self.x:.2f} cx={self.cx:.2f}\nz={self.z:.2f} cz={self.cz:.2f}\nmode:{self.mode.name}"
        
class Structure:
    
    _zero = RoofArguments()
    
    def __init__(self, shape:Shape, levels, mode, mask, arg_2, arg_1, arg_f1, overhang=1):
        self._shape = shape #copy?
        self.levels = levels
        self.mode = mode
        self.mask = mask
        self.arg_2 = arg_2
        self.arg_1 = arg_1
        self.arg_f1 = arg_f1
        self.overhang = overhang
    
    @staticmethod
    def _get_arg(arg, use):
        if use:
            return arg.get_args()
        else:
            return Structure._zero.get_args()
        
    def get_args(self):
        return(
            self.mode,
            *Structure._get_arg(self.arg_2, self.mask[0]),
            *Structure._get_arg(self.arg_1, self.mask[1]),
            *Structure._get_arg(self.arg_f1, self.mask[2]),
        )

    @property
    def shape(self):
        return self._shape

class Gradient:
    
    def __init__(self, start_block:BlockType, end_block:BlockType, steps:int, representation, gradient_func:Callable, direction:tuple)-> None:
        self.start_block = start_block
        self.end_block = end_block
        self.steps = steps
        self.representation = representation
        self.gradient_func = gradient_func
        self.direction = direction
    