classdef autoblksMbcSetupDataset < handle
%

% Copyright 2018-2023 The MathWorks, Inc. 
properties
    Name % Name of dataset
    BaseName % Initial name 
    ImportName = 'Data';
    ImportDataEditorCloseFcn function_handle = @() 1;
    EditDataEditorCloseFcn function_handle = @() 1;
    TestPlan autoblksMbcSetupTestplan   
    SaveOnDataEditClose = true;
    ImportDescription char = '';
end
    
properties (SetAccess = private)
    DataEditCloseAction
    SignalDescriptionMap containers.Map
    SharedImportDatasets autoblksMbcSetupDataset = autoblksMbcSetupDataset.empty;
    NumDatasets
end

methods
    %% Constuctor
    function obj = autoblksMbcSetupDataset(InputName)
        obj.Name = '';
        obj.BaseName = InputName;
        obj.SignalDescriptionMap = containers.Map.empty;
        obj.SaveOnDataEditClose = true;
    end
    
    %% AddSignalDescription
    function AddSignalDescription(obj, SignalCell)
        
        if isa(SignalCell, 'containers.Map')
            Keys = SignalCell.keys;
            for i = 1:length(Keys)
               obj.SignalDescriptionMap(Keys{i}) = SignalCell(Keys{i}); 
            end
        else
            for i = 1:size(SignalCell, 1)
                obj.SignalDescriptionMap(SignalCell{i,1}) = SignalCell{i,2};
            end
        end
    end
    
    %% ShareDatasetImport
    function ShareDatasetImport(obj, varargin)
        for i = 1:(nargin-1)
            isShared = false;
            for j = 1:length(obj.SharedImportDatasets)
                if isequal(varargin{i}, obj.SharedImportDatasets(j))
                   isShared = true;
                   break;
                end
            end
            
            if ~isShared 
               obj.SharedImportDatasets(end+1) = varargin{i};
               obj.AddSignalDescription(varargin{i}.SignalDescriptionMap);
               varargin{i}.ShareDatasetImport(obj);
            end
        end   
    end
    
    %% ImportData
    function ImportData(obj, GetFileTitle, dataFilePath, fileName)
        if nargin < 2
            GetFileTitle = 'Import Data';
        end
        ms = mbcmodelview.project.MessageService;
        % MBCProject is the mdevproject object – not mbcmodel.project command-line
        P = address(dataobject(obj.TestPlan.MbcProject.MBCProject));
        ms.setNewProject(P)
        obj.NumDatasets = length(obj.TestPlan.MbcProject.MBCProject.Data);
        
        % Get the data using the data loading wizard
        if nargin <= 2
           [ss,OK] = xregImportDataFile(GetFileTitle);
        elseif nargin ==3
           [ss,OK] = xregImportDataFile(GetFileTitle,dataFilePath); 
        elseif nargin ==4
           % path=fullfile(matlabroot,'toolbox','autoblks','autoblksshared','mbc');
           % addpath(path);
           [ss,OK] = autoblksImportDataFile(fileName);  
        end
        
        if OK
           % add data to project
           createData(ms,ss);
           obj.AddNewDataCallback;
        end
     
    end
    
    %% EditData
    function EditData(obj)
        % Close Data editor if open
        f = xregdatagui.Editor.find('dataEditor');
        if ~isempty(f)
            if strcmp(f.Visible, 'on')
                close(f.Figure)
            end
        end

        % get internal information from command-line project
        % no test plan yet so use project level
        P = address(dataobject(obj.TestPlan.MbcProject.MBCProject));
        ms = mbcmodelview.project.MessageService;
        ms.setNewProject(P);
        % select current data set

        DataPtr = -1;
        for i = 1:length(ms.Datasets)
            CurrDataset = info(ms.Datasets(i));
            if strcmp(get(CurrDataset, 'label'), obj.Name)
                DataPtr = ms.Datasets(i);
                break;
            end
        end
        ms.CurrentData = DataPtr;

        % listener
        fClose = @(h,evt) obj.EditDataCallback();
        obj.DataEditCloseAction = event.listener(ms,'DataUpdated',fClose);

        % import data and open editor
        execute(ms.Actions.EditData);
    end

    %% isDataImported
    function DataImported = isDataImported(obj)
        if isempty(obj.TestPlan.MbcProject.MBCProject)
            DataImported = false;
        else
            AllDatasets = obj.TestPlan.MbcProject.MBCProject.Data;
            DataNames = cell(size(AllDatasets));
            for i = 1:length(DataNames)
                DataNames{i} = AllDatasets(i).Name;
            end   
            DataImported = any(strcmp(DataNames, obj.Name)) || obj.TestPlan.isTestPlanCreated;
        end
        
        
    end
    
    %% isAllRequiredImported
    function [DataImported, SignalInfo] = isAllRequiredImported(obj)
        if obj.isDataImported
            SignalInfo = obj.AllImportSignalInfo;
            DataImported = all([SignalInfo.isImported]);
        else
            SignalInfo = [];
            DataImported = false;
        end
    end

    %% isDataInTestPlan
    function isDataUsed = isDataInTestPlan(obj)
        Dataset = obj.getDatasetObj;
        if ~isempty(Dataset)
            isDataUsed = isa(dataobject(Dataset),'testplansweepsetfilter');
        else
           isDataUsed = false; 
        end
    end
    
    %% DataBndryRange
    function Range =  DataBndryRange(obj, InputName)
        Range = obj.getDataRanges(InputName);
    end
    
    %% DataUpperBndry
    function UpperBndry =  DataUpperBndry(obj, SignalName)
        SignalRange = obj.getDataRanges(SignalName);
        UpperBndry = SignalRange(2);
    end
    
    %% DataLowerBndry
    function LowerBndry =  DataLowerBndry(obj, SignalName)
        SignalRange = obj.getDataRanges(SignalName);
        LowerBndry = SignalRange(1);
    end
    
    %% getDataSignals
    function SignalNames = getDataSignals(obj)
        SignalNames = {};
        try
            Dataset = obj.getDatasetObj;
            if ~isempty(Dataset)
                SignalNames = Dataset.SignalNames;
            else
                SignalNames = [];
            end
        catch
            
        end
    end
    
    %% getDatasetObj
    function Dataset = getDatasetObj(obj, CheckTestPlan)
        if nargin < 2
            CheckTestPlan = false;
        end
        Dataset = [];
        if ~isempty(obj.TestPlan)
            if obj.TestPlan.isNewestDataUsed
                TestPlanObj = obj.TestPlan.getMBCTestPlanObject;
                Dataset = TestPlanObj.Data;
            else
                try
                    AllDatasets = obj.TestPlan.MbcProject.MBCProject.Data;
                catch
                    AllDatasets = [];
                end
                for i = 1:length(AllDatasets)
                    if strcmp(obj.Name, AllDatasets(i).Name)
                        Dataset = AllDatasets(i);
                        break;
                    end
                end
            end  
        end
        
        if isempty(Dataset) && ~CheckTestPlan
            CheckTestPlan = obj.TestPlan.getMBCTestPlanObject;
            if ~isempty(CheckTestPlan)
                obj.Name = CheckTestPlan.Data.Name;
                Dataset = obj.getDatasetObj(true);
            end
        end 
    end
    
    %% AddOptionalVars
    function AddOptionalVars(obj)
        % Setup
        SignalInfo = obj.AllImportSignalInfo;
        Dataset = obj.getDatasetObj;
        Dataset.BeginEdit;
        
        % Check for optional signals
        for i = 1:length(SignalInfo)
           if ~SignalInfo(i).isRequired && ~SignalInfo(i).isImported
               if obj.TestPlan.OptionalImportSignals.isKey(SignalInfo(i).Name)
                   Dataset.AddVariable([SignalInfo(i).Name, ' = ', ...
                       obj.TestPlan.OptionalImportSignals(SignalInfo(i).Name)], SignalInfo(i).Units);
               end
           end     
        end
        Dataset.CommitEdit;
    end
    %% TestPlanSignalInfo
    function SignalInfo = TestPlanSignalInfo(obj)
        SignalInfo = obj.TestPlan.TemplateSignalInfo;
        
        % Add description 
        for i = 1:length(SignalInfo)
            SignalName = SignalInfo(i).Name;
            if obj.SignalDescriptionMap.isKey(SignalName)
                SignalInfo(i).Description = obj.SignalDescriptionMap(SignalName);
            else
                SignalInfo(i).Description = '';
            end
        end
        
        % Check if imported
        SignalNames = obj.getDataSignals;
        for i = 1:length(SignalInfo)
            if isempty(SignalNames)
                SignalInfo(i).isImported = false;
            elseif any(strcmp(SignalNames, SignalInfo(i).Name))
                SignalInfo(i).isImported = true;
            else
                SignalInfo(i).isImported = false;
            end
            SignalInfo(i).isDerivedVar = false;
            SignalInfo(i).DerivedVarExp = '';
        end   
        
        % Dervied parameters
        if ~isempty(SignalNames)
            DataObj = dataobject(obj.getDatasetObj);
            VarStruct = get(DataObj, 'variables');
            for i = 1:length(VarStruct)
                DerivedVarSelect = (strcmp({SignalInfo.Name}, VarStruct(i).varName));
                SignalInfo(DerivedVarSelect).isDerivedVar = true;
                SignalInfo(DerivedVarSelect).DerivedVarExp = VarStruct(i).varExp;
            end    
        end
    end
    
    %% AllImportSignalInfo
    function SignalInfo = AllImportSignalInfo(obj)
        SignalInfo = obj.TestPlanSignalInfo;       
        for i = 1:length(obj.SharedImportDatasets)
            SignalInfo = [SignalInfo, obj.SharedImportDatasets(i).TestPlanSignalInfo];   %#ok<*AGROW>
        end        
        [~,IA,IC] = unique({SignalInfo.Name}, 'stable');
        idx = 1:length(SignalInfo);
        for i = 1:length(IA)
            if any([SignalInfo(IC==i).isRequired])
                for j = idx(IC==i)
                    SignalInfo(j).isRequired = true;
                end
            end
        end
        SignalInfo = SignalInfo(IA);
    end
    %% AddNewDataCallback
    function AddNewDataCallback(obj, ~, ~)
        PrevNumDatasets = obj.NumDatasets;
        obj.NumDatasets = length(obj.TestPlan.MbcProject.MBCProject.Data);
        obj.DataEditCloseAction.Enabled = false;
        obj.DataEditCloseAction = [];  
        if obj.NumDatasets > PrevNumDatasets
            obj.TestPlan.deleteTestPlanObj;
            for i = 1:length(obj.SharedImportDatasets)
                obj.SharedImportDatasets(i).TestPlan.deleteTestPlanObj;
            end
            % Set name
            idx = length(obj.TestPlan.MbcProject.MBCProject.Data);
            obj.TestPlan.MbcProject.MBCProject.Data(idx).Name = obj.BaseName;
            obj.Name = obj.TestPlan.MbcProject.MBCProject.Data(idx).Name;

            for i = 1:length(obj.SharedImportDatasets)
                obj.SharedImportDatasets(i).Name = obj.Name;
            end
            obj.AddOptionalVars;
            if obj.SaveOnDataEditClose
               obj.TestPlan.MbcProject.SaveProject;
            end
        end
        obj.ImportDataEditorCloseFcn();
    end
    
    %% EditDataCallback
    function EditDataCallback(obj)
        obj.AddOptionalVars;
        obj.EditDataEditorCloseFcn();
        if obj.SaveOnDataEditClose
            obj.TestPlan.MbcProject.SaveProject;
        end
    end
end

methods (Access = private)
    %% getDataRanges
    function SignalRange = getDataRanges(obj, SignalName)
        Data = obj.getDatasetObj;
        AllNames = Data.SignalNames;
        AllValues = Data.Value;
        SignalValue = AllValues(:, strcmp(SignalName, AllNames'));
        SignalRange = [min(SignalValue), max(SignalValue)];
    end
end
end