classdef SCDclass_mdsobjcontainer
    % This class is a container class
    % for all MDS objects required for handling
    % a no-nocompile Simulink SCD model
    %
    % Presently it implements methods for handling
    % objects of the following classes:
    % SCDclass_mdsparam (and childrens)
    % SCDclass_mdswavegen (and childrens)
    
    properties
        mdsparams           % params objects array
        mdswavegens         % wavegens objects array
        simstructlist       % list of names of simstructs to be transferred to
                            % base workspace upon expcode setup
        modeltoactualize    % model name to actualize ('all' for all)
        modeltogenerate     % model name to generate conf script ('all' for all)
    end
    
    methods
        function obj = SCDclass_mdsobjcontainer()
            % contructor, empty container
            obj.modeltoactualize='all';
            obj.modeltogenerate='all';
        end
        
        function obj = addparameter(obj, param)
            % Adds one or more parameter objects to list of params
            obj.mdsparams = [obj.mdsparams, param];
        end

        function obj = printparameters(obj)
          % prints the parameters object list
          for ii=1:obj.getnumparams
            obj.mdsparams(ii).printinfo();
          end
        end
        
        function obj = actualizeparameters(obj, shot)
          % actualize the parameters in the data dictionary,
          % naive version with a mds acces for every parameter
          for ii=1:obj.getnumparams
            if strcmp(obj.modeltoactualize,'all')
              obj.mdsparams(ii).actualizedata(shot);
            else
              if strcmp(obj.modeltoactualize,obj.mdsparams(ii).getmodelname)
                obj.mdsparams(ii).actualizedata(shot);
              end
            end
          end
        end

        function obj = addwavegen(obj, wavegen)
            % adds one or more wavegen objects
            obj.mdswavegens=[obj.mdswavegens; wavegen];
        end
        
        function obj = setwavegenbasestruct(obj, basestruct)
          for ii=1:obj.getnumwavegens
            obj.mdswavegens(ii)=obj.mdswavegens(ii).setbasestruct(basestruct);
          end
        end
        
        function obj = printwavegens(obj)
          for ii=1:obj.getnumwavegens
            obj.mdswavegens(ii).printinfo();
          end
        end
        
        function obj = actualizewavegens(obj, shot)
          % actualize the wavegen waves timeseries
          % naive version, a mds connection is called for every object
          for ii=1:obj.getnumwavegens
            if(strcmp(obj.modeltoactualize,'all'))
              obj.mdswavegens(ii).actualizedata(shot);
            else
              if(strcmp(obj.modeltoactualize,obj.mdswavegens(ii).getmodelname))
                obj.mdswavegens(ii).actualizedata(shot);
              end
            end
          end
        end
                
        function obj = cleanwavegens(obj)
            % clean wavegens leaving a consistent (with model buses)
            % empty timeseries data structure
            if obj.getnumwavegens>0
               for ii=1:obj.getnumwavegens
                  obj.mdswavegens(ii).cleandata;
               end
            end
        end
      
        function obj = importmdsparams(obj, source)
            % import mds parameters into this container from another source

            numparamstoimport = source.getnumparams;
            paramstoimport    = source.mdsparams;

            % exclusion list of parameters already imported that shuold not
            % be imported again.
            exclusionlist = false(size(paramstoimport)); % init
            if ~isempty(obj.mdsparams) % if there are already model params in the destination
              destparamlist = obj.mdsparams.getmodelparam; % existing parameter names in destination
              for ii=1:numparamstoimport % loop over params to import
                my_modelparam = paramstoimport(ii).getmodelparam;
                if ismember(destparamlist,my_modelparam)
                  warning('SCDclass_mdsobjcontainer:importmdsobjects',...
                  'An mds object driving ''%s'' is already present in the destination expcode, skipping!',...
                  my_modelparam);
                  exclusionlist(ii) = true;
                end
              end  
            end
            
            % import non-excluded source parameters into parameter list
            obj=obj.addparameter(paramstoimport(~exclusionlist));
        end
           
        function obj = importmdswavegens(obj, source)        
            % wavegens import     
            destwavegentargets=cell(1,obj.getnumwavegens);
            for ii=1:obj.getnumwavegens
              destwavegentargets{ii}=obj.mdswavegens(ii).gettargetwavegen;
            end
                           
            numwavegenstoimport = source.getnumwavegens;
            wavegenstoimport = source.mdswavegens;
            
            for ii=1:numwavegenstoimport
              if ~ismember(wavegenstoimport(ii).gettargetwavegen, destwavegentargets)
                obj=obj.addwavegen(wavegenstoimport(ii));
              else
                warning('SCDclass_mdsobjcontainer:importmdsobjects','A mds object driving ''%s'' is already present in the dest. expcode, skipping!',wavegenstoimport(ii).gettargetwavegen);
              end
            end
         
        end
        
        function printMARTe2parconfig(obj, shot)
          marteparlist=[];
            
          switch obj.modeltogenerate
            case 'all'
              iorder = getParamsServerTreeOrder(obj);  % order entries following mdsserver, mdstree order
              prevServer = ''; % init
              prevTree   = ''; % init
              
              % Header for MDS for loader
              %loaderStr = sprintf('\n\n+MDSParameters = {\n Class=MDSObjLoader\n Shot=%d\n',shot);
              loaderStr = sprintf('\n\n+MDSParameters = {\n Class=MDSObjLoader\n Shot=MDSSRCSHOT\n');              
              fprintf("%s",loaderStr);
         
              for ii=1:obj.getnumparams
                mymdsparam = obj.mdsparams(iorder(ii));

                currentServer = mymdsparam.getMDSserver;
                currentTree   = mymdsparam.getMDStree;
                
                % generate header for MDSsource if necessary
                if ~strcmp(currentServer,prevServer) || ~strcmp(currentTree,prevTree) %if a new server needs to be opened
                  if ii~=1, fprintf(' }\n'), end % close bracket for previous one
                  % print new source header
                  fprintf("%s\n",mymdsparam.genMARTe2MDSsourcestr); 
                end
                prevServer = currentServer;
                prevTree   = currentTree;
                
                % generate data source entry
                str = mymdsparam.genMARTe2entry(shot);
                fprintf("  %s\n",str);
                marteparlist=[marteparlist; {mymdsparam.gettargetparammarte}];
              end
              fprintf(" }\n}\n\n");

            otherwise
              for ii=1:obj.getnumparams
                if(strcmp(obj.modeltogenerate,obj.mdsparams(ii).getmodelname))
                  str=obj.mdsparams(ii).genMARTe2entry(shot);
                  fprintf("  %s\n",str);
                  marteparlist=[marteparlist; {obj.mdsparams(ii).gettargetparammarte}];
                end
              end
          end
          % Now check for MARTe parameters names unicity
          for ii=2:numel(marteparlist)
             for jj=1:ii-1
                 if strcmp(marteparlist(ii), marteparlist(jj))
                     warning("found identical MARTe par. names here: %s", marteparlist{ii});
                 end
             end
          end
        end
        
        function autopopulateMDSparams(obj, shot)
              for ii=1:obj.getnumparams
                if(strcmp(obj.modeltogenerate,obj.mdsparams(ii).getmodelname))
                  obj.mdsparams(ii).autopopulatemds(shot);
                end
              end            
        end
        
        function autopopulateMDSwavegens(obj, shot)
              for ii=1:obj.getnumwavegens
                if(strcmp(obj.modeltogenerate,obj.mdswavegens(ii).getmodelname))
                  obj.mdswavegens(ii).autopopulatemds(shot);
                end
              end            
        end
        
        function iorder = getParamsServerTreeOrder(obj)
          % find server-tree order of parameters
          mdsservertree = cell(numel(obj.mdsparams),2);
          for ii=1:numel(obj.mdsparams)
            mdsservertree{ii,1} = obj.mdsparams(ii).getMDSserver;
            mdsservertree{ii,2} = obj.mdsparams(ii).getMDStree;
          end
          [~,iorder] = sortrows(mdsservertree);
        end
        
        function iorder = geWavegensServerTreeOrder(obj)
          % find server-tree order of wavegens
          mdsservertree = cell(numel(obj.mdswavegens),2);
          for ii=1:numel(obj.mdswavegens)
            mdsservertree{ii,1} = obj.mdswavegens(ii).getMDSserver;
            mdsservertree{ii,2} = obj.mdswavegens(ii).getMDStree;
          end
          [~,iorder] = sortrows(mdsservertree);
        end
               
        function printMARTe2wgconfig(obj, shot)
          for ii=1:obj.getnumwavegens
            if(strcmp(obj.modeltogenerate,'all'))
              str=obj.mdswavegens(ii).genMARTe2entry(shot);
              fprintf("   %s\n",str);
            else
              if(strcmp(obj.modeltogenerate,obj.mdswavegens(ii).getmodelname))
                str=obj.mdswavegens(ii).genMARTe2entry(shot);
                fprintf("   %s\n",str);
              end
            end
          end
        end
                
        function overalldim = printMARTe2wgbusconfig(obj, shot, ddname, busname, frequency)
            dd=SCDconf_getdatadict(ddname);
            bus=dd.getEntry(busname).getValue;
            nelems=numel(bus.Elements);
            server='';
            tree='';
            dimension=0;
            overalldim=0;
            nwavegen=1;
            
            fprintf("+MDSWavegen_%s_%d = {\n", busname, nwavegen);
            fprintf(" Class = MDSObjWavegen\n");
            fprintf(" Shot = MDSSRCSHOT\n"); 
            fprintf(" Frequency = %.2f\n",frequency);            
            fprintf(" Verbosity = VERBOSITY\n");
            fprintf(" Interpolation = 1\n");
            fprintf(" TimeOffset = 0.0\n");
                        
            for elem=1:nelems
                signame=bus.Elements(elem).Name;
                found=false;
                for wgs=1:obj.getnumwavegens
                    if(strcmp(obj.mdswavegens(wgs).gettarget,signame))
                        found=true;
                        break;
                    end
                end
                if(~found)
                    error('SCDclass_mdsobjcontainer:wgnotfound','Signal %s not found in configured wavegens objects', signame);
                else
                    
                    
                    actserver=obj.mdswavegens(wgs).getMDSserver;
                    acttree=obj.mdswavegens(wgs).getMDStree;
                    
                    if(~strcmp(actserver,server) || ~strcmp(acttree,tree))
                        if(~strcmp(server,'') || ~strcmp(tree,''))
                            
                            fprintf(" Signals = {\n");
                            fprintf("   time = { Type = int32 }\n");
                            fprintf("   wavegen_%s_%d = { Type = float32 NumberOfElements = %d }\n", busname, nwavegen, dimension);
                            fprintf(" }\n");
                            fprintf("}\n");
                            
                            nwavegen=nwavegen+1;
                            overlalldim=overalldim+dimension;
                            dimension=0;
                            
                            fprintf("+MDSWavegen_%s_%d = {\n", busname, nwavegen);
                            fprintf(" Class = MDSObjWavegen\n");
                            fprintf(" Shot = MDSSRCSHOT\n");
                            fprintf(" Frequency = %.2f\n",frequency);           
                            fprintf(" Verbosity = VERBOSITY\n");
                            fprintf(" Interpolation = 1\n");
                            fprintf(" TimeOffset = 0.0\n");
                            server=actserver;
                            tree=acttree;
                            fprintf(' Server="%s"\n', server);
                            fprintf(' Tree="%s"\n', tree)
                            
                            % error('SCDclass_mdsobjcontainer:multipleconnections','multiple MDS+ connections/trees not supported within the same wavegen generator');
                        else
                            
                            
                            server=actserver;
                            tree=acttree;
                            fprintf(' Server="%s"\n', server);
                            fprintf(' Tree="%s"\n', tree)
                        end
                    end
                    
                    entrystring=obj.mdswavegens(wgs).genMARTe2entry(shot);
                    fprintf(' %s\n', entrystring);
                    dimension=dimension+bus.Elements(elem).Dimensions;
                    
                end
            end
            fprintf(" Signals = {\n");
            fprintf("   time = { Type = int32 }\n");
            fprintf("   wavegen_%s_%d = { Type = float32 NumberOfElements = %d }\n", busname, nwavegen, dimension);
            fprintf(" }\n");
            fprintf("}\n");
            
            
            if nwavegen>1
                warning('SCDclass_mdsobjcontainer:multipleconnections','multiple MDS+ connections/trees detected, an IOGAM must be used to concatenate wavegens outputs at MARTe2 RTThread level');
            end
            
            overalldim=dimension;
            %overlalldim=overalldim+dimension;
            
            
        end
        
        function iorder = getWavegensServerTreeOrder(obj)
          % find server-tree order of wavegens
          mdsservertree = cell(numel(obj.mdswavegens),2);
          for ii=1:numel(obj.mdswavegens)
            mdsservertree{ii,1} = obj.mdswavegens(ii).getMDSserver;
            mdsservertree{ii,2} = obj.mdswavegens(ii).getMDStree;
          end
          [~,iorder] = sortrows(mdsservertree);
        end
        
        function num = getnumparams(obj)
          % get number or parameters
          num = numel(obj.mdsparams);
        end
        
         function num = getnumwavegens(obj)
          % get number or parameters
          num = numel(obj.mdswavegens);
        end
    end
end
