# -*- coding: utf-8 -*-
#
# Copyright (C) 2016-2025 EDF R&D
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

from os import path,getcwd,rename,remove,replace
from sys import exit

class Write_in_dsin:
    """
    This class has been written to change dsin.txt (a file that is generated by
    Dymola and that contains the values of the parameters of a simulation). Changing
    the values of parameters in dsin.txt lets the user launch a different simulation.
    This can also be done with method modelicares.exps.write_params.
    The present class has been written in case in order to make it possible to
    change the parameters values in dsin.txt without importing modelicares.

    New features introduced 02/2019:
    - possibility of change a dsin.txt type file having a different name
    - by default, the previous use of this module should remain unchanged

    New features introduced 09/2022:
    - script adapted to the situations in which the variables are described on
      a single line (instead of 2 traditionnally) in the dsin file
    """

    def __init__(self,dict_inputs,filedir=path.abspath(getcwd()),dsin_name='dsin.txt',old_file_name='dsin_old.txt',new_file_name='dsin.txt'):
        self.__dict_inputs = dict_inputs.copy() #this way the dictionary is not emptied
        self.__filedir = filedir
        self.__dsin_name = dsin_name
        self.__old_file_name = old_file_name
        self.__new_file_name = new_file_name
        self.__maybe_par_dict = {}

    @property
    def dict_inputs(self):
        """
        Dictionary associating to each variable whose value has to be given to
        dsin.txt (inputs + iteration variables if necessary) its value.
        """
        return self.__dict_inputs

    @property
    def dsin_name(self):
        """
        Name of the dsin.txt file to change, it might have a different name
        """
        return self.__dsin_name

    @property
    def maybe_par_dict(self):
        """
        List of variables to be changed which are not identified as 'parameter' in the dsin.txt.
        """
        return self.__maybe_par_dict

    @property
    def filedir(self):
        """
        Path of the directory in which the file dsin.txt is located.
        The new version of this file will also be saved in this directory.
        """
        return self.__filedir

    @property
    def old_file_name(self):
        """
        The original file dsin.txt that is modified is saved under a name that can be chosen.
        dsin_old.txt seems appropriate
        """
        return self.__old_file_name

    @property
    def new_file_name(self):
        """
        The new version of dsin.txt is given a name that can be chosen through
        this attribute. In order to be able to launch a simulation, this name has
        to be dsin.txt.
        """
        return self.__new_file_name

########################################################################
#                            MAIN METHODS                              #
########################################################################
    def write_in_dsin(self):
        """
        Main method of this class.
        This method creates a new dsin.txt file.
        The original file is renamed 'dsin_old.txt'.
        The new file is named 'dsin.txt'
        """
        #reading has value 1 while the part of the file currently being
        #read is the part of the file in which the variables are given
        #values. Only this part of the file has to be changed in order
        #to change the parameters values.
        reading=0
        in_line1,in_line2=None,None
        with open(path.join(self.filedir,self.dsin_name),'r') as lines:
            with open(path.join(self.filedir,'dsin_tmp.txt'),'w') as output:
                for line in lines:
                    if reading==1 and len(line)>1 and not line[0] == '#':
                        if in_line1 == None:
                            in_line1 = line
                        else:
                            in_line2 = line
                    else:
                        output.write(line)

                    if 'initialValue' in line:
                        reading=1
                    #If the structure of file dsin.txt generated by
                    #Dymola was changed, the following line could have
                    #to be changed
                    if '# Matrix with 6 columns' in line:
                        reading=0

                    # - START - Take into account the possibility of handling dsin.txt files with one single line for describin variables
                    if in_line1 != None and len(in_line1.split())>4 and reading==1: #Case where there is one line per parameter rather than two in the dsin file

                        line_list = in_line1.split() #The variable name may contain spaces...
                        valtype1,val,elem13,elem14,valtype2,elem16,elem17 = line_list[0],line_list[1],line_list[2],line_list[3],line_list[4],line_list[5],line_list[6]

                        var = ''
                        for reste in line_list[7:]:
                            var = var + reste

                        if var in self.dict_inputs.keys():
                            val = self.dict_inputs[var]
                            #The format is not the same depending on the length of some variables
                            if len(str(val))<=7 and len(elem14)<=7:
                                txt ='{elem11:>3} {elem12:>7} {elem13:>18} {elem14:>7} {elem15:>18} {elem16:>5} {elem17:>3} {elem18:<}\n'
                            if len(str(val))<=7 and len(elem14)>7:
                                txt ='{elem11:>3} {elem12:>7} {elem13:>18} {elem14:>23} {elem15:>2} {elem16:>5} {elem17:>3} {elem18:<}\n'
                            if len(str(val))>7 and len(elem14)<=7:
                                txt ='{elem11:>3} {elem12:>23} {elem13:>2} {elem14:>7} {elem15:>18} {elem16:>5} {elem17:>3} {elem18:<}\n'
                            if len(str(val))>7 and len(elem14)>7:
                                txt ='{elem11:>3} {elem12:>23} {elem13:>2} {elem14:>23} {elem15:>2} {elem16:>5} {elem17:>3} {elem18:<}\n'

                            out_line = txt.format(elem11=valtype1,elem12=val,elem13=elem13,elem14=elem14,elem15=valtype2,elem16=elem16,elem17=elem17,elem18=var)

                            if valtype1 != '-1' or (valtype2 != '1' and valtype2 != '2') : #(0,6) combination looks like the only case to look into
                                self.__maybe_par_dict[var] = val
                            self.__dict_inputs.pop(var) #Deleting the variable from the list of 'to do'

                        else:
                            out_line = in_line1

                        output.write(out_line)

                        in_line1=None
                        # - END - Take into account the possibility of handling dsin.txt files with one single line for describin variables


                    elif in_line2 != None:
                        valtype1,val,elem13,elem14 = in_line1.split()
                        #valtype2,elem22,elem23,var = in_line2.split()
                        line2_list = in_line2.split() #The variable name may contain spaces...
                        valtype2 = line2_list[0]
                        var = ''
                        for reste in line2_list[3:]:
                            var = var + reste

                        if var in self.dict_inputs.keys():
                            val = self.dict_inputs[var]
                            out_line1 = '{elem11:>3} {elem12:>23} {elem13:23} {elem14:>23}\n'.format(elem11=valtype1,elem12=val,elem13=elem13,elem14=elem14)
                            if valtype1 != '-1' or (valtype2 != '1' and valtype2 != '2') : #(0,6) combination looks like the only case to look into
                                self.__maybe_par_dict[var] = val
                            self.__dict_inputs.pop(var) #Deleting the variable from the list of 'to do'
                        else:
                            out_line1 = in_line1
                        out_line2 = in_line2

                        output.write(out_line1)
                        output.write(out_line2)

                        in_line1,in_line2=None,None

#       rename(path.join(self.filedir,'dsin.txt'),path.join(self.filedir,self.old_file_name))
        replace(path.join(self.filedir,self.dsin_name),path.join(self.filedir,self.old_file_name)) # resolve the conflit with old version

        if len(self.dict_inputs) > 0 : #At least one variable was not changed
            remove(path.join(self.filedir,'dsin_tmp.txt'))
            raise KeyError("The following variables were not found in dsin.txt:\n %s" % list(self.dict_inputs.keys()))

        rename(path.join(self.filedir,'dsin_tmp.txt'),path.join(self.filedir,self.new_file_name))

if __name__ == '__main__':
    print('\n  AUTODIAGNOSTIC\n  ==============\n')
