from .interfaz import *
import tkinter as tk
from tkinter import ttk
from sympy import symbols
from sympy.logic.boolalg import Not, And, Or, Implies, to_dnf, to_cnf, to_anf, simplify_logic
from sympy.parsing.sympy_parser import parse_expr

BUSCADOR_WIDTH = 20
TABLA_PADX = 50

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

        panel = tk.Frame(self)
        panel.pack(expand=True, fill='x')

        # 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 búsqueda
        tk.Button(panelConsulta, text='🔎', font=TEXT_FONT, width=SINGLE_CHAR_BUTTON_WIDTH, command=self.buscar).pack(side='left', padx=GRAL_PAD, pady=GRAL_PAD)

        # 2. Operadores lógicos
        panelOperadores = tk.Frame(panel)
        panelOperadores.pack()
        self.replacements = {'¬': '~', '!': '~', 'Not': '~', 'not': '~',
                            '∧': '&', 'And': '&', 'and': '&',
                            '∨': '|', 'Or': '|', 'or': '|',
                            '⇒': '>>', '→': '>>', '->': '>>', '=>': '>>', 'Implies': '>>', 'implies': '>>'} # Operadores
        self.symbols = {chr(i): symbols(chr(i))  for i in range(97, 123)} | \
                       {chr(i): symbols(chr(i)) for i in range(65, 91)} # Minúsculas y mayúsculas

        # 2.1. Not (¬)
        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. And (∧)
        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. Or (∨)
        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.4. Implies (⇒)
        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)

    def __addChar(self, char: str) -> None:
        self.consulta.insert(tk.INSERT, char)
        
    def buscar(self) -> bool:
        rawExpr = self.consulta.get()
        expr = self.parser(rawExpr)
        if expr != '':
            self.controlador.hideFrame(self.controlador.getCurrFrame())
            self.controlador.setFrame(FrameRespuesta, args=(rawExpr, expr,))
        else:
            self.controlador.warningField(self.consulta)

    def parser(self, expr: str) -> str:
        try:
            for replacement in self.replacements:
                expr = expr.replace(replacement, self.replacements[replacement])            
            expr = parse_expr(expr, evaluate=False, local_dict=self.symbols)
            return expr
        except:
            return ''

class FrameRespuesta(tk.Frame):
    def __init__(self, controlador, rawExpr, expr):
        self.controlador = controlador
        super().__init__(controlador)

        self.panel = tk.Frame(self)
        self.panel.pack(expand=True, fill='x', padx=TABLA_PADX)

        # Conversiones
        exprDnf = self.convertir(expr, 'dnf')
        exprCnf = self.convertir(expr, 'cnf')
        exprAnf = self.convertir(expr, 'anf')

        self.__setTabla(rawExpr, exprDnf, exprCnf, exprAnf)

    def __setTabla(self, exprOriginal, exprDnf, exprCnf, exprAnf):

        # Estilo
        style = ttk.Style()
        style.configure('mystyle.Treeview', font=TEXT_FONT) # Filas
        style.configure('mystyle.Treeview.Heading', font=TEXT_FONT + ('bold',)) # Encabezados

        # Tabla
        self.tabla = ttk.Treeview(self.panel, columns=('Forma', 'Expresión'), show='headings', style='mystyle.Treeview')
        self.tabla.heading('Forma', text='Forma')
        self.tabla.heading('Expresión', text='Expresión')

        # Filas
        self.tabla.insert('', 'end', values=('Original', exprOriginal))
        self.tabla.insert('', 'end', values=('DNF', exprDnf))
        self.tabla.insert('', 'end', values=('CNF', exprCnf))
        self.tabla.insert('', 'end', values=('ANF', exprAnf))

        self.tabla.pack(expand=True, fill='x')

    def convertir(self, expr: str, tipo: str) -> str:
        match tipo:
            case 'dnf':
                finalExpr = self.parser(str(to_dnf(expr, simplify=True)))
            case 'cnf':
                finalExpr = self.parser(str(to_cnf(expr, simplify=True)))
            case 'anf':
                finalExpr = self.parser(str(to_anf(expr)))
        return finalExpr

    def parser(self, expr):
        return expr.replace('~', '¬').replace('|', '∨').replace('&', '∧').replace('^', '⊻')

def main():
    app = Controlador(
        title='II. Lógica y Conjuntos — Conversor lógico de Expresiones',
        firstFrame=FrameBuscador,
        height=500, width=700
    )
    app.mainloop()

if __name__ == '__main__':
    main()
