function currentTest = deriveFunctionalTestCase(obj, signalLogger, dsmLogger, tcIdx)
    %

    %   Copyright 2014-2023 The MathWorks, Inc.
    
    if isa(signalLogger,'Simulink.ModelDataLogs')
        convertedDatasetLogger = signalLogger.convertToDataset('convertedDatasetLogger');
    elseif isa(signalLogger,'Simulink.SimulationData.Dataset')
        convertedDatasetLogger = signalLogger;
    else
        obj.generateErrorMessageForLogger;
    end
    if isempty(dsmLogger)
        % do nothing
    elseif isa(dsmLogger,'Simulink.ModelDataLogs')
        convertedDSMDatasetLogger = dsmLogger.convertToDataset('convertedDatasetLogger');
    elseif isa(dsmLogger,'Simulink.SimulationData.Dataset')
        convertedDSMDatasetLogger = dsmLogger;
    else
        obj.generateErrorMessageForLogger;
    end
    % Construct Test Cases as an
    % Simulink.SimulationData.Dataset object
    currentTest = Simulink.SimulationData.Dataset;
    currentTest.Name = sprintf('%s:%d','Test Case',tcIdx);
    
    % Deriving from Signal Logging
    numPorts = length(obj.PortHsToLog);
    for idx=1:numPorts
        inportPath = getfullname(get_param(obj.PortHsToLog(idx),'Parent'));
        signalObj = convertedDatasetLogger.getElement(inportPath,'-blockPath');
        assert(isa(signalObj,'Simulink.SimulationData.Signal'));
        currentTest = ...
            currentTest.setElement(idx,signalObj.Values,sprintf('%d',idx));
    end
    
    % Deriving from Data Store Memory logging
    numDSMs = length(obj.dsmHsToLog);
    if ~isempty(obj.globalDsmData)
        numGlobals = length(obj.globalDsmData.varsUsage);
    else
        numGlobals = 0;
    end
    
    if numDSMs == 0 && numGlobals == 0
        return;
    end
    inPortInfo = obj.subsystemIO.InputPortInfo;

    DSMNamesInDataSet = convertedDSMDatasetLogger.getElementNames;
    
    portSignalLables = cell(1,length(inPortInfo));
    for idx=numPorts+1:length(inPortInfo)
        portSignalLables{idx} = inPortInfo{idx}.SignalLabels;
    end
    
    if exist('signalObj','var')
        baseTSVec = signalObj.Values.Time;
    else
        baseTSVec = (obj.intervalStartTime:obj.subsystemIO.SampleTimes:obj.intervalStopTime)';
    end
    
    numBaseTs = numel(baseTSVec);
    
    for idx=1:numDSMs+numGlobals
        if idx <= numDSMs
            thisDataStoreName = get_param(obj.dsmHsToLog(idx),'DataStoreName');
        else
            thisDataStoreName = obj.globalDsmData.varsUsage(idx-numDSMs).Name;
        end
        dsmIdx = find(strcmp(DSMNamesInDataSet,thisDataStoreName));
        signalObj = convertedDSMDatasetLogger.getElement(dsmIdx); %#ok<FNDSB>
        portInfoIdx = strcmp(portSignalLables,thisDataStoreName);
        thisPortInfo = inPortInfo{portInfoIdx};
        
        ts = deriveProperDSMValue(signalObj,thisPortInfo, baseTSVec ,numBaseTs,obj.TopLevelModelH);
        ts.Name = signalObj.Name;
        currentTest = ...
            currentTest.setElement(idx+numPorts,ts,sprintf('%d',idx+numPorts));
    end
end
function ts = deriveProperDSMValue(sig,portInfo, baseTsVec, numBaseTs,modelH)
    % This function derives proper values from data store logging from
    % prior DS writers information in portInfo.
    % (1) If no DSW executed prior to the selected subsystem,
    % the last written value of the previous step should be taken.
    % (2) If one or multiple DSWs are executed to the selected subsystem,
    % the last written value prior to the selected subsystem in the same
    % step should be taken. If no DSW is executed in a particular step
    % (i.e. DSWs are in conditionally executed subsystem), the last
    % written value of the previous step should be taken.
    % Note:
    %  sig.DSMWriters indexes are decided in an arbitrary order.
    %  sig.Values.Data is stored in execution order.
    
    
    if isempty(sig.Values) || isempty(sig.Values.Time)
        % This could have initial value but we don't care it since it is
        % not used in sliced model.
        ts = timeseries(cast([], portInfo.DataType));
        return;
    end
    uniqTime = unique(sig.Values.Time,'stable');
    
    priorWriters = portInfo.priorWriters;
    noPriorWriters = isempty(priorWriters);
    nDim = portInfo.Dimensions;
    allTime = sig.Values.Time;
    allData = sig.Values.Data;
    DSMWriters = sig.DSMWriters;

    colonStr = '';
    if length(nDim) == 1
        isVector = true;
    else
        isVector = false;
        for p=1:length(nDim)
            % Constructing eval string
            colonStr = [colonStr ':,']; %#ok<AGROW>
        end
    end
    
    ts = timeseries();
    
    try
        if strcmpi(sig.Scope,'global')
            sigObj = getVarFromGlobalScope(modelH,sig.Name);
            initValueStr = sigObj.InitialValue;
            if isempty(initValueStr)
                initValueStr = '0';
            end
        else
            dsmBlockPath  = sig.BlockPath.getBlock(1);
            initValueStr = get_param(dsmBlockPath,'InitialValue');
        end
        initValue = modelslicerprivate('evalinModel',getfullname(modelH),initValueStr);
    catch mex %#ok<NASGU>
        if isVector
            initValue = zeros(1,nDim);
        else
            initValue = zeros([nDim 1]);
        end
    end
    
    % Compute index to be taken from allData.
    tIdx = getTimeIndex();

    initIdx = find(tIdx ==0);
    if isVector
        % Vector case
        if ~isempty(initIdx)
            % create initial data part
            dataInit = zeros(length(initIdx),nDim);
            for p=initIdx
                dataInit(p,:) = initValue;
            end
            % create logged data part
            idxNoInit = tIdx(tIdx~=0);
            dataNoInit = allData(idxNoInit,:);
            
            data = [dataInit; dataNoInit];
        else
            % Initial data is not used.
            data = allData(tIdx,:);
        end
    else
        % Matrix case
        % To this function universal to given dimensions, we use ":," times
        % number of dimensions and eval function.
        % Example:
        %  allData = ones(3,4,5,101);
        %  tIdx = [2 5 10 50 77];
        %  data = eval('allData(:,:,:,tIdx);')
        if ~isempty(initIdx)
            dataInit = initValue;
            for p=1:length(initIdx)-1
                dataInit = cat(length(nDim)+1, dataInit, initValue);
            end
            idxNoInit = tIdx(tIdx~=0); %#ok<NASGU>
            dataNoInit = eval(['allData(' colonStr 'idxNoInit);']);
            data = cat(length(nDim)+1,dataInit,dataNoInit);
        else
            data = eval(['allData(' colonStr 'tIdx);']);
        end
    end
    
    ts.Time = baseTsVec;
    ts.Data = data;

    function tIdx = getTimeIndex()
        % The DSM might not be logged or might have multiple logged data 
        % in a given time. 
        % This function returns index to be taken from allData.
        %
        %  allData(tIdx) will give data for t=0:Ts:StopTime.
        %
        % Note: the index value "0" means to use initial value.
        % Examples are commented in the if sections.
        %
        tIdx = zeros(numBaseTs,1);
        allTimeLen = numel(allTime);
        
        if allTimeLen == length(uniqTime)
            %% Single writer
            if allTimeLen == numBaseTs
                % Fast path for no multiple write and has all time steps
                if noPriorWriters
                    tIdx = (0:allTimeLen-1)';
                else
                    tIdx = (1:allTimeLen)';
                end
                return;
            else
                tIdx = normalizeIndex(allTime);
            end
            return;
        end
        
        %% Multiple writers
        
        % Getting indexes for each time data
        timeIdx = zeros(length(uniqTime),2);
        sIdx = 1;
        for m=1:length(uniqTime)
            eIdx = sIdx + 1;
            while eIdx <= allTimeLen && allTime(eIdx) == uniqTime(m)
                eIdx = eIdx + 1;
            end
            timeIdx(m,:) = [sIdx eIdx-1];
            sIdx = eIdx;
        end
        
        if noPriorWriters
            % Multiple writers and fastest execution (read-before-write)
            if isequal(uniqTime,baseTsVec)
                % Fast path 1: initValue + use last written values
                tIdx = [0; timeIdx(1:end-1,2)];
            elseif allTimeLen == numBaseTs
                % Fast path 2: for base-sample-execusion
                % Simply shift 1 step
                tIdx = (0:allTimeLen-1)';
            else
                % Example:
                %  t    = [0 2 2 2 5 7 7 7];
                %  #    = [1 2 3 4 5 6 7 8];
                %  Ts = 1;
                % Results of normalization returns:
                %  t    = [ 0 1 2 3 4 5 6 7 8 9 10];
                %  tIdx = [ 0 0 0 4 4 4 5 5 8 8 8 ];
                tIdx = normalizeIndex(allTime);
            end
        else
            % Multiple writers and DSWs which have faster sorted order exist
            % Identify index of sig.DSMWriters in dsm log.
            % priorWriters is assigned as slower -> faster order.
            % Example:
            %  t = [0 2 2 2 5 7 7 7];
            %  # = [1 2 3 4 5 6 7 8];
            %  w = [b b b a b a b a]  (b: before, a: after)
            %  Ts = 1;
            % Results should be:
            % t     = [ 0 1 2 3 4 5 6 7 8 9 10];
            % tIdx  = [ 0 1 3 4 4 4 5 7 8 8 8];
            %
            % Convert block handles of prior writers -> index in sig.DSMWriters
            priorWritersIdx = [];
            for n=1:length(priorWriters)
                for m=1:length(sig.DSMWriterBlockPaths)
                    % \n is replaced with a white space in DSM log
                    priorWriteBlk = regexprep(getfullname(priorWriters(n)),'\n',' ');
                    blkLen = sig.DSMWriterBlockPaths(m).getLength;
                    for idx = 1:blkLen
                        if strcmp(priorWriteBlk,sig.DSMWriterBlockPaths(m).getBlock(idx))
                            priorWritersIdx(end+1) = m;  %#ok<AGROW>
                            break;
                        end
                    end
                end
            end
            utIdx=1;
            thisIdx = 0;
            extiTime = uniqTime;
            extiTime(end+1) = baseTsVec(end)+1; %Last element always greater than ts
            allIdxVec = 1:allTimeLen;
            for j=1:length(baseTsVec)
                % We need to loop-through all base steps
                if baseTsVec(j) >= extiTime(utIdx)
                    try
                        thisTimeIdx = timeIdx(utIdx,1):timeIdx(utIdx,2);
                        idxThisTime = allIdxVec(thisTimeIdx);
                        thisDSMWriters = DSMWriters(thisTimeIdx);
                        found = false;
                        for k=1:length(priorWritersIdx)
                            aIdx = find(thisDSMWriters == priorWritersIdx(k));
                            if ~isempty(aIdx)
                                % aIdx might not be a scalar for for-iterator ss.
                                tIdx(j) = idxThisTime(aIdx(end));
                                found = true;
                                break;
                            end
                        end
                        if ~found
                            % No prior writer this step
                            % Use the last written value of the previous step.
                            tIdx(j) = thisIdx;
                        end
                        % At this point, the index is determined.
                        % The last written idx should be used for next step
                        if ~isempty(idxThisTime)
                            thisIdx = idxThisTime(end);
                        end
                    catch
                        if isempty(thisIdx)
                            thisIdx = 0;
                        end
                    end
                    utIdx = utIdx+1;
                else
                    tIdx(j)  = thisIdx;
                end
            end
        end
    end
    function tIdx = normalizeIndex(iTime)
        tIdx = zeros(numBaseTs,1);
        
        % Example case:
        % t = [0 2 7];
        % # = [1 2 3];
        % Ts = 1;
        
        extiTime = iTime;
        extiTime(end+1) = baseTsVec(end)+1; %Last element always greater than ts
        
        if noPriorWriters
            % t = 0 1 2 3 4 5 6 7 8 9 10
            % # = 0 1 1 2 2 2 2 2 3 3 3
            thisIdx = 0;
            for n=1:numBaseTs
                idx = find(baseTsVec(n) > extiTime,1,'last');
                if ~isempty(idx)
                    thisIdx = idx;
                end
                tIdx(n) = thisIdx;
            end
        else
            % t = 0 1 2 3 4 5 6 7 8 9 10
            % # = 1 1 2 2 2 2 2 3 3 3 3
            if iTime(1) ~= 0
                thisIdx = 0;
            else
                thisIdx = 1;
            end
            tIdx(1) = thisIdx;
            for n=2:numBaseTs
                idx = find(baseTsVec(n) >= extiTime,1,'last');
                if ~isempty(idx)
                    thisIdx = idx;
                end
                tIdx(n) = thisIdx;
            end
        end
    end
end
