import numpy as np
import copy
"""FCS Bulk Correlation Software

    Copyright (C) 2015  Dominic Waithe

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""


def initialise_fcs(int_obj):
    # default options for the fitting.
    int_obj.def_options = {}

    int_obj.def_options['Diff_eq'] = 1
    int_obj.def_options['Diff_species'] = 1
    int_obj.def_options['Triplet_eq'] = 1
    int_obj.def_options['Triplet_species'] = 1

    int_obj.def_options['Dimen'] = 1

    A1 = {'alias': 'A1', 'value': 1.0, 'minv': 0.0, 'maxv': 1.0,
          'vary': False, 'to_show': True, 'stdev': False, 'calc': False}
    A2 = {'alias': 'A2', 'value': 1.0, 'minv': 0.0, 'maxv': 1.0,
          'vary': False, 'to_show': True, 'calc': False}
    A3 = {'alias': 'A3', 'value': 1.0, 'minv': 0.0, 'maxv': 1.0,
          'vary': False, 'to_show': True, 'calc': False}
    # The offset
    offset = {'alias': 'offset', 'value': 0.01, 'minv': -1.0,
              'maxv': 1.0, 'vary': True, 'to_show': True, 'calc': False}
    # int_obj.defin.add('offset', value=0.0, min=-1.0,max=5.0,vary=False)
    # The amplitude
    GN0 = {'alias': 'GN0', 'minv': 0.001, 'value': 1,
           'maxv': 1.0, 'vary': True, 'to_show': True, 'calc': False}
    # int_obj.def_param.add('GN0', value=1.0, vary=True)
    # The alpha value
    # int_obj.def_param.add('alpha', value=1.0, min=0,max=1.0, vary=True)
    # lateral diffusion coefficent
    txy1 = {'alias': 'txy1', 'value': 0.01, 'minv': 0.001,
            'maxv': 2000.0, 'vary': True, 'to_show': True, 'calc': False}
    txy2 = {'alias': 'txy2', 'value': 0.01, 'minv': 0.001,
            'maxv': 2000.0, 'vary': True, 'to_show': True, 'calc': False}
    txy3 = {'alias': 'txy3', 'value': 0.01, 'minv': 0.001,
            'maxv': 2000.0, 'vary': True, 'to_show': True, 'calc': False}

    alpha1 = {'alias': 'alpha1', 'value': 1.0, 'minv': 0.0,
              'maxv': 2.0, 'vary': True, 'to_show': True, 'calc': False}
    alpha2 = {'alias': 'alpha2', 'value': 1.0, 'minv': 0.0,
              'maxv': 2.0, 'vary': True, 'to_show': True, 'calc': False}
    alpha3 = {'alias': 'alpha3', 'value': 1.0, 'minv': 0.0,
              'maxv': 2.0, 'vary': True, 'to_show': True, 'calc': False}

    tz1 = {'alias': 'tz1', 'value': 1.0, 'minv': 0.0,
           'maxv': 1.0, 'vary': True, 'to_show': True, 'calc': False}
    tz2 = {'alias': 'tz2', 'value': 1.0, 'minv': 0.0,
           'maxv': 1.0, 'vary': True, 'to_show': True, 'calc': False}
    tz3 = {'alias': 'tz3', 'value': 1.0, 'minv': 0.0,
           'maxv': 1.0, 'vary': True, 'to_show': True, 'calc': False}

    # Axial ratio coefficient

    AR1 = {'alias': 'AR1', 'value': 1.0, 'minv': 0.001,
           'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}
    AR2 = {'alias': 'AR2', 'value': 1.0, 'minv': 0.001,
           'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}
    AR3 = {'alias': 'AR3', 'value': 1.0, 'minv': 0.001,
           'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}

    B1 = {'alias': 'B1', 'value': 1.0, 'minv': 0.001,
          'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}
    B2 = {'alias': 'B2', 'value': 1.0, 'minv': 0.001,
          'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}
    B3 = {'alias': 'B3', 'value': 1.0, 'minv': 0.001,
          'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}

    T1 = {'alias': 'T1', 'value': 1.0, 'minv': 0.0, 'maxv': 1000.0,
          'vary': True, 'to_show': True, 'calc': False}
    T2 = {'alias': 'T2', 'value': 1.0, 'minv': 0.0, 'maxv': 1000.0,
          'vary': True, 'to_show': True, 'calc': False}
    T3 = {'alias': 'T3', 'value': 1.0, 'minv': 0.0, 'maxv': 1000.0,
          'vary': True, 'to_show': True, 'calc': False}

    tauT1 = {'alias': 'tauT1', 'value': 0.055, 'minv': 0.001,
             'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}
    tauT2 = {'alias': 'tauT2', 'value': 0.055, 'minv': 0.001,
             'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}
    tauT3 = {'alias': 'tauT3', 'value': 0.005, 'minv': 0.001,
             'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': False}

    N_FCS = {'alias': 'N (FCS)', 'value': 0.0, 'minv': 0.001,
             'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    cpm = {'alias': 'cpm (kHz)', 'value': 0.0, 'minv': 0.001,
           'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    N_mom = {'alias': 'N (mom)', 'value': 0.0, 'minv': 0.001,
             'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    bri = {'alias': 'bri (kHz)', 'value': 0.0, 'minv': 0.001,
           'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    CV = {'alias': 'Coincidence', 'value': 0.0, 'minv': 0.001,
          'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    f0 = {'alias': 'PBC f0', 'value': 0.0, 'minv': 0.001,
          'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    overtb = {'alias': 'PBC tb', 'value': 0.0, 'minv': 0.001,
              'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}

    ACAC = {'alias': 'ACAC', 'value': 0.0, 'minv': 0.001,
            'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}
    ACCC = {'alias': 'ACCC', 'value': 0.0, 'minv': 0.001,
            'maxv': 1000.0, 'vary': True, 'to_show': True, 'calc': True}

    int_obj.def_param = {'A1': A1, 'A2': A2, 'A3': A3, 'txy1': txy1, 'txy2': txy2, 'txy3': txy3, 'offset': offset, 'GN0': GN0, 'alpha1': alpha1, 'alpha2': alpha2, 'alpha3': alpha3,
                         'tz1': tz1, 'tz2': tz2, 'tz3': tz3, 'AR1': AR1, 'AR2': AR2, 'AR3': AR3, 'B1': B1, 'B2': B2, 'B3': B3, 'T1': T1, 'T2': T2, 'T3': T3, 'tauT1': tauT1, 'tauT2': tauT2, 'tauT3': tauT3}
    int_obj.def_param['N_FCS'] = N_FCS
    int_obj.def_param['cpm'] = cpm
    int_obj.def_param['N_mom'] = N_mom
    int_obj.def_param['bri'] = bri
    int_obj.def_param['CV'] = CV
    int_obj.def_param['f0'] = f0
    int_obj.def_param['overtb'] = overtb

    int_obj.def_param['ACAC'] = ACAC
    int_obj.def_param['ACCC'] = ACCC

    int_obj.order_list = ['offset', 'GN0', 'N_FCS', 'cpm', 'A1', 'A2', 'A3', 'txy1', 'txy2', 'txy3', 'tz1', 'tz2', 'tz3', 'alpha1', 'alpha2', 'alpha3',
                          'AR1', 'AR2', 'AR3', 'B1', 'B2', 'B3', 'T1', 'T2', 'T3', 'tauT1', 'tauT2', 'tauT3', 'N_mom', 'bri', 'CV', 'f0', 'overtb', 'ACAC', 'ACCC']


def decide_which_to_show(int_obj):

    for art in int_obj.objId_sel.param:
        if int_obj.objId_sel.param[art]['to_show'] == True:
            int_obj.objId_sel.param[art]['to_show'] = False

        int_obj.objId_sel.param['offset']['to_show'] = True
        int_obj.objId_sel.param['GN0']['to_show'] = True

        int_obj.def_options['Diff_species'] = int_obj.diffNumSpecSpin.value()
        int_obj.def_options['Triplet_species'] = int_obj.tripNumSpecSpin.value()

        # Optional parameters
        for i in range(1, int_obj.def_options['Diff_species']+1):
            int_obj.objId_sel.param['A'+str(i)]['to_show'] = True
            int_obj.objId_sel.param['txy'+str(i)]['to_show'] = True
            int_obj.objId_sel.param['alpha'+str(i)]['to_show'] = True
            # 2 in this case corresponds to 3D:
            if int_obj.def_options['Dimen'] == 2:
                if int_obj.def_options['Diff_eq'] == 1:
                    int_obj.objId_sel.param['tz'+str(i)]['to_show'] = True

                if int_obj.def_options['Diff_eq'] == 2:
                    int_obj.objId_sel.param['AR'+str(i)]['to_show'] = True

        if int_obj.def_options['Triplet_eq'] == 2:
            # Triplet State equation1
            for i in range(1, int_obj.tripNumSpecSpin.value()+1):
                int_obj.objId_sel.param['B'+str(i)]['to_show'] = True
                int_obj.objId_sel.param['tauT'+str(i)]['to_show'] = True

        if int_obj.def_options['Triplet_eq'] == 3:
            # Triplet State equation2
            for i in range(1, int_obj.tripNumSpecSpin.value()+1):
                int_obj.objId_sel.param['T'+str(i)]['to_show'] = True
                int_obj.objId_sel.param['tauT'+str(i)]['to_show'] = True
        calc_param_fcs(int_obj, objId=int_obj.objId_sel)


def update_each(int_obj, text):
    """Will try and populate paramaters with what is present in the inteface, but if new option will goto the default"""
    try:
        exec("valueV = int_obj."+text+"_value.value()")
        exec("minV = int_obj."+text+"_min.value()")
        exec("maxV = int_obj."+text+"_max.value()")
        exec("varyV = int_obj."+text+"_vary.isChecked()")

        int_obj.objId_sel.param[text]['value'] = valueV
        int_obj.objId_sel.param[text]['minv'] = minV
        int_obj.objId_sel.param[text]['maxv'] = maxV
        int_obj.objId_sel.param[text]['vary'] = varyV
    except:

        int_obj.objId_sel.param[text] = copy.deepcopy(int_obj.def_param[text])


def update_param_fcs(int_obj):
    """Depending on the menu options this function will update the params of the current data set. """
    if int_obj.objId_sel == None:
        return
    decide_which_to_show(int_obj)
    # Set all the parameters to not show.

    for art in int_obj.objId_sel.param:
        if int_obj.objId_sel.param[art]['to_show'] == True:
            update_each(int_obj, art)

    calc_param_fcs(int_obj, objId=int_obj.objId_sel)


def calc_param_fcs(int_obj, objId):
    # Calculated parameters.
    try:
        objId.param['N_FCS']['value'] = 1/objId.param['GN0']['value']
        objId.param['N_FCS']['to_show'] = True
    except:
        objId.param['N_FCS']['value'] = 1
        objId.param['N_FCS']['to_show'] = False

    try:
        objId.param['cpm']['value'] = float(
            objId.kcount)/(1/objId.param['GN0']['value'])
        objId.param['cpm']['to_show'] = True
    except:
        objId.param['cpm']['value'] = 1
        objId.param['cpm']['to_show'] = False
    try:
        objId.param['N_mom']['value'] = float(objId.numberNandB)
        objId.param['N_mom']['to_show'] = True
    except:
        objId.param['N_mom']['value'] = 1
        objId.param['N_mom']['to_show'] = False
    try:
        objId.param['bri']['value'] = float(objId.brightnessNandB)
        objId.param['bri']['to_show'] = True
    except:
        objId.param['bri']['value'] = 1
        objId.param['bri']['to_show'] = False
    try:
        objId.param['CV']['value'] = float(objId.CV)
        objId.param['CV']['to_show'] = True
    except:
        pass
    try:
        objId.param['f0']['value'] = float(objId.pbc_f0)
        objId.param['f0']['to_show'] = True
        objId.param['overtb']['value'] = float(objId.pbc_tb)
        objId.param['overtb']['to_show'] = True
    except:
        pass

    if int_obj.objIdArr != [] and objId.siblings != None and objId.ch_type != 2:

        if objId.siblings[0].fitted == True:

            objId.param['ACAC']['value'] = float(
                objId.param['GN0']['value'])/float(objId.siblings[0].param['GN0']['value'])
            objId.param['ACAC']['to_show'] = True
            objId.param['ACCC']['value'] = float(
                objId.param['GN0']['value'])/float(objId.siblings[1].param['GN0']['value'])
            objId.param['ACCC']['to_show'] = True


def equation_(param, tc, options):
    """This is equation for fitting"""

    # A1 is relative component of fluorescent species
    # tc is tau.
    # txy1 is xy difusion   for fluorescent species one.
    # alpha1 is
    # tz1 is z diffusion for fluorescent species one.
    offset = param['offset'].value
    GN0 = param['GN0'].value

    if (options['Dimen'] == 2):
        if (options['Diff_eq'] == 1):
            # Equation 1A with 3D term.
            if (options['Diff_species'] == 1):
                A1 = param['A1'].value
                txy1 = param['txy1'].value
                alpha1 = param['alpha1'].value
                tz1 = param['tz1'].value
                # For one diffusing species
                GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1)*((1+(tc/tz1))**-0.5)))
            elif (options['Diff_species'] == 2):
                A1 = param['A1'].value
                txy1 = param['txy1'].value
                alpha1 = param['alpha1'].value
                tz1 = param['tz1'].value
                A2 = param['A2'].value
                txy2 = param['txy2'].value
                alpha2 = param['alpha2'].value
                tz2 = param['tz2'].value
                param['A2'].value = 1.0-A1
                A2 = param['A2'].value
                # For two diffusing species
                GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1)*((1+(tc/tz1))**-0.5))) + \
                    (A2*(((1+((tc/txy2)**alpha2))**-1)*((1+(tc/tz2))**-0.5)))
            elif (options['Diff_species'] == 3):
                A1 = param['A1'].value
                txy1 = param['txy1'].value
                alpha1 = param['alpha1'].value
                tz1 = param['tz1'].value
                A2 = param['A2'].value
                txy2 = param['txy2'].value
                alpha2 = param['alpha2'].value
                tz2 = param['tz2'].value
                A3 = param['A3'].value
                txy3 = param['txy3'].value
                alpha3 = param['alpha3'].value
                tz3 = param['tz3'].value
                param['A2'].value = 1.0-A1-A3
                A2 = param['A2'].value
                param['A3'].value = 1.0-A2-A1
                A3 = param['A3'].value
                # For three diffusing species
                GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1)*((1+(tc/tz1))**-0.5))) + (A2*(((1+((tc/txy2)**alpha2))
                                                                                          ** -1)*((1+(tc/tz2))**-0.5))) + (A3*(((1+((tc/txy3)**alpha3))**-1)*((1+(tc/tz3))**-0.5)))
        elif (options['Diff_eq'] == 2):
            if (options['Diff_species'] == 1):
                A1 = param['A1'].value
                txy1 = param['txy1'].value
                alpha1 = param['alpha1'].value
                AR1 = param['AR1'].value
                # For one diffusing species
                GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1) *
                             (((1+(tc/((AR1**2)*txy1)))**-0.5))))
            elif (options['Diff_species'] == 2):
                A1 = param['A1'].value
                txy1 = param['txy1'].value
                alpha1 = param['alpha1'].value
                AR1 = param['AR1'].value
                A2 = param['A2'].value
                txy2 = param['txy2'].value
                alpha2 = param['alpha2'].value
                AR2 = param['AR2'].value
                param['A2'].value = 1.0-A1
                A2 = param['A2'].value
                # For two diffusing species
                GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1)*(((1+(tc/((AR1**2)*txy1)))**-0.5)))) + \
                    (A2*(((1+((tc/txy2)**alpha2))**-1) *
                         (((1+(tc/((AR2**2)*txy2)))**-0.5))))
            elif (options['Diff_species'] == 3):
                A1 = param['A1'].value
                txy1 = param['txy1'].value
                alpha1 = param['alpha1'].value
                AR1 = param['AR1'].value
                A2 = param['A2'].value
                txy2 = param['txy2'].value
                alpha2 = param['alpha2'].value
                AR2 = param['AR2'].value
                A3 = param['A3'].value
                txy3 = param['txy3'].value
                alpha3 = param['alpha3'].value
                AR3 = param['AR3'].value
                # For two diffusing species
                param['A2'].value = 1.0-A1-A3
                A2 = param['A2'].value
                param['A3'].value = 1.0-A2-A1
                A3 = param['A3'].value
                # For three diffusing species
                GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1)*(((1+(tc/((AR1**2)*txy1)))**-0.5))))+(A2*(((1+((tc/txy2)**alpha2))**-1)
                                                                                                     * (((1+(tc/((AR2**2)*txy2)))**-0.5))))+(A3*(((1+((tc/txy3)**alpha3))**-1)*(((1+(tc/((AR3**2)*txy3)))**-0.5))))

    if (options['Dimen'] == 1):
        # Equation 1A with 2D term.
        if (options['Diff_species'] == 1):
            A1 = param['A1'].value
            txy1 = param['txy1'].value
            alpha1 = param['alpha1'].value
            # For one diffusing species
            GDiff = (A1*(((1+((tc/txy1)**alpha1))**-1)))
        elif (options['Diff_species'] == 2):
            A1 = param['A1'].value
            txy1 = param['txy1'].value
            alpha1 = param['alpha1'].value
            A2 = param['A2'].value
            txy2 = param['txy2'].value
            alpha2 = param['alpha2'].value
            # For two diffusing species

            param['A2'].value = 1.0-A1
            A2 = param['A2'].value

            GDiff = (A1*(((1+(tc/txy1)**alpha1)**-1))) + \
                (A2*(((1+(tc/txy2)**alpha2)**-1)))

            # if A1 +A2 != 1.0:
            #    GDiff = 99999999999
        elif (options['Diff_species'] == 3):
            A1 = param['A1'].value
            txy1 = param['txy1'].value
            alpha1 = param['alpha1'].value
            A2 = param['A2'].value
            txy2 = param['txy2'].value
            alpha2 = param['alpha2'].value
            A3 = param['A3'].value
            txy3 = param['txy3'].value
            alpha3 = param['alpha3'].value
            # For two diffusing species
            param['A2'].value = 1.0-A1-A3
            A2 = param['A2'].value
            param['A3'].value = 1.0-A2-A1
            A3 = param['A3'].value
            # For three diffusing species
            GDiff = (A1*(((1+(tc/txy1)**alpha1)**-1)))+(A2 *
                                                        (((1+(tc/txy2)**alpha2)**-1)))+(A3*(((1+(tc/txy3)**alpha3)**-1)))

    if (options['Triplet_eq'] == 1):
        # For no triplets.
        GT = 1
    elif (options['Triplet_eq'] == 2):
        # Equation (2) 1st equation.
        if (options['Triplet_species'] == 1):
            B1 = param['B1'].value
            tauT1 = param['tauT1'].value
            # For one dark state.
            GT = 1 + (B1*np.exp(-tc/tauT1))
        elif (options['Triplet_species'] == 2):
            B1 = param['B1'].value
            tauT1 = param['tauT1'].value
            B2 = param['B2'].value
            tauT2 = param['tauT2'].value
            # For two dark state
            GT = 1 + (B1*np.exp(-tc/tauT1))+(B2*np.exp(-tc/tauT2))
        elif (options['Triplet_species'] == 3):
            B1 = param['B1'].value
            tauT1 = param['tauT1'].value
            B2 = param['B2'].value
            tauT2 = param['tauT2'].value
            B3 = param['B3'].value
            tauT3 = param['tauT3'].value
            # For three dark state
            GT = 1 + (B1*np.exp(-tc/tauT1)) + \
                (B2*np.exp(-tc/tauT2))+(B3*np.exp(-tc/tauT3))

    elif (options['Triplet_eq'] == 3):
        # Equation (2) 2nd equation.
        if (options['Triplet_species'] == 1):
            T1 = param['T1'].value
            tauT1 = param['tauT1'].value
            # For one dark state.
            GT = 1 - T1 + (T1*np.exp(-tc/tauT1))
        elif (options['Triplet_species'] == 2):
            T1 = param['T1'].value
            tauT1 = param['tauT1'].value
            T1 = param['T2'].value
            tauT1 = param['tauT2'].value
            # For two dark state.
            GT = 1 - (T1+T2) + ((T1*np.exp(-tc/tauT1))+(T2*np.exp(-tc/tauT2)))
        elif (options['Triplet_species'] == 3):
            T1 = param['T1'].value
            tauT1 = param['tauT1'].value
            T2 = param['T2'].value
            tauT1 = param['tauT2'].value
            T3 = param['T3'].value
            tauT1 = param['tauT3'].value
            # For three dark state.
            GT = 1 - (T1+T2+T3) + ((T1*np.exp(-tc/tauT1)) +
                                   (T2*np.exp(-tc/tauT2))+(T3*np.exp(-tc/tauT3)))

    return offset + (GN0*GDiff*GT)
