from .interfaz import *
import tkinter as tk
from matplotlib import pyplot as plt
from matplotlib_venn import venn2, venn3
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

WIDTH_CONJUNTO = 10
PADY_CONJUNTO = 5
MARGIN_GRAL_PAD = 10
BUSCADOR_WIDTH = 20
SINGLE_CHAR_BUTTON_WIDTH = 3

class FrameConjuntos(tk.Frame):
    def __init__(self, controlador):
        self.controlador = controlador
        super().__init__(controlador)

        # 1. Instrucciones
        panelInstrucciones = tk.Frame(self)
        panelInstrucciones.pack(fill='x')
        tk.Label(panelInstrucciones, text='Introduzca los elementos de cada conjunto separados por comas:', font=TEXT_FONT).pack(anchor='nw', padx=MARGIN_GRAL_PAD, pady=MARGIN_GRAL_PAD)

        # 2. Conjuntos
        panelConjuntos = tk.Frame(self)
        panelConjuntos.pack(expand=True)

        # 2.1. A
        panelA = tk.Frame(panelConjuntos, pady=PADY_CONJUNTO)
        panelA.pack()
        tk.Label(panelA, text='A = {', font=TEXT_FONT).pack(side='left')
        self.entryA = tk.Entry(panelA, font=TEXT_FONT, width=WIDTH_CONJUNTO, highlightthickness=1, highlightbackground='white')
        self.entryA.pack(side='left')
        tk.Label(panelA, text='}', font=TEXT_FONT).pack(side='left')

        # 2.2. B
        panelB = tk.Frame(panelConjuntos, pady=PADY_CONJUNTO)
        panelB.pack()
        tk.Label(panelB, text='B = {', font=TEXT_FONT).pack(side='left')
        self.entryB = tk.Entry(panelB, font=TEXT_FONT, width=WIDTH_CONJUNTO, highlightthickness=1, highlightbackground='white')
        self.entryB.pack(side='left')
        tk.Label(panelB, text='}', font=TEXT_FONT).pack(side='left')

        # 2.3. C
        panelC = tk.Frame(panelConjuntos, pady=PADY_CONJUNTO)
        panelC.pack()
        tk.Label(panelC, text='C = {', font=TEXT_FONT).pack(side='left')
        self.entryC = tk.Entry(panelC, font=TEXT_FONT, width=WIDTH_CONJUNTO, highlightthickness=1, highlightbackground='white')
        self.entryC.pack(side='left')
        tk.Label(panelC, text='}', font=TEXT_FONT).pack(side='left')

        # 3. Botón para continuar
        panelDown = tk.Frame(self)
        panelDown.pack(side='bottom', anchor='ne')
        tk.Button(panelDown, text='Continuar', font=TEXT_FONT, command=self.__nextFrame).pack(padx=MARGIN_GRAL_PAD, pady=MARGIN_GRAL_PAD)

    def __nextFrame(self):
        conjuntos = self.parser()
        if conjuntos != ():
            self.controlador.hideFrame(self.controlador.getCurrFrame())
            self.controlador.setFrame(FrameDiagrama, args=conjuntos+(False,))

    def parser(self):
        cnt = 0
        try:
            conjuntoA = set(map(int, self.entryA.get().split(',')))
            cnt += 1
            conjuntoB = set(map(int, self.entryB.get().split(',')))
            cnt += 1
            if self.entryC.get() == '':
                conjuntoC = set()
            else:
                conjuntoC = set(map(int, self.entryC.get().split(',')))
            conjuntos = conjuntoA, conjuntoB, conjuntoC
        except:
            if cnt == 0:
                field = self.entryA
            elif cnt == 1:
                field = self.entryB
            else:
                field = self.entryC
            self.controlador.warningField(field)
            conjuntos = ()
        
        return conjuntos

