#!/usr/bin/env python3

#This file imports the members defined in cellBase.h into a separated python module
# (from the "flat" module CLICopti.CLICopti generated by SWIG)

#The base class
from CLICopti.CLICopti import CellBase
#The various interpolators
from CLICopti.CLICopti import \
    CellBase_grid, \
    CellBase_compat, \
    CellBase_linearInterpolation, \
    CellBase_linearInterpolation_freqScaling

#Database paths
import os
celldatabase_TD_12GHz_v1 = os.path.join(os.path.dirname(__file__),\
                                        "TD_12GHz_v1.dat")
celldatabase_TD_12GHz_v2 = os.path.join(os.path.dirname(__file__),\
                                        "TD_12GHz_v2.dat")
celldatabase_TD_30GHz    = os.path.join(os.path.dirname(__file__),\
                                        "TD_30GHz.dat")

import numpy as np
import warnings

#Patch the constructors to be Pythonesque
# https://stackoverflow.com/a/19546169

_CellBase_linearInterpolation_CPPconstructor = CellBase_linearInterpolation.__init__
def _CellBase_linearInterpolation_PYconstructor(self,fname,interpolateVariables):
    import CLICopti.CellParams
    offsets,numIndices = CLICopti.CellParams.getOffsetArray_byString(interpolateVariables)

    _CellBase_linearInterpolation_CPPconstructor(self, fname, numIndices,offsets)

    #Save the proxy in the created object so that it doesn't go out of scope before the object dies
    self._offsets_proxy = offsets
CellBase_linearInterpolation.__init__ = _CellBase_linearInterpolation_PYconstructor

_CellBase_linearInterpolation_freqScaling_CPPconstructor = CellBase_linearInterpolation_freqScaling.__init__
def _CellBase_linearInterpolation_freqScaling_PYconstructor(self,fname,interpolateVariables,f0):
    import CLICopti.CellParams
    offsets,numIndices = CLICopti.CellParams.getOffsetArray_byString(interpolateVariables)

    _CellBase_linearInterpolation_freqScaling_CPPconstructor(self, fname, numIndices,offsets, f0)

    #Save the proxy in the created object so that it doesn't go out of scope before the object dies
    self._offsets_proxy = offsets
CellBase_linearInterpolation_freqScaling.__init__ = _CellBase_linearInterpolation_freqScaling_PYconstructor

_CellBase_compat_CPPconstructor = CellBase_compat.__init__
def _CellBase_compat_PYconstructor(self, fname, f0=29.985, havePhaseAdvance=True, scalingLevel=3):
    #Catch a few errors before calling the real constructor
    if (fname == celldatabase_TD_30GHz) and (havePhaseAdvance != True):
        warnings.warn("For this database file should normally set havePhaseAdvance=True")
    elif (fname == celldatabase_TD_12GHz_v1) and (havePhaseAdvance != False):
        warnings.warn("For this database file should normally set havePhaseAdvance=False")
    elif (fname == celldatabase_TD_12GHz_v2) and (havePhaseAdvance != False):
        warnings.warn("For this database file should normally set havePhaseAdvance=False")
    _CellBase_compat_CPPconstructor(self, fname, f0, havePhaseAdvance, scalingLevel)
CellBase_compat.__init__ = _CellBase_compat_PYconstructor


#Patch the grid idx or coords -> cell loaders
_CellBase_grid_getCellGrid_CPP = CellBase_grid.getCellGrid
def _CellBase_grid_getCellGrid_PY(self,gridIdx):

    from CLICopti.CLICopti import size_t_array
    offsets = size_t_array(len(gridIdx))
    for i, idx in enumerate(gridIdx):
        offsets[i] = idx

    cell = _CellBase_grid_getCellGrid_CPP(self,offsets)
    return cell
CellBase_grid.getCellGrid = _CellBase_grid_getCellGrid_PY

def _doubleArrayConvertTo(iterable):
    "Convert an iterable python object to a SWIG double_array which can be passed to the C++ library"
    from CLICopti.CLICopti import double_array
    array = double_array(len(iterable))
    for i,p in enumerate(iterable):
        if not (np.isscalar(p) and np.isreal(p)):
            raise TypeError(f'gridCoords must consist of an array of real numbers, got:"{p}", typeof:{type(p)}')
        array[i] = p
    return array

def _doubleArrayConvertFrom(array,len):
    """
    Convert a SWIG double_array to a numpy array, given the array and it's length.
    If length is specified wrong, we may access out of bounds and segfault!
    """
    #Can't trust __sizeof__()
    #if array.__sizeof__() != len*8:
    #    raise ValueError("Assuming 8-byte doubles, the len is wrong")
    from CLICopti.CLICopti import double_array
    arr = double_array.frompointer(array)
    r = np.empty(len)
    for i in range(len):
        r[i] = arr[i]
    return r

_CellBase_grid_getGridlabels_CPP = CellBase_grid.getGridlabels
def _CellBase_grid_getGridlabels_PY(self,dimension):
    "Return the gridlabels for the given dimension"
    rp = _CellBase_grid_getGridlabels_CPP(self,dimension)
    rl = self.getGridsort(dimension)
    return _doubleArrayConvertFrom(rp,rl)
CellBase_grid.getGridlabels = _CellBase_grid_getGridlabels_PY

def _CellBase_grid_getGrid_meshgrid_PY(self):
    "Creates a meshgrid corresponding to the underlying grid in the CellBase_grid class"
    ndims = self.numIndices
    labels = []
    for di in range(ndims):
        label=self.getGridlabels(di)
        labels.append(label)
    mg = np.meshgrid(*labels)
    return mg,labels
CellBase_grid.getGrid_meshgrid= _CellBase_grid_getGrid_meshgrid_PY

_CellBase_linearInterpolation_getCellInterpolated_CPP = CellBase_linearInterpolation.getCellInterpolated
def _CellBase_linearInterpolation_getCellInterpolated_PY(self, gridCoords):
    return _CellBase_linearInterpolation_getCellInterpolated_CPP(self, _doubleArrayConvertTo(gridCoords))
CellBase_linearInterpolation.getCellInterpolated = _CellBase_linearInterpolation_getCellInterpolated_PY

_CellBase_linearInterpolation_freqScaling_getCellInterpolated_CPP = CellBase_linearInterpolation_freqScaling.getCellInterpolated
def _CellBase_linearInterpolation_freqScaling_getCellInterpolated_PY(self, gridCoords):
    return _CellBase_linearInterpolation_freqScaling_getCellInterpolated_CPP(self, _doubleArrayConvertTo(gridCoords))
CellBase_linearInterpolation_freqScaling.getCellInterpolated = _CellBase_linearInterpolation_freqScaling_getCellInterpolated_PY

_CellBase_compat_getCellInterpolated_CPP = CellBase_compat.getCellInterpolated
def _CellBase_compat_getCellInterpolated_PY(self, gridCoords):
    return _CellBase_compat_getCellInterpolated_CPP(self, _doubleArrayConvertTo(gridCoords))
CellBase_compat.getCellInterpolated = _CellBase_compat_getCellInterpolated_PY
