classdef RecordedData < handle & matlab.mixin.CustomDisplay
    %RECORDEDDATA Object representing recorded data.
    %
    % RecordedData objects are used to collect data recorded from multiple
    % sensors on a hardware board into a single container. The dataset
    % contains information about the source objects that represents
    % recorded data sources, meta information about the recording session
    % and user meta information. The recorded data can be extracted from
    % the dataset file and can be played back in Simulink out of device
    % driver blocks.
    %
    % A RecordedData object has the following properties:
    %
    % Filename       - The file name for the dataset
    % Description    - User meta data describing dataset
    % HardwareBoard  - The name of the hardware board used for data collection
    % Tags           - A cell array of user tags
    % Date           - The date dataset was created
    % Sources        - A cell array of source names within the dataset
    %
    % ds = RecordedData(fileName) creates an object corresponding
    % to the specified data file.
    %
    %
    % Examples:
    %
    % ds = RecordedData('dataset.tgz');
    % ts = getData(ds,'My source');
    
    % Copyright 2016-2018 The MathWorks, Inc.
    
    properties
        Description char
        Hardware char
        Tags cell
        Reader
        DataType
    end
    
    properties (Dependent, SetAccess = private)
        Filename char
        Sources cell
    end
    properties (Access = private, Constant)
        PARAM_MATCH_LIST = {'Pin','DataType','DataSize',...
            'DataLength','DataByteLength','TypeByteSize','DataLength',...
            'SampleTime','SamplesPerFrame','DataFile','SampleRate',...
            'SamplesPerFrame','NumberOfChannels','RecordedSampleTime',...
            'NumberOfDataPoints','QueueDuration','DataFile','SelectSourceDlg','OutputDataType'}
    end
    
    properties (GetAccess = public, SetAccess = private, Transient = true)
        Date
    end
    
    properties (Access = private, Transient = true)
        ResolvedFilename_ = ''     % The name of the resolved dataset file
        UncompressLocation_ = ''   % Location of uncompressed files
        Info_                      % Dataset information in file
        LocMap_ = containers.Map   % List of uncompressed dataset location
    end
    
    properties (Constant,Hidden)
        Version = '2.0';
    end
    
    methods
        function obj = RecordedData(fileName)
            if nargin < 1
                return;
            end
            fileName = convertStringsToChars(fileName);
            % Resolve file location
            obj.ResolvedFilename_ = ioplayback.util.resolveFile(fileName,'.tgz');
            dirInfo = dir(obj.ResolvedFilename_);
            
            % Load Dataset.info file and extract sources
            % ??? Should re-loading a saved object preserver uncompress
            % location?
            if isKey(obj.LocMap_,obj.ResolvedFilename_)
                fileInfo = obj.LocMap_(obj.ResolvedFilename_);
                if (fileInfo.Dir.datenum == dirInfo.datenum) && ...
                        (fileInfo.Dir.bytes == dirInfo.bytes)
                    try
                        obj.UncompressLocation_ = ioplayback.util.resolveDirectory(...
                            fileInfo.UncompressLocation);
                    catch
                        remove(obj.LocMap_,obj.ResolvedFilename_);
                        uncompressDataset(obj);
                    end
                else
                    remove(obj.LocMap_,obj.ResolvedFilename_);
                    uncompressDataset(obj);
                end
            else
                uncompressDataset(obj);
            end
            
            % Load dataset infro
            infoFile = fullfile(obj.UncompressLocation_,'dataset.mat');
            obj.Info_= obj.loadInfo(infoFile);
            fileInfo = createFileInfo(obj.ResolvedFilename_,obj.UncompressLocation_);
            obj.LocMap_(obj.ResolvedFilename_) = fileInfo;
            
            % Populate object properties
            obj.Hardware = obj.Info_.Hardware;
            obj.Description = obj.Info_.Description;
            obj.Tags = obj.Info_.Tags;
            obj.Date = obj.Info_.Date;
            if isequal(obj.Info_.Version , '0.1' ) || isequal(obj.Info_.Version , '1.0' )
                obj.createNewDataset(obj.Info_)
            end
        end
        function obj = createNewDataset(obj,info)
            disp('Converting old data format to new format');
            % Create a temporary directory to save intermediate data
            for k = 1:numel(info.Source)
                
                orgHWFile = fullfile(obj.UncompressLocation_,info.Source{k}.DataFile );
                
                HWFid = fopen(orgHWFile ,'r');
                if HWFid < 0
                    error(message('ioplayback:general:CanNotOPenFileForRead'));
                end
                
                srcObj = info.Source{k};
                info.Source{k} = [];
                prevWarnStatus = warning('off', 'MATLAB:structOnObject');
                classParams = struct(srcObj) ;
                warning(prevWarnStatus)
                props = fieldnames(classParams);
                for j = 1:numel(props)
                    if ismember(props{j},obj.PARAM_MATCH_LIST)
                        info.Source{k}.params.(props{j})= classParams.(props{j});
                    end
                end
                if isfield(info.Source{k}.params, 'DataLength')
                    info.Source{k}.params.HWSignalInfo.Dimensions = [info.Source{k}.params.DataLength 1 ];
                    info.Source{k}.params.DataLen =  info.Source{k}.params.DataLength;
                    
                elseif isfield(info.Source{k}.params, 'DataSize')
                    info.Source{k}.params.HWSignalInfo.Dimensions = prod(info.Source{k}.params.DataSize);
                    info.Source{k}.params.DataLen =  prod(info.Source{k}.params.DataSize);
                end
                if isfield(info.Source{k}.params, 'DataType')
                    info.Source{k}.params.HWSignalInfo.DataType = info.Source{k}.params.DataType;
                elseif isfield(info.Source{k}.params, 'OutputDataType')
                    info.Source{k}.params.HWSignalInfo.DataType = info.Source{k}.params.OutputDataType ;
                else
                    info.Source{k}.params.HWSignalInfo.DataType = 'double' ;
                end
                info.Source{k}.params.HWSignalInfo.IsComplex = false ;
                
                info.Source{k}.SourceType = 'ioplayback.SourceSystem';
                info.Source{k}.params.PayloadSizeFieldLen = -1;
                info.Source{k}.params.DataType = info.Source{k}.params.HWSignalInfo.DataType ;
                info.Source{k}.SourceName =  srcObj.SourceName;
                info.Source{k}.HdrSize = 0 ;
                
                tmpTFile = fullfile(obj.UncompressLocation_, sprintf('src_%d_T.bin',k-1) ) ;
                
                if isfield(info.Source{k}.params, 'SampleTime')
                    tmpDFile = fullfile(obj.UncompressLocation_, sprintf('src_%d_D.bin',k-1) ) ;
                    y = fread(HWFid ,Inf ,['*' info.Source{k}.params.HWSignalInfo.DataType]);
                    length(y);
                    ys = length(y);
                    te = (ys / double(prod(info.Source{k}.params.HWSignalInfo.Dimensions))) * info.Source{k}.params.SampleTime ;
                    info.Source{k}.DataFile{1} =  tmpTFile ;
                    info.Source{k}.DataFile{2} =  tmpDFile ;
                    info.Source{k}.params.FileFormat = 'TimeStamp';
                    tmpDataFid = fopen(tmpDFile,'wb');
                    if tmpDataFid < 0
                        error(message('ioplayback:general:CanNotOPenFileForWrite'));
                    end
                    
                elseif isfield(info.Source{k}.params, 'SampleRate') % for audio case
                    tmpDFile = orgHWFile ;  %#ok<NASGU> % sprintf('src_%d_D.wav',k) ;
                    [y,fs] = audioread(orgHWFile ,[1,inf],'native');
                    ys = size(y,1);
                    te = ys / fs;
                    info.Source{k}.DataFile{1} =  orgHWFile ;
                    info.Source{k}.DataFile{2} =  orgHWFile ;
                    info.Source{k}.params.FileFormat = 'Wave';
                end
                tmpTimeFid = fopen(tmpTFile,'wb');
                if tmpTimeFid < 0
                    error(message('ioplayback:general:CanNotOPenFileForWrite'));
                end
                
                tseries = linspace(0,te,ys);
                % Writting the Timestamp information for current operation
                fwrite(tmpTimeFid, int32(0),'int32'  );
                fwrite(tmpTimeFid, tseries , '*double');
                
                if isfield(info.Source{k}.params, 'SampleTime')
                    %Writting the data information for current operation
                    fwrite(tmpDataFid, int32(0),'int32'  );
                    fwrite(tmpDataFid, y , info.Source{k}.params.HWSignalInfo.DataType);
                    fclose(tmpDataFid);
                end
                fclose(HWFid);
                fclose(tmpTimeFid);
            end
            info.Version = '0.2' ;
            obj.Info_ = info;
        end
        function info = exportSysObjectPropToStruct(obj,info)
            disp('Converting old data format to new format \n');
            % Create a temporary directory to save intermediate data
            for k = 1:numel(obj.Info_.Source)
                orgHWFile = obj.Info_.Source{k}.DataFile ;
                srcObj = obj.Info_.Source{k};
                obj.Info_.Source{k} = [];
                prevWarnStatus = warning('off', 'MATLAB:structOnObject');
                classParams = struct(srcObj) ;
                warning(prevWarnStatus)
                props = fieldnames(classParams);
                for j = 1:numel(props)
                    if ismember(props{j},obj.PARAM_MATCH_LIST)
                        info.Source{k}.params.(props{j})= classParams.(props{j});
                    end
                end
                if isfield(info.Source{k}.params, 'DataType')
                    info.Source{k}.params.HWSignalInfo.DataType = info.Source{k}.params.DataType;
                elseif isfield(info.Source{k}.params, 'OutputDataType')
                    info.Source{k}.params.HWSignalInfo.DataType = info.Source{k}.params.OutputDataType ;
                else
                    info.Source{k}.params.HWSignalInfo.DataType = 'double' ;
                end
                info.Source{k}.params.HWSignalInfo.IsComplex = false ;
                
                info.Source{k}.SourceType = 'ioplayback.SinkSystem';
                info.Source{k}.params.PayloadSizeFieldLen = -1;
                info.Source{k}.params.DataType = info.Source{k}.params.HWSignalInfo.DataType ;
                info.Source{k}.SourceName =  srcObj.SourceName;
                info.Source{k}.HdrSize = 0 ;
                info.Source{k}.DataFile{1} =  orgHWFile ;
                info.Source{k}.DataFile{2} =  orgHWFile ;
                info.Source{k}.params.FileFormat = 'Wave';
                
            end
        end
        
        
        function ret = get.Sources(obj)
            ret = {};
            if ~isempty(obj.Info_) && isfield(obj.Info_,'Source')
                for k = 1:numel(obj.Info_.Source)
                    ret{end+1} = obj.Info_.Source{k}.SourceName; %#ok<AGROW>
                end
            end
        end
        
        function ret = get.Filename(obj)
            ret = obj.ResolvedFilename_;
        end
        
        function source = getDataSource(obj,sourceName)
            % This function returns the Source object by name or index.
            % Invalid name or invalid index causes return of an empty object
            if nargin == 2
                sourceName = convertStringsToChars(sourceName);
            end
            validateattributes(sourceName,{'char'},{'nonempty','row'},'','sourceName');
            allSourceNames = getAllSourceNames(obj);
            sourceName = validatestring(sourceName,allSourceNames);
            
            % At this point, we know that sourceName is in allSourceNames
            % Just return the corresponding source object
            source = obj.Info_.Source{ismember(allSourceNames,sourceName)};
            if isstruct(source)
                if isequal(source.SourceType,'ioplayback.SinkSystem')
                    source.SendSimulationInputTo = 'Data file';
                else
                    source.SimulationOutput = 'From recorded file';
                end
                source.DatasetName = obj.Filename;
            end
        end
        
        
        function ret = getDataFile(obj,sourceName)
            if nargin == 2
                sourceName = convertStringsToChars(sourceName);
            end
            allSourceNames = getAllSourceNames(obj);
            sourceName = validatestring(sourceName,allSourceNames);
            
            ret = {'',''};
            for k = 1:numel(obj.Info_.Source)
                if isequal(sourceName,obj.Info_.Source{k}.SourceName)
                    if exist(obj.Info_.Source{k}.DataFile{1},'file')
                        ret = obj.Info_.Source{k}.DataFile;
                    else
                        ret{1} = fullfile(obj.UncompressLocation_,obj.Info_.Source{k}.DataFile{1});
                        ret{2} = fullfile(obj.UncompressLocation_,obj.Info_.Source{k}.DataFile{2});
                    end
                    break;
                end
            end
        end
        function ts = getData(obj,sourceName)
            if nargin == 2
                sourceName = convertStringsToChars(sourceName);
            end
            dSrc = getDataSource(obj,sourceName);
            % Reading the Audio file
            
            if isequal(dSrc.params.FileFormat,'wave') ||  isequal(sourceName, 'plughw:1,0') || isfield(dSrc.params , 'SampleRate')
                dataFile = getDataFile(obj ,sourceName);
                [y,fs] = audioread(dataFile{2} ,[1,inf],'native');
                ys = size(y,1);
                te = ys / fs;
                ts = timeseries(y,linspace(0,te,ys));
            else % Reading the timestamped File
                dataFile = getDataFile(obj ,sourceName);
                if dSrc.params.DataLen == -1
                    variableSizeData = 1;
                else
                    variableSizeData = 0;
                end
                obj.Reader = ioplayback.util.MultiPortReader('Filename',dataFile, ...
                    'SignalInfo',dSrc.params.HWSignalInfo,...
                    'HWSignalInfo',dSrc.params.HWSignalInfo,...
                    'HdrSize',dSrc.HdrSize,...
                    'DataLen',dSrc.params.DataLen,...
                    'PayloadSizeFieldLen',dSrc.params.PayloadSizeFieldLen);
                setup(obj.Reader,1);
                if  variableSizeData
                    tStamps=obj.Reader.SamplesCollected;
                    ts = timeseries(dSrc.SourceName);
                    dt = zeros([dSrc.params.DataSize,1],dSrc.params.DataType);
                    for i = 1:tStamps
                        tstamp = readTimestamp(obj.Reader);
                        data = step(obj.Reader,1);
                        dt(1:length(data)) = data;
                        dt(length(data)+1:end) = nan;
                        ts = addsample(ts,'Data',dt,'Time',tstamp);
                    end
                else
                    data = [];
                    tseries = [];
                    prevTstamp = 0;
                    tStamps = obj.Reader.SamplesCollected;
                    for i = 1:tStamps
                        tStamp = readTimestamp(obj.Reader);
                        samples = step(obj.Reader,1 );
                        tseries =  [ tseries ; linspace(prevTstamp , tStamp ,  dSrc.params.SamplesPerFrame )' ]; %#ok<AGROW>
                        data = [data ; samples]; %#ok<AGROW>
                        prevTstamp = tStamp;
                    end
                    ts = timeseries( data ,tseries);
                end
            end
        end
        
        function addDataSource(obj,sourceObj,sourceName,dataFile)
            if nargin < 4
                sourceName = convertStringsToChars(sourceName);
            end
            if nargin == 4
                sourceName = convertStringsToChars(sourceName);
                dataFile = convertStringsToChars(dataFile);
            end
            validateattributes(sourceObj,{'ioplayback.System'},{'nonempty'},'','sourceObj');
            validateattributes(sourceName,{'char'},{'nonempty','row'},'','sourceName');
            validateattributes(dataFile,{'char'},{'nonempty','row'},'','dataFile');
            if ismember(sourceName,obj.Sources)
                error(message('ioplayback:utils:RepeatSourceName',sourceName));
            end
            newSrcObj = clone(sourceObj);
            dataFile = ioplayback.util.resolveFile(dataFile);
            %checkDataFile(newSrcObj,dataFile);
            if isa(newSrcObj,'ioplayback.SourceSystem')
                newSrcObj.SimulationOutput = 'From recorded file';
            else
                newSrcObj.SendSimulationInputTo = 'Data file';
            end
            newSrcObj.SourceName = sourceName;
            newSrcObj.DataFile   = dataFile;
            if isempty(obj.Info_) || ~isfield(obj.Info_,'Source')
                obj.Info_.Source{1} = newSrcObj;
            else
                obj.Info_.Source{end+1} = newSrcObj;
            end
            info.DatasetName=sourceObj.DatasetName; %#ok<STRNU>
        end
        
        function save(obj,fileName)
            if nargin == 1
                return ;
            end
            if nargin == 2
                fileName = convertStringsToChars(fileName);
            end
            validateattributes(fileName,{'char'},{'nonempty','row'},'','fileName');
            
            % Create info structure that will go into dataset
            info.Version  = obj.Version;
            info.Date     = datetime;
            info.Tags     = obj.Tags;
            info.Hardware = obj.Hardware;
            info.Description = obj.Description;
            fileList = {}; %#ok<NASGU>
            if isempty(obj.UncompressLocation_)
                tDirName = tempname;
                mkdir(tDirName);
            else
                tDirName = obj.UncompressLocation_;
            end
            % Add cleanup task to delete temporary directory
            tdirdel = onCleanup(@() rmdir(tDirName,'s'));
            
            if ~isempty(obj.Info_)
                %start of filling all the signal paremeters
                InfofileList = cell(1,1+ 2 *(numel(obj.Info_.Source)));
                k = 0;
                for i = 1:2:2*numel(obj.Info_.Source)
                    if ~isstruct(obj.Info_.Source{k+1})
                        if isprop(obj.Info_.Source{k+1} , 'SampleRate')
                            info = obj.exportSysObjectPropToStruct(info);
                            InfofileList{i}   = info.Source{k+1}.DataFile{1} ;
                            InfofileList{i+1} = info.Source{k+1}.DataFile{2};
                            info.Source{k+1}.NumberOfDataPoints = 0;
                        else
                            orgHwFile = obj.Info_.Source{k+1}.DataFile;
                            InfofileList{i} = sprintf('src_%d_T.bin',k );
                            InfofileList{i+1} = sprintf('src_%d_D.bin',k );
                            
                            SignalFid = fopen(orgHwFile,'r');
                            if SignalFid < 0
                                error(message('ioplayback:general:CanNotOPenFileForRead'));
                            end
                            
                            TimeFid = fopen(fullfile(tDirName,InfofileList{i}),'wb');
                            if TimeFid < 0
                                error(message('ioplayback:general:CanNotOPenFileForWrite'));
                            end
                            
                            DataFid = fopen(fullfile(tDirName,InfofileList{i+1}),'wb');
                            if DataFid < 0
                                error(message('ioplayback:general:CanNotOPenFileForWrite'));
                            end
                            
                            hdrSize = fread(SignalFid,1,'*uint32');
                            % updating the source properties
                            headerType =  fread(SignalFid,1,'*uint32'); %#ok<NASGU>
                            magicNumber =  (fread(SignalFid,1,'*uint32'));
                            if magicNumber == hex2dec('a1b2c3d4')
                                endian = 'ieee-le' ; %#ok<NASGU>
                            elseif  magicNumber == hex2dec('d4c3b2a1')
                                endian = 'ieee-be' ; %#ok<NASGU>
                            end
                            version=  (fread(SignalFid,1,'*double'))'; %#ok<NASGU>
                            info.Source{k+1}.params.FileFormat =  deblank((fread(SignalFid, 20 ,'*char'))');
                            fread(SignalFid,48,'*char');
                            info.Source{k+1}.params.PeripheralName =         deblank((fread(SignalFid,32,'*char') )');
                            info.Source{k+1}.params.RecordDuration =         fread(SignalFid,1,'*double');
                            info.Source{k+1}.params.SampleTime =             fread(SignalFid,1,'*double');
                            info.Source{k+1}.params.FrameSize =              fread(SignalFid,1,'*uint32');
                            info.Source{k+1}.params.DataLen =                 fread(SignalFid,1,'*int32');
                            info.Source{k+1}.params.RecordingStartTime =     fread(SignalFid,1,'*double');
                            info.Source{k+1}.params.PayloadSizeFieldLen=     fread(SignalFid,1,'*uint32');
                            info.Source{k+1}.params.TotalReceivedSamples =   fread(SignalFid,1,'*uint32');
                            info.Source{k+1}.params.DataType =               fread(SignalFid, 8 ,'*char')';
                            info.Source{k+1}.params.DataType =               deblank(info.Source{k+1}.params.DataType);
                            obj.DataType = info.Source{k+1}.params.DataType;
                            
                            reserveInfoLen =  fread( SignalFid,1,'*uint');
                            readCount = 0;
                            while(readCount < reserveInfoLen )
                                namevalueLen = fread( SignalFid,1,'*uint32');
                                nameValuepair =(fread( SignalFid,namevalueLen,'*char'))';
                                test = regexp(nameValuepair,'\=','split');
                                info.Source{k+1}.params.(test{1}) = test{2};
                                readCount = readCount + namevalueLen + 4 ;
                            end
                            info.Source{k+1}.params.NoOfSignals = 1;
                            info.Source{k+1}.params.HWSignalInfo.Dimensions = [info.Source{k+1}.params.DataLen 1 ];
                            info.Source{k+1}.params.HWSignalInfo.DataType =  info.Source{k+1}.params.DataType ;
                            info.Source{k+1}.params.HWSignalInfo.IsComplex = false ;
                            info.Source{k+1}.SourceName = obj.Info_.Source{k+1}.SourceName  ;
                            info.Source{k+1}.SourceType = 'ioplayback.SinkSystem';
                            info.Source{k+1}.DataFile = {InfofileList{i}, InfofileList{i+1} };
                            info.Source{k+1}.HdrSize = hdrSize;
                            
                            if isprop(obj.Info_.Source{1,1},'RegisterOffset')
                                info.Source{k+1}.RegisterOffset = uint32(obj.Info_.Source{1,1}.RegisterOffset);
                            end
                            
                            fseek(SignalFid , 4 , 'bof');
                            header  =  fread(SignalFid,hdrSize,'*uint8');
                            
                            fwrite(TimeFid,hdrSize ,'*uint32');
                            fwrite(TimeFid,header ,'*uint8');
                            fwrite(DataFid,hdrSize,'*uint32');
                            fwrite(DataFid,header,'*uint8');
                            while ~feof(SignalFid)
                                if ~strcmp(info.Source{k+1}.params.FileFormat, 'Raw')
                                    ts = fread(SignalFid,1,'*double');
                                    fwrite(TimeFid,ts,'*double');
                                    if isempty(ts)
                                        break;
                                    end
                                end
                                if  info.Source{k+1}.params.DataLen == -1  % check fixed size/variable size output, -1 for variable size
                                    len = fread(SignalFid,1,'*uint16'); % data size
                                    if isempty(len) || (len <= 0)
                                        break;
                                    end
                                    fwrite(DataFid,len,'*uint16');
                                else
                                    len = info.Source{k+1}.params.DataLen;
                                    info.Source{k+1}.params.SamplesPerFrame = len;
                                end
                                data = fread(SignalFid, len, ['*' info.Source{k+1}.params.DataType] );
                                fwrite(DataFid,data,['*' info.Source{k+1}.params.DataType]);
                            end
                            
                            fclose(SignalFid);
                            fclose(TimeFid);
                            fclose(DataFid);
                            
                            InfofileList{i}=fullfile(tDirName,InfofileList{i});
                            InfofileList{i+1}=fullfile(tDirName,InfofileList{i+1});
                            
                        end
                    else
                        info.Source{k+1} = obj.Info_.Source{k+1};
                        if isequal(obj.Info_.Source{k+1}.params.FileFormat , 'Wave')
                            InfofileList{i}   = info.Source{k+1}.DataFile{1} ;
                            InfofileList{i+1} = info.Source{k+1}.DataFile{2} ;
                        else
                            InfofileList{i} =  fullfile(tDirName, info.Source{k+1}.DataFile{1});
                            InfofileList{i+1} =  fullfile(tDirName, info.Source{k+1}.DataFile{2});
                        end
                    end
                    k = k+1;
                    %end of filling all the signal parameters
                end
                % Create dataset
                datasetInfoFile = fullfile(tempdir,'dataset.mat');
                save(fullfile(tDirName,'dataset.mat'),'info');
                save(datasetInfoFile,'info');
                InfofileList{end} = datasetInfoFile;
                [p,f,ext] = fileparts(fileName);
                if ~isequal(ext,'.tgz')
                    fileName = fullfile(p,[f '.tgz']);
                end
                
                tar(fileName,InfofileList);
            end
        end
        
        
    end
    
    methods (Hidden)
        function ret = isDataSourcePresent(obj,sourceName)
            [~,ret] = ismember(sourceName,obj.Sources);
        end
        
        function addDataSourceAtIndex(obj,sourceObj,sourceName,dataFile,index)
            validateattributes(sourceObj,{'ioplayback.System'},{'nonempty'},'','sourceObj');
            validateattributes(sourceName,{'char'},{'nonempty','row'},'','sourceName');
            validateattributes(dataFile,{'char'},{'nonempty','row'},'','dataFile');
            
            newSrcObj = clone(sourceObj);
            dataFile = ioplayback.util.resolveFile(dataFile);
            %checkDataFile(newSrcObj,dataFile);
            if isa(newSrcObj,'ioplayback.SourceSystem')
                newSrcObj.SimulationOutput = 'From recorded file';
            else
                newSrcObj.SendSimulationInputTo = 'Data file';
            end
            newSrcObj.SourceName = sourceName;
            newSrcObj.DataFile   = dataFile;
            
            obj.Info_.Source{index} = newSrcObj;
        end
    end
    
    methods (Access = protected)
        function allSourceNames = getAllSourceNames(obj)
            allSourceNames = {};
            for k = 1:numel(obj.Info_.Source)
                allSourceNames{k} = obj.Info_.Source{k}.SourceName; %#ok<AGROW>
            end
        end
        
        function displayScalarObject(obj)
            header = getHeader(obj);
            disp(header);
            % Display main options
            fprintf('              Filename: %-30s\n',obj.Filename);
            fprintf('              Hardware: %-30s\n',obj.Hardware);
            fprintf('           Description: %-30s\n',obj.Description);
            fprintf('                  Tags: %-30s\n',ioplayback.util.cell2str(obj.Tags));
            fprintf('                  Date: %-30s\n',obj.Date);
            fprintf('               Sources: %-30s\n',ioplayback.util.cell2str(obj.Sources));
            fprintf('\n');
            fprintf(' - Use getDataSource to get a handle to a recorded source object.\n')
            fprintf(' - Use getData to access data associated with a source.\n');
            fprintf('\n%s\n',getFooter(obj));
        end
        
        function uncompressDataset(obj)
            obj.UncompressLocation_ = tempname;
            try
                untar(obj.ResolvedFilename_,obj.UncompressLocation_);
            catch
                error(message('ioplayback:utils:DatasetOpen'));
            end
        end
        
        function s = getFooter(obj)
            mc = metaclass(obj);
            s = sprintf('\n  <a href="matlab: methods(''%s'')">Methods</a>, ', mc.Name);
            s = [s, sprintf('<a href="matlab: superclasses(''%s'')">Superclasses</a>\n', mc.Name)];
        end
    end
    
    methods (Access = private,Static)
        function info = loadInfo(infoFile)
            try
                data = load(infoFile);
                info = data.info;
            catch
                error(message('ioplayback:utils:CorruptedDataset'));
            end
        end
    end
end

%% Local functions
function info = createFileInfo(fileName,uncompressLocation)
info.Dir = dir(fileName);
info.UncompressLocation = uncompressLocation;
end
%[EOF]

