from .interfaz import *
from numpy import linspace, sin, cos, atan, pi, radians, degrees
from sympy import symbols, sympify, solve, latex

RESOLUTION = 1000

class TrigonometriaOperacionesBasicas(SimpleInteractiveGraphFrame):
    def __init__(self, controlador):
        self.controlador = controlador
        super().__init__(controlador)
        
        # 1. Inicialización de interfaz general
        self.setPanels(showEjes=False, toolBar=False)

        # 2. Paneles de interacción con la gráfica
        # 2.1. Panel izquierdo
        self.panelDownLeft = tk.Frame(self.panelDown)
        self.panelDownLeft.pack(side='left', fill='x', expand=True)

        # 2.1.1. Droplist de la incógnita
        panelIncognita = tk.Frame(self.panelDownLeft)
        panelIncognita.pack(side='right', padx=GRAL_PAD)
        self.incognitaVar = tk.StringVar(value='Hipotenusa')
        self.incognita = 'Hipotenusa'
        tk.Label(panelIncognita, text='Incógnita', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='top', padx=GRAL_PAD)
        self.menuIncognita = tk.OptionMenu(panelIncognita, self.incognitaVar, 'Cateto adyacente', 'Cateto opuesto', 'Hipotenusa', command=self.__updateIncognita)
        self.menuIncognita.config(font=TEXT_FONT)
        self.menuIncognita['menu'].config(font=TEXT_FONT)
        self.menuIncognita.pack(side='top', padx=GRAL_PAD)
        
        # 2.1.2. Slider del cateto adyacente
        self.panelCatetoAdyacente = tk.Frame(self.panelDownLeft, highlightthickness=1, highlightbackground='white')
        self.panelCatetoAdyacente.pack(side='top', fill='x', expand=True)
        tk.Label(self.panelCatetoAdyacente, text='Cateto\nadyacente', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='left', padx=GRAL_PAD)
        self.sliderCatetoAdyacente = tk.Scale(self.panelCatetoAdyacente, from_=3, to=10, font=SLIDER_FONT, resolution=0.01, orient='horizontal', command=self.__updateCatetoAdyacente)
        self.sliderCatetoAdyacente.pack(side='left', fill='x', expand=True, padx=GRAL_PAD)
        self.sliderCatetoAdyacente.set(3)
        self.catetoAdyacente = 3

        # 2.1.3. Slider del cateto opuesto
        self.panelCatetoOpuesto = tk.Frame(self.panelDownLeft, highlightthickness=1, highlightbackground='white')
        self.panelCatetoOpuesto.pack(side='top', fill='x', expand=True)
        tk.Label(self.panelCatetoOpuesto, text='Cateto\nopuesto', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='left', padx=GRAL_PAD)
        self.sliderCatetoOpuesto = tk.Scale(self.panelCatetoOpuesto, from_=3, to=10, font=SLIDER_FONT, resolution=0.01, orient='horizontal', command=self.__updateCatetoOpuesto)
        self.sliderCatetoOpuesto.pack(side='left', fill='x', expand=True, padx=GRAL_PAD)
        self.sliderCatetoOpuesto.set(4)
        self.catetoOpuesto = 4

        # 2.1.4. Slider de la hipotenusa
        self.panelHipotenusa = tk.Frame(self.panelDownLeft, highlightthickness=1, highlightbackground='white')
        self.panelHipotenusa.pack(side='top', fill='x', expand=True)
        tk.Label(self.panelHipotenusa, text='Hipotenusa', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='left', padx=GRAL_PAD)
        self.sliderHipotenusa = tk.Scale(self.panelHipotenusa, from_=3, to=10, font=SLIDER_FONT, resolution=0.01, orient='horizontal', command=self.__updateHipotenusa)
        self.sliderHipotenusa.pack(side='left', fill='x', expand=True, padx=GRAL_PAD)
        self.sliderHipotenusa.set(5)
        self.hipotenusa = 5

        # 3. Gráfica
        self.panelIncognita = self.panelHipotenusa
        self.__updateGraph(init=True)

    def __checkReal(self, lado):
        if lado == 0 or isinstance(lado, complex):
            self.controlador.warningField(self.panelActual)
            return False
        return True

    def __setPuntos(self):
        self.puntoA = [0, 0]
        self.puntoB = [self.catetoAdyacente, 0]
        self.puntoC = [self.catetoAdyacente, self.catetoOpuesto]

    def __getTriangulo(self):
        self.panelIncognita.pack(side='top', fill='x', expand=True)
        self.catetoAdyacente = self.sliderCatetoAdyacente.get()
        self.catetoOpuesto = self.sliderCatetoOpuesto.get()
        self.hipotenusa = self.sliderHipotenusa.get()
        match self.incognita:
            case 'Hipotenusa':
                hipotenusa = (self.catetoAdyacente**2 + self.catetoOpuesto**2)**0.5
                self.panelHipotenusa.pack_forget()
                self.panelIncognita = self.panelHipotenusa
                if self.__checkReal(hipotenusa):
                    self.hipotenusa = hipotenusa
                    self.sliderHipotenusa.set(hipotenusa)
                    self.__setPuntos()
                else:
                    return None, None, None, None
                trianguloSinIncognita = zip(self.puntoA, self.puntoB, self.puntoC)
                trianguloIncognita = zip(self.puntoA, self.puntoC)
            case 'Cateto adyacente':
                catetoAdyacente = (self.hipotenusa**2 - self.catetoOpuesto**2)**0.5
                self.panelCatetoAdyacente.pack_forget()
                self.panelIncognita = self.panelCatetoAdyacente
                if self.__checkReal(catetoAdyacente):
                    self.catetoAdyacente = catetoAdyacente
                    self.sliderCatetoAdyacente.set(catetoAdyacente)
                    self.__setPuntos()
                else:
                    return None, None, None, None
                trianguloSinIncognita = zip(self.puntoA, self.puntoC, self.puntoB)
                trianguloIncognita = zip(self.puntoA, self.puntoB)
            case 'Cateto opuesto':
                catetoOpuesto = (self.hipotenusa**2 - self.catetoAdyacente**2)**0.5
                self.panelCatetoOpuesto.pack_forget()
                self.panelIncognita = self.panelCatetoOpuesto
                if self.__checkReal(catetoOpuesto):
                    self.catetoOpuesto = catetoOpuesto
                    self.sliderCatetoOpuesto.set(catetoOpuesto)
                    self.__setPuntos()
                else:
                    return None, None, None, None
                trianguloSinIncognita = zip(self.puntoC, self.puntoA, self.puntoB)
                trianguloIncognita = zip(self.puntoC, self.puntoB)
        return *trianguloSinIncognita, *trianguloIncognita

    def __updateGraph(self, _=None, *, init=False):
        trianguloSinIncognitaX, trianguloSinIncognitaY, trianguloIncognitaX, trianguloIncognitaY = self.__getTriangulo()
        if trianguloSinIncognitaX is not None:
            theta = atan(self.catetoOpuesto / self.catetoAdyacente)
            thetaDegrees = degrees(theta)
            arcoThetaX = [0.7 * cos(t) for t in np.linspace(0, theta, 100)]
            arcoThetaY = [0.7 * sin(t) for t in np.linspace(0, theta, 100)]
            if init:
                self.ejes.set_aspect('equal')
                self.trianguloSinIncognita, = self.ejes.plot(trianguloSinIncognitaX, trianguloSinIncognitaY, color='black')
                self.trianguloIncognita, = self.ejes.plot(trianguloIncognitaX, trianguloIncognitaY, color='black', linestyle='dashdot')
                self.textCatetoAdyacente = self.ejes.text(self.puntoA[0]+self.catetoAdyacente/2, self.puntoA[1], rf'${self.catetoAdyacente:.3g}$', bbox=WHITE_TEXT_BBOX)
                self.textCatetoOpuesto = self.ejes.text(self.puntoB[0], self.puntoB[1]+self.catetoOpuesto/2, rf'${self.catetoOpuesto:.3g}$', bbox=WHITE_TEXT_BBOX)
                self.textHipotenusa = self.ejes.text(*self.puntoC, rf'${self.hipotenusa:.3g}$', bbox=WHITE_TEXT_BBOX)
                self.arcoTheta, = self.ejes.plot(arcoThetaX, arcoThetaY, color='red', label=rf'$\theta ={thetaDegrees:.3g}°$')
                plt.legend()
            else:
                self.trianguloSinIncognita.set_data(trianguloSinIncognitaX, trianguloSinIncognitaY)
                self.trianguloIncognita.set_data(trianguloIncognitaX, trianguloIncognitaY)
                xText = self.puntoA[0] + self.catetoAdyacente/2
                yText = self.puntoB[1] + self.catetoOpuesto/2
                self.textCatetoAdyacente.set_position((xText, self.puntoA[1]))
                self.textCatetoAdyacente.set_text(rf'${self.catetoAdyacente:.3g}$')
                self.textCatetoOpuesto.set_position((self.puntoB[0], yText))
                self.textCatetoOpuesto.set_text(rf'${self.catetoOpuesto:.3g}$')
                self.textHipotenusa.set_position((xText, yText))
                self.textHipotenusa.set_text(rf'${self.hipotenusa:.3g}$')
                self.arcoTheta.set_data(arcoThetaX, arcoThetaY)
                self.arcoTheta.set_label(rf'$\theta ={thetaDegrees:.3g}°$')
                self.ejes.relim()
                self.ejes.autoscale_view()
                plt.legend()
                self.canvas.draw()

    def __updateIncognita(self, _=None):
        self.incognita = self.incognitaVar.get()
        self.__updateGraph()

    def __updateCatetoAdyacente(self, _=None):
        self.panelActual = self.panelCatetoAdyacente
        self.catetoAdyacente = self.sliderCatetoAdyacente.get()
        self.__updateGraph()

    def __updateCatetoOpuesto(self, _=None):
        self.panelActual = self.panelCatetoOpuesto
        self.catetoOpuesto = self.sliderCatetoOpuesto.get()
        self.__updateGraph()

    def __updateHipotenusa(self, _=None):
        self.panelActual = self.panelHipotenusa
        self.hipotenusa = self.sliderHipotenusa.get()
        self.__updateGraph()

def main():
    app = Controlador(
        title='III. Funciones — Triángulo rectángulo',
        firstFrame=TrigonometriaOperacionesBasicas,
        height=800, width=775
    )
    app.mainloop()

if __name__ == '__main__':
    main()
