#cython: boundscheck=False, wraparound=False, initializedcheck=False

cimport cython

from finesse.cymath cimport complex_t
from finesse.cymath.gaussbeam cimport transform_q
from finesse.simulations.base cimport NodeBeamParam
from finesse.simulations.basematrix cimport CarrierSignalMatrixSimulation
from finesse.cmatrix cimport SubCCSView1DArray
from finesse.symbols import Symbol

from cpython.ref cimport PyObject

import logging

ctypedef (double*, ) ptr_tuple_1


LOGGER = logging.getLogger(__name__)


cdef class LensValues(BaseCValues):
    def __init__(LensValues self):
        cdef ptr_tuple_1 ptr = (&self.f, )
        cdef tuple params = ("f", )
        self.setup(params, sizeof(ptr), <double**>&ptr)


cdef class LensConnections:
    def __cinit__(self, CarrierSignalMatrixSimulation sim):
        self.P1i_P2o = SubCCSView1DArray(sim.carrier.optical_frequencies.size)
        self.P2i_P1o = SubCCSView1DArray(sim.carrier.optical_frequencies.size)

        self.conn_ptrs.P1i_P2o = <PyObject**>self.P1i_P2o.views
        self.conn_ptrs.P2i_P1o = <PyObject**>self.P2i_P1o.views


cdef class LensWorkspace(KnmConnectorWorkspace):
    def __init__(self, owner, CarrierSignalMatrixSimulation sim, refill):
        super().__init__(
            owner,
            sim,
            refill,
            refill,
            LensConnections(sim),
            None,
            LensValues()
        )
        self.lc = self.carrier.connections
        self.lv = self.values

        self.P1i_id = sim.carrier.node_id(owner.p1.i)
        self.P1o_id = sim.carrier.node_id(owner.p1.o)
        self.P2i_id = sim.carrier.node_id(owner.p2.i)
        self.P2o_id = sim.carrier.node_id(owner.p2.o)

        self.sym_abcd_Cx = NULL
        self.sym_abcd_Cy = NULL

    def __dealloc__(self):
        cy_expr_free(self.sym_abcd_Cx)
        cy_expr_free(self.sym_abcd_Cy)

    cpdef compile_abcd_cy_exprs(self):
        cdef:
            dict abcd_handles = self.owner._abcd_matrices
            tuple keyx = (self.owner.p1.i, self.owner.p2.o, "x")
            tuple keyy = (self.owner.p1.i, self.owner.p2.o, "y")
            cdef object[:, ::1] Mx_sym = abcd_handles[keyx][0]
            cdef object[:, ::1] My_sym = abcd_handles[keyy][0]

        # NOTE (sjr) Only element C of a Lens ABCD matrix can possibly change

        if isinstance(Mx_sym[1][0], Symbol):
            ch_sym = Mx_sym[1][0].expand_symbols().eval(keep_changing_symbols=True)
            if isinstance(ch_sym, Symbol):
                self.sym_abcd_Cx = cy_expr_new()
                cy_expr_init(self.sym_abcd_Cx, ch_sym)

        if isinstance(My_sym[1][0], Symbol):
            ch_sym = My_sym[1][0].expand_symbols().eval(keep_changing_symbols=True)
            if isinstance(ch_sym, Symbol):
                self.sym_abcd_Cy = cy_expr_new()
                cy_expr_init(self.sym_abcd_Cy, ch_sym)

    cpdef update_parameter_values(self):
        ConnectorWorkspace.update_parameter_values(self)

        if self.sym_abcd_Cx != NULL:
            self.abcd_x[1][0] = cy_expr_eval(self.sym_abcd_Cx)

        if self.sym_abcd_Cy != NULL:
            self.abcd_y[1][0] = cy_expr_eval(self.sym_abcd_Cy)

# TODO (sjr) make c_lens_fill function?
