from .interfaz import *
from numpy import pi, sin, cos, linspace, radians

SLIDER_INC = 0.01
TRIG_GRAPH_RESOLUTION = 100

class TrigonometriaTresFrame(TrigonometriaFrame):
    def __init__(self, controlador):
        self.controlador = controlador
        super().__init__(controlador)

        # 1. Inicialización de interfaz general
        self.setPanels(ejes=3, toolBar=False)

        # 2. Gráfica
        self._animating = False
        self.xLim = [-pi/2, 3*pi/2]
        self.A = 1
        self.omega = radians(180)
        self.__updateGraph(init=True)

        # 3. Paneles de interacción con la gráfica
        # 3.1. Slider de tiempo
        self.panelTiempo = tk.Frame(self.panelDown)
        self.panelTiempo.pack(fill='x', expand=True)
        tk.Label(self.panelTiempo, text='Tiempo\n(t)', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='left', padx=GRAL_PAD)
        self.sliderTiempo = tk.Scale(self.panelTiempo, from_=self.xLim[0], to=self.xLim[1], font=SLIDER_FONT, resolution=SLIDER_INC, orient='horizontal', command=self.__updateGraph)
        self.sliderTiempo.pack(side='left', fill='x', expand=True, padx=GRAL_PAD)
        self.sliderTiempo.set(1)

        # 3.2. Slider de amplitud
        self.panelA = tk.Frame(self.panelDown)
        self.panelA.pack(fill='x', expand=True)
        tk.Label(self.panelA, text='A', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='left', padx=GRAL_PAD)
        self.sliderA = tk.Scale(self.panelA, from_=0.0, to=2.0, font=SLIDER_FONT, resolution=SLIDER_INC, orient='horizontal', command=self.__updateGraph)
        self.sliderA.pack(side='left', fill='x', expand=True, padx=GRAL_PAD)
        self.sliderA.set(self.A)

        # 3.3. Slider de frecuencia angular
        self.panelOmega = tk.Frame(self.panelDown)
        self.panelOmega.pack(fill='x', expand=True)
        tk.Label(self.panelOmega, text='ω\n(grados)', font=TEXT_FONT, width=LABEL_GRAL_WIDTH).pack(side='left', padx=GRAL_PAD)
        self.sliderOmega = tk.Scale(self.panelOmega, from_=0, to=180, font=SLIDER_FONT, resolution=1, orient='horizontal', command=self.__updateGraph)
        self.sliderOmega.pack(side='left', fill='x', expand=True, padx=GRAL_PAD)
        self.sliderOmega.set(180)

        # 3.4. Botón para animar
        self.panelAnimar = tk.Frame(self.panelDown)
        self.panelAnimar.pack(fill='x', expand=True)
        self.buttonAnimar = tk.Button(self.panelAnimar, text='Animar', font=TEXT_FONT, padx=GRAL_PAD, command=self.animar)
        self.buttonAnimar.pack()

    def __updateGraph(self, _=None, *, init=False):
        if init:
            self.fig.set_constrained_layout(True)
            self.ejes[0].set_box_aspect(1)
            self.setCirculoGraph() # Eje izquierdo
            self.setTrigGraphs() # Ejes medio y derecho
        else:
            self.t = self.sliderTiempo.get()
            self.A = self.sliderA.get()
            self.omega = radians(self.sliderOmega.get())
            self.updateCirculoGraph()
            self.updateTrigGraphs()
        plt.legend()
        self.canvas.draw()

    def setTrigGraphs(self):
        self.x = linspace(*self.xLim, TRIG_GRAPH_RESOLUTION)

        # Configuraciones generales
        for i in [1, 2]:
            self.ejes[i].set_ylim(-2.0, 2.0)
            self.ejes[i].set_xlim(self.xLim)
            self.ejes[i].grid(True)
            self.ejes[i].set_box_aspect(1)
            
        # Eje medio
        self.ejes[1].set_title('Posición del pistón')
        y_sin = self.A * sin(self.omega * self.x)
        self.sin_line, = self.ejes[1].plot(self.x, y_sin, color='red', label=rf'${self.A:.3g}\sin{{({self.omega:.3g}t)}}$')
        y_sin_t = self.A * sin(self.omega * self.t)
        self.pointSin, = self.ejes[1].plot([self.t], [y_sin_t], marker='o', linewidth=0, color='red', label=rf'$({self.t:.3g},{y_sin_t:.3g})$')
        self.ejes[1].legend()

        # Eje derecho
        self.ejes[2].set_title('Proyección horizontal')
        y_cos = self.A * cos(self.omega * self.x)
        self.cos_line, = self.ejes[2].plot(self.x, y_cos, color='blue', label=rf'${self.A:.3g}\cos{{({self.omega:.3g}t)}}$')
        y_cos_t = self.A * cos(self.omega * self.t)
        self.pointCos, = self.ejes[2].plot([self.t], [y_cos_t], marker='o', linewidth=0, color='blue', label=rf'$({self.t:.3g},{y_cos_t:.3g})$')
        self.ejes[2].legend()

    def updateTrigGraphs(self):
        y_sin = self.A * sin(self.omega * self.x)
        y_cos = self.A * cos(self.omega * self.x)

        # Líneas
        self.sin_line.set_data(self.x, y_sin)
        self.cos_line.set_data(self.x, y_cos)

        # Puntos
        y_sin_t = self.A * sin(self.omega * self.t)
        y_cos_t = self.A * cos(self.omega * self.t)
        self.pointSin.set_data([self.t], [y_sin_t])
        self.pointCos.set_data([self.t], [y_cos_t])

        # Labels
        self.sin_line.set_label(rf'${self.A:.3g}\sin{{({self.omega:.3g}t)}}$')
        self.cos_line.set_label(rf'${self.A:.3g}\cos{{({self.omega:.3g}t)}}$')
        self.pointSin.set_label(rf'$({self.t:.3g},{y_sin_t:.3g})$')
        self.pointCos.set_label(rf'$({self.t:.3g},{y_cos_t:.3g})$')
        self.ejes[1].legend()
        self.ejes[2].legend()

    def __animationStep(self):
        if self._animating:
            current = self.sliderTiempo.get()
            nxt = current + SLIDER_INC
            if nxt >= self.xLim[1]:
                self.sliderTiempo.set(self.xLim[1])
                self._animating = False
                self.buttonAnimar.config(text='Animar')
                return
            self.sliderTiempo.set(nxt)
            self._after_id = self.after(50, self.__animationStep)

    def animar(self):
        if self._animating: # Detener
            self._animating = False
            self.buttonAnimar.config(text='Animar')
            if getattr(self, '_after_id', None):
                try:
                    self.after_cancel(self._after_id)
                except Exception:
                    pass
                self._after_id = None
        else: # Iniciar
            self._animating = True
            self.buttonAnimar.config(text='Detener')
            self.__animationStep()

def main():
    app = Controlador(
        title='III. Funciones — Círculo unitario y Proyecciones',
        firstFrame=TrigonometriaTresFrame,
        height=700, width=1200
    )
    app.mainloop()

if __name__ == '__main__':
    main()
