#-------------------------------------------------------------------------------
# Name:        InputManager
# Purpose:     Allow for the object oriented implementation of inputs
#              
# Author:      Jonathan G
#
# Created:     10.08.2021
# Copyright:   (c) Institute of technical biocatalysis TUHH
# Licence:     GNU GPL-v3
#-------------------------------------------------------------------------------
import pandas as pd
import pubchempy as pcp
import json,re

class ConfContainer:

    def __init__(self, **entries) -> None:
        self.__dict__.update(entries)
        
    def print(self):
        """ for testing purposes only """
        for key in self.__dict__:
            print("{}: {}".format(key,self.__dict__[key]))
        print('')

class InputManager:

    """ Class for loading both the general admin values and the
        user inputs for the experiment made in the excel sheet """

    def __init__(self) -> None:
        
        # Loads admin parameters
        with open('C:\\Entwicklung\\seabreeze-kinetic-enzyme-pj\\OverhaulEN\\AdminValues_2022_05_24.json','rb') as fp:
            tempAdminParams = json.load(fp)

        self.adminParameters = ConfContainer(**tempAdminParams)

        # Loads user inputs
        self.generalPath = self.adminParameters.MainFolder
        self.initUserValues()
        self.counter = 0 # counter for the currently used restime
    
    def initUserValues(self):
        
        """ Reads the user inputs
            """

        # Reads the user Inputs (column E) and the corresponding keys (column D)
        UserPath = '\\'.join((self.generalPath,'UserInput.xlsx'))
        tempDF = pd.read_excel(UserPath,'UserInputs')
        tempStartValues = tempDF.iloc[:,[0,1]].dropna()
        tempStartValDict = {}
        for key in tempStartValues.iloc[:,0]:
            tempStartValDict[key.replace(" ",'')] = tempStartValues[tempStartValues.iloc[:,0] == key]['User Input'].values[0]
        
        # Modifies the calibration title to fit the experiment
        self.modifyStartValDict(tempStartValDict)

        self.StartValues = ConfContainer(**tempStartValDict)

        if self.StartValues.PumpMode == 1:
            tempResTimes = tempDF['Residence Times [min]'].dropna().to_list()
            self.ResTimes = tempResTimes
        elif self.StartValues.PumpMode == 2:
            tempResTimes = tempDF['Residence Times [min]'].dropna()
            tempTarget1 = tempDF['S1 target [mmol/L]'].dropna()
            self.RunParameters = pd.DataFrame({'ResTimes':tempResTimes,'TargetConcentrationS1':tempTarget1})
        elif self.StartValues.PumpMode == 3:
            tempResTimes = tempDF['Residence Times [min]'].dropna()
            tempTarget1 = tempDF['S1 target [mmol/L]'].dropna()
            tempTarget3 = tempDF['S3 target [mmol/L]'].dropna()
            self.RunParameters = pd.DataFrame({'ResTimes':tempResTimes,'TargetConcentrationS1':tempTarget1,'TargetConcentrationS3':tempTarget3})


    def modifyStartValDict(self,iDict):
        """ Modifies the calibration title to fit the used chemicals by edding
            the PubchemID of the product, educt and solvent. The title is later 
            used by PerformMeasurements.py """

        titleFactors = ['Product','Educt','Solvent']
        calibrationTitle = 'calData'
        
        for factor in titleFactors:
            calibrationTitle = calibrationTitle+'_'+str(self.getPubchemID(iDict[factor]))
        
        calibrationTitle = calibrationTitle + '_'+ str(iDict['CuvetteDiameter'])
        iDict['calibrationTitle'] = calibrationTitle

    def getPubchemID(self,ID):
        """ Returns the PubchemID of a chemical compound,
            if it isn't a PubchemID already """

        try:
            int(ID)
            return(ID)
        except:
            return self.getMoleculeFromPubchem(ID)

    def getMoleculeFromPubchem(self,ID):
        """ Borrowed from Simon Muller from the TVT.
            Returns the PubchemID of a chemical compound """

        results = pcp.get_compounds(ID, 'name')
        
        if len(results) == 0:

            substances = pcp.get_substances(ID, 'name')
            
            if len(substances) == 1:
                try:
                    results = [substances[0].standardized_compound]
                except:
                    results = []
        
        elif len(results) > 1:
            raise ValueError("{} was not a unique identifier".format(ID))
            
        if len(results) == 0:
            results = pcp.get_compounds(ID, 'inchikey')
            
        if len(results) == 0:
            results = pcp.get_compounds(ID, 'name')
        
        if len(results) == 0:
            raise ValueError("Could not find a molecule on Pubchem for {}".format(ID))
        elif len(results) != 1:
            raise ValueError("{} was not a unique identifier".format(ID))
        else:
            return results[0].cid
    
    def isValidCAS(self,ID):
        """ Borrowed from Simon Muller from the TVT.
            Checks whether a number is a valid CAS-number.
            Currently not in use """

        cas_match = re.search('(\d+)-(\d\d)-(\d)',ID)
        try:
            cas_string = cas_match.group(1) + cas_match.group(2) + cas_match.group(3)
        except:
            return False

        increment = 0
        sum_cas = 0

        # Slices the reversed number string
        for number in reversed(cas_string):
            if increment == 0:
                validate = int(number)
            else:
                sum_cas = sum_cas + (int(number) * increment)

            increment = increment + 1
        
        # Does the math
        if validate == sum_cas % 10:
            return True
        else: 
            return False


    def getAllResTimes(self):
        """ Returns all ResTimes """
        return self.ResTimes
    
    def getNextResTimes(self):
        """ Returns the next ResTime """

        if self.counter >= len(self.ResTimes):
            return False
        
        ResTime = self.ResTimes[self.counter]
        self.counter += 1
        return ResTime

    def getAllRunParameters(self):
        """ Returns all RunParameters """
        return self.RunParameters
    
    def getNextRunParameters(self):
        """ Returns the next RunParameters """

        if self.counter >= len(self.RunParameters):
            return False
        
        rates = self.RunParameters[self.counter]
        self.counter += 1
        return rates
    
    def resetCounter(self):
        """ Resets the (ResTime-)counter to 0 """
        self.counter = 0
    
    def getStartValues(self):
        """ Returns all start values """
        return self.StartValues
    
    def getAdminValues(self):
        """ Returns all admin values """
        return self.adminParameters

    def getPumpMode(self):
        """ Returns the pump mode.
            Vielleicht unnötig """
        return self.StartValues.PumpMode

    def getControlMode(self):
        """ Returns the control mode.
            Vielleicht unnötig """
        return self.StartValues.ControlMode

    def getIntermediatePurge(self):
        """ Returns the intermedate purge status. """
        return self.StartValues.IntermediatePurge


if __name__ == '__main__':
    """ Tests the InputManager by starting it and printing all
        the admin values and ResTimes """
    test = InputManager()
    '''test.getStartValues().print()'''
    if test.getPumpMode() == 1:
        print(test.getAllResTimes())
    elif test.getPumpMode() == 2 or test.getPumpMode() == 3:
        print(test.getAllRunParameters())
        print(test.StartValues.DiameterS1)