class FrameDiagrama(tk.Frame):
    def __init__(self, controlador, conjuntoA, conjuntoB, conjuntoC=None, showElements=False):
        self.controlador = controlador
        super().__init__(controlador)

        if conjuntoC is None:
            conjuntoC = set()
        self.conjuntos = {'A': conjuntoA, 'B': conjuntoB, 'C': conjuntoC, 'U': set(range(1, 11))}

        panel = tk.Frame(self)
        panel.pack(expand=True, fill='both')
        
        # 1. Consulta
        panelConsulta = tk.Frame(panel)
        panelConsulta.pack()
        
        # 1.1. Campo de consulta
        self.consulta = tk.Entry(panelConsulta, font=TEXT_FONT, width=BUSCADOR_WIDTH)
        self.consulta.pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 1.2. Botón de igualdad
        tk.Button(panelConsulta, text='=', font=TEXT_FONT, width=SINGLE_CHAR_BUTTON_WIDTH, command=self.calcular).pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 2. Operadores
        panelOperadores = tk.Frame(panel)
        panelOperadores.pack()
        self.conjuntos = {'A': conjuntoA, 'B': conjuntoB, 'C': conjuntoC, 'U': set(range(1, 11))}

        # 2.1. Unión (⋃)
        tk.Button(panelOperadores, text='⋃', font=TEXT_FONT, width=SINGLE_CHAR_BUTTON_WIDTH, command=lambda: self.__addChar('⋃')).pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 2.2. Intersección (⋂)
        tk.Button(panelOperadores, text='⋂', font=TEXT_FONT, width=SINGLE_CHAR_BUTTON_WIDTH, command=lambda: self.__addChar('⋂')).pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 2.3. Diferencia (-)
        tk.Button(panelOperadores, text='-', font=TEXT_FONT, width=SINGLE_CHAR_BUTTON_WIDTH, command=lambda: self.__addChar('-')).pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 2.3. Complemento (⬚ᶜ)
        tk.Button(panelOperadores, text='⬚ᶜ', font=TEXT_FONT, width=SINGLE_CHAR_BUTTON_WIDTH, command=lambda: self.__addChar('ᶜ')).pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 3. Gráfica
        self.fig = plt.figure()
        self.ejes = self.fig.add_subplot()
        panelGraph = tk.Frame(panel)
        panelGraph.pack(side='bottom', fill='both', expand=True)
        self.canvas = FigureCanvasTkAgg(self.fig, master=panelGraph)

        # Dibujar Venn
        if conjuntoC == set():
            venn = venn2((conjuntoA, conjuntoB), ('A', 'B'), ax=self.ejes)
        else:
            venn = venn3((conjuntoA, conjuntoB, conjuntoC), ('A', 'B', 'C'), ax=self.ejes)

        if showElements:
            if conjuntoC == set():
                venn.get_label_by_id('10').set_text('\n'.join(map(str, conjuntoA - conjuntoB)))
                venn.get_label_by_id('01').set_text('\n'.join(map(str, conjuntoB - conjuntoA)))
                venn.get_label_by_id('11').set_text('\n'.join(map(str, conjuntoA & conjuntoB)))
            else:
                venn.get_label_by_id('100').set_text('\n'.join(map(str, conjuntoA - conjuntoB - conjuntoC)))
                venn.get_label_by_id('010').set_text('\n'.join(map(str, conjuntoB - conjuntoA - conjuntoC)))
                venn.get_label_by_id('001').set_text('\n'.join(map(str, conjuntoC - conjuntoA - conjuntoB)))
                venn.get_label_by_id('110').set_text('\n'.join(map(str, (conjuntoA & conjuntoB) - conjuntoC)))
                venn.get_label_by_id('101').set_text('\n'.join(map(str, (conjuntoA & conjuntoC) - conjuntoB)))
                venn.get_label_by_id('011').set_text('\n'.join(map(str, (conjuntoB & conjuntoC) - conjuntoA)))
                venn.get_label_by_id('111').set_text('\n'.join(map(str, conjuntoA & conjuntoB & conjuntoC)))

        self.canvas.get_tk_widget().pack(fill='both', expand=True)

        # cleanup: cuando el frame se destruya, cerramos la figura y widget de matplotlib
        self.bind("<Destroy>", self._on_destroy, add=True)

    def __addChar(self, char: str) -> None:
        self.consulta.insert(tk.INSERT, char)

    def parser(self, expr: str) -> str:
        try:
            # 1. ᶜᶜ -> '' (complementos crudamente anidados)
            expr = expr.replace('ᶜᶜ', '')
                
            # 2. Xᶜ -> (X^U)
            expr = expr.replace('Aᶜ', '(A^U)').replace('Bᶜ', '(B^U)').replace('Cᶜ', '(C^U)')

            # 3. (...)ᶜ -> ((...)^U)
            exprAux = expr
            rightPtr = len(expr) - 2
            while True:
                parentesis = 0
                for i in range(rightPtr, -1, -1):
                    if parentesis == 0:
                        if expr[i:i+2] == ')ᶜ':
                            parentesis = 1
                            rightPtr = i
                    else:
                        if expr[i] == ')':
                            parentesis += 1
                        elif expr[i] == '(':
                            parentesis -= 1
                        if parentesis == 0:
                            segment = expr[i:rightPtr+2]
                            exprAux = exprAux.replace(segment, f'({segment[:-1]}^U)')
                            rightPtr -= 1
                            break
                else:
                    break
            expr = exprAux
            expr = expr.replace('⋂', '&').replace('⋃', '|')

            # usar self.conjuntos como locals, globals vacío
            return eval(expr, {}, self.conjuntos)
        except:
            return ''

    def calcular(self):
        expr = self.parser(self.consulta.get())
        if expr == set():
            self.__updateGraphTitle('{}')
        elif expr != '':
            self.__updateGraphTitle(expr)
        else:
            self.controlador.warningField(self.consulta)

    def __updateGraphTitle(self, title):
        self.ejes.set_title(title)
        self.canvas.draw()

    def _on_destroy(self, event):
        # Si el widget ya está siendo destruido, limpiar recursos de matplotlib
        try:
            widget = getattr(self, 'canvas', None)
            if widget is not None:
                widget.get_tk_widget().destroy()
            fig = getattr(self, 'fig', None)
            if fig is not None:
                plt.close(fig)
        except Exception:
            pass

def main():
    app = Controlador(
        title='II. Lógica y Conjuntos — Operación de 2 o 3 Conjuntos y Diagrama de Venn',
        firstFrame=FrameConjuntos,
        height=600, width=775
    )
    app.mainloop()

if __name__ == '__main__':
    main()
