function varargout = openNEV(varargin)

% openNEV
%
% Opens an .nev file for reading, returns all file information in a NEV
% structure. Works with File Spec 2.1 & 2.2 & 2.3.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Use OUTPUT = openNEV(fname, 'noread', 'report', 'noparse', 'nowarning',
%                             'nosave', 'nomat', 'uV', 'overwrite').
%
% NOTE: All input arguments are optional. Input arguments may be in any order.
%
%   fname:        Name of the file to be opened. If the fname is omitted
%                 the user will be prompted to select a file using an open
%                 file user interface.
%                 DEFAULT: Will open Open File UI.
%
%   'noread':     Will not read the spike waveforms if user passes this argument.
%                 DEFAULT: will read spike waveform.
%
%   'report':     Will show a summary report if user passes this argument.
%                 DEFAULT: will not show report.
%
%   'parse':    The code will not parse the experimental parameters in digital I/O.
%                 See below for guidelines on how to format your parameters.
%                 DEFAULT: will not parse the parameters.
%
%   'nowarning':  The code will not give a warning if there is an error in
%                 parsing.
%                 DEFAULT: will give warning message.
%
%   'nosave':     The code will not save a copy of the NEV structure as a
%                 MAT file. By default the code will save a copy in the same
%                 folder as the NEV file for easy future access.
%                 DEFAULT: will save the MAT file.
%
%   'nomat':      Will not look for a MAT file. This option will force
%                 openNEV to open a NEV file instead of any available MAT
%                 files.
%                 DEFAULT: will load the MAT file if available.
%
%   'uV':         Will read the spike waveforms in unit of uV instead of
%                 raw values. Note that this conversion may lead to loss of
%                 information (e.g. 15/4 = 4) since the waveforms type will
%                 stay in int16. It's recommended to read raw spike
%                 waveforms and then perform the conversion at a later
%                 time.
%                 DEFAULT: will read waveform information in raw.
%
%   '8bits':      Indicates that 8 bits on the digital IO port was used
%                 instead of 16 bits.
%                 DEFAULT: will assumes that 16 bits of digital IO were used.
%
%   't:':         Indicats the time window of the NEV file to be read. For
%                 example, if t: is set to 2 (i.e. 't:0.6')
%                 then only the first 2 seconds of the file is to be read. If set
%                 to 2-50 (i.e. 't:2:50) then the time between 2 seconds
%                 and 50 seconds will be read.
%                 DEFAULT: the entire file will be read if 't:xx:xx' is not
%                 passed to the function.
%
%   'overwrite':  If MATLAB loads a NEV file using 'nomat' and a MAT file
%                 already exists, by default it will prompt the user to
%                 allow for overwriting the old MAT. Passing the
%                 'overwrite' flag will automatically overwrite the newly
%                 opened NEV file ont the old MAT file.
%                 DEFAULT: will ask the user whether to overwrite the old
%                 MAT.
%
%   OUTPUT:       Contains the NEV structure.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   USAGE EXAMPLE:
%
%   openNEV('report','read');
%
%   In the example above, the file dialogue will prompt for a file. A
%   report of the file contents will be shown. The digital data will not be
%   parsed. The data needs to be in the proper format (refer below). The
%   spike waveforms are in raw units and not in uV.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   DIGITAL PARAMETERS/MARKERS FORMAT:
%
%   In order for this function to parse your experimental parameters they
%   need to be in the following format:
%
%   *ParamLabel:Parameter1=value1;Parameter2=value2;Parameter3=value3;#
%
%   TWO EXAMPLES:
%   *ExpParameter:Intensity=1.02;Duration=400;Trials=1;PageSegment=14;#
%
%   *Stimulation:StimCount=5;Duration=10;#
%
%   In the first example, the parameter is of type "ExpParameter". The
%   parameters are, "Intensity, Duration, Trials, and PageSement." The
%   values of those parameters are, "1.02, 400, 1, and 14," respectively.
%   The second example is of type "Stimulation". The name of the parameters
%   are "StimCount" and "Duration" and the values are "5" and "10"
%   respectively.
%   -----------------------------------------------------------------------
%   It can also read single value markers that follow the following format.
%
%   *MarkerName=Value;#
%
%   EXAMPLES:  *WaitSeconds=10;# OR  *JuiceStatus=ON;#
%
%   The above line is a "Marker". The marker value is 10 in the first
%   and it's ON in the second example.
%   -----------------------------------------------------------------------
%   Moreover, the marker could be a single value:
%
%   *MarkerValue#
%
%   EXAMPLES: *JuiceOff#  OR  *HandsOnSwitches#
%   -----------------------------------------------------------------------
%   The label, parameter name, and values are flexible and can be anything.
%   The only required formatting is that the user needs to have a label
%   followed by a colon ':', followed by a field name 'MarkerVal', followed
%   by an equal sign '=', followed by the parameter value '10', and end
%   with a semi-colon ';'.
%
%   NOTE:
%   Every line requires a pound-sign '#' at the very end.
%   Every line requires a star sign '*' at the very beginning. If you
%   use LabVIEW SendtoCerebus.vi by Kian Torab then there is no need for
%   a '*' in the beginning.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Kian Torab
%   kian@blackrockmicro.com
%   Blackrock Microsystems
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Version History
%
% 4.4.0.0:
%   - Major performance boost in reading NEV files when tracking data is
%   stored in the file.
%
% 4.4.0.2:
%   - Updated documentation.
%
% 4.4.0.3: 5 January 2014
%   - Fixed the way DayOfWeek is read in MetaTags.
%   - Fixed 'noread' argument, so when passed, openNEV will not read the
%   spike waveforms.
%
% 4.4.1.0: 25 January 2014
%   - Fixed a bug that resulted from passing 'read' to openNEV.
%
% 4.4.2.0: 28 February 2014
%   - Fixed bug related to loading data with t:XX:XX argument.
%
% 4.4.3.0: 12 June 2014
%   - Fixed a typo in the help.
%
% 4.4.3.1: 13 June 2014
%   - Updated the version numbers in the help and in the function itself.
%
% 4.4.3.2: 21 June 2014
%   - Fixed a bug where Application name wasn't being read properly.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Defining structures
NEV = struct('MetaTags',[], 'ElectrodesInfo', [], 'Data', []);
NEV.MetaTags = struct('Subject', [], 'Experimenter', [], 'DateTime', [],...
    'SampleRes',[],'Comment',[],'FileTypeID',[],'Flags',[], 'openNEVver', [], ...
    'DateTimeRaw', [], 'FileSpec', [], 'PacketBytes', [], 'HeaderOffset', [], ...
    'DataDuration', [], 'DataDurationSec', [], 'PacketCount', [], ...
    'TimeRes', [], 'Application', [], 'Filename', [], 'FilePath', []);
NEV.Data = struct('SerialDigitalIO', [], 'Spikes', [], 'Comments', [], 'VideoSync', [], ...
    'Tracking', [], 'TrackingEvents', [], 'PatientTrigger', [], 'Reconfig', []);
NEV.Data.Spikes = struct('TimeStamp', [],'Electrode', [],...
    'Unit', [],'Waveform', [], 'WaveformUnit', []);
NEV.Data.SerialDigitalIO = struct('InputType', [], 'TimeStamp', [],...
    'TimeStampSec', [], 'Type', [], 'Value', [], 'InsertionReason', [], 'UnparsedData', []);
NEV.Data.VideoSync = struct('TimeStamp', [], 'FileNumber', [], 'FrameNumber', [], 'ElapsedTime', [], 'SourceID', []);
NEV.Data.Comments = struct('TimeStamp', [], 'TimeStampSec', [], 'CharSet', [], 'Color', [], 'Text', []);
NEV.Data.Tracking = [];
NEV.Data.TrackingEvents = struct('TimeStamp', [], 'TimeStampSec', [], 'Text', []);
NEV.Data.PatientTrigger = struct('TimeStamp', [], 'TriggerType', []);
NEV.Data.Reconfig = struct('TimeStamp', [], 'ChangeType', [], 'CompName', [], 'ConfigChanged', []);
Flags = struct;

NEV.MetaTags.openNEVver = '4.4.3.1';

%% Check for multiple versions of openNEV in path
if size(which('openNEV', '-ALL'),1) > 1
    disp('WARNING: There are multiple openNEV functions in the path. Use which openNEV -ALL for more information.');
end

%% Validating input arguments
for i=1:length(varargin)
    switch lower(varargin{i})
        case 'report'
            Flags.Report = varargin{i};
        case 'noread'
            Flags.ReadData = varargin{i};
        case 'read'
            Flags.ReadData = varargin{i};
        case 'nosave'
            Flags.SaveFile = varargin{i};
        case 'nomat'
            Flags.NoMAT = varargin{i};
        case 'nowarning'
            Flags.WarningStat = varargin{i};
        case 'parse'
            Flags.ParseData = 'parse';
        case 'uv'
            Flags.waveformUnits = 'uV';
        case '8bits'
            Flags.digIOBits = '8bits';
        case 'overwrite'
            Flags.Overwrite = 'overwrite';
        otherwise
            temp = varargin{i};
            if length(temp)>3 && ...
                    (strcmpi(temp(3),'\') || ...
                     strcmpi(temp(1),'/') || ...
                     strcmpi(temp(2),'/'))
                fileFullPath = varargin{i};
                if exist(fileFullPath, 'file') ~= 2
                    disp('The file does not exist.');
                    varargout{1} = [];
                    return;
                end
            elseif length(temp)>3 && strcmpi(temp(1:2),'t:') && ~strcmpi(temp(3), '\') && ~strcmpi(temp(3), '/')
                temp(1:2) = [];
                temp = str2num(temp);
                if length(temp) == 1
                    fprintf('Only one timepoint (%0.0f) was passed to the function.\n', temp);
                    fprintf('The initial timepoint is set to 0, so data between 0 and %0.0f will be read.\n', temp);
                    temp(2) = temp;
                    temp(1) = 0;
                end
                readTime = [temp(1), temp(end)];
                Flags.SaveFile = 'nosave';
                Flags.NoMAT = 'nomat';
            else
                if ~isnumeric(varargin{i})
                    disp(['Invalid argument ''' varargin{i} ''' .']);
                else
                    disp(['Invalid argument ''' num2str(varargin{i}) ''' .']);
                end
                clear variables;
                if nargout
                    varargout{1} = [];
                end
                return;
            end
            clear temp;
    end
end; clear i;

%% Defining and validating variables
if ~exist('fileFullPath', 'var')
    if exist('getFile.m', 'file') == 2
        [fileName pathName] = getFile('*.nev', 'Choose a NEV file...');
    else
        [fileName pathName] = uigetfile;
    end
    fileFullPath = [pathName fileName];
    if fileFullPath==0;
        clear variables;
        if nargout
            varargout{1} = [];
        end
        disp('No file was selected.');
        return
    end
end

if ~isfield(Flags, 'Report');        Flags.Report = 'noreport'; end
if ~isfield(Flags, 'WarningStat');   Flags.WarningStat = 'warning'; end;
if ~isfield(Flags, 'ReadData');      Flags.ReadData = 'read'; end
if ~isfield(Flags, 'ParseData');     Flags.ParseData = 'noparse'; end
if ~isfield(Flags, 'SaveFile');      Flags.SaveFile = 'save'; end;
if ~isfield(Flags, 'NoMAT');         Flags.NoMAT = 'yesmat'; end;
if ~isfield(Flags, 'waveformUnits'); Flags.waveformUnits = 'raw'; end;
if ~isfield(Flags, 'digIOBits');     Flags.digIOBits = '16bits'; end;
if ~isfield(Flags, 'Overwrite');     Flags.Overwrite = 'noOverwrite'; end;
if strcmpi(Flags.Report, 'report')
    disp(['openNEV ' NEV.MetaTags.openNEVver]);
end
%%  Validating existance of parseCommand
if strcmpi(Flags.ParseData, 'parse')
    if exist('parseCommand.m', 'file') ~= 2
        disp('This version of openNEV requires function parseCommand.m to be placed in path.');
        clear variables;
        if nargout
            varargout{1} = [];
        end
        return;
    end
end

tic;
matPath = [fileFullPath(1:end-4) '.mat'];

%% Check for a MAT file and load that instead of NEV
if exist(matPath, 'file') == 2 && strcmpi(Flags.NoMAT, 'yesmat')
    disp('MAT file corresponding to selected NEV file already exists. Loading MAT instead...');
    load(matPath);
    if isempty(NEV.Data.Spikes.Waveform) && strcmpi(Flags.ReadData, 'read')
        disp('The MAT file does not contain waveforms. Loading NEV instead...');
    else
        if ~nargout
            assignin('base', 'NEV', NEV);
            clear variables;
        else
            varargout{1} = NEV;
        end
        return;
    end
end

%% Reading BasicHeader information from file
FID                       = fopen(fileFullPath, 'r', 'ieee-le');
BasicHeader               = fread(FID, 336, '*uint8');
NEV.MetaTags.FileTypeID   = char(BasicHeader(1:8)');
NEV.MetaTags.FileSpec     = [num2str(double(BasicHeader(9))) '.' num2str(double(BasicHeader(10)))];
NEV.MetaTags.Flags        = dec2bin(double(typecast(BasicHeader(11:12), 'uint16')),16);
Trackers.fExtendedHeader  = double(typecast(BasicHeader(13:16), 'uint32'));
NEV.MetaTags.HeaderOffset = Trackers.fExtendedHeader;
Trackers.countPacketBytes = double(typecast(BasicHeader(17:20), 'uint32'));
NEV.MetaTags.PacketBytes  = Trackers.countPacketBytes;
NEV.MetaTags.TimeRes      = double(typecast(BasicHeader(21:24), 'uint32'));
NEV.MetaTags.SampleRes    = typecast(BasicHeader(25:28), 'uint32');
t                         = double(typecast(BasicHeader(29:44), 'uint16'));
tempApp                   = BasicHeader(45:76)';
tempApp(find(tempApp == 0):end) = [];
NEV.MetaTags.Application  = char(tempApp); clear tempApp;
NEV.MetaTags.Comment      = char(BasicHeader(77:332)');
[NEV.MetaTags.FilePath, NEV.MetaTags.Filename] = fileparts(fileFullPath);
Trackers.countExtHeader   = typecast(BasicHeader(333:336), 'uint32');
clear BasicHeader;

if strcmpi(NEV.MetaTags.FileTypeID, 'NEURALEV')
    if exist([fileFullPath(1:end-8) '.sif'], 'file') == 2
        METATAGS = textread([fileFullPath(1:end-8) '.sif'], '%s');
        NEV.MetaTags.Subject      = METATAGS{3}(5:end-5);
        NEV.MetaTags.Experimenter = [METATAGS{5}(8:end-8) ' ' METATAGS{6}(7:end-7)];
    end
end
if ~any(strcmpi(NEV.MetaTags.FileSpec, {'2.1', '2.2', '2.3'}))
    disp('Unknown filespec. Cannot open file.');
    fclose FID;
    clear variables;
    if nargout
        varargout{1} = [];
    end
    return;
end
clear fileFullPath;

%% Parsing and validating FileSpec and DateTime variables
NEV.MetaTags.DateTimeRaw = t.';
NEV.MetaTags.DateTime = [num2str(t(2)) '/'  num2str(t(4)+2) '/' num2str(t(1))...
    ' ' datestr(t(3), 'dddd') ' ' num2str(t(5)) ':'  ...
    num2str(t(6)) ':'  num2str(t(7)) '.' num2str(t(8))] ;
clear t;

%% Removing extra garbage characters from the Comment field.
NEV.MetaTags.Comment(find(NEV.MetaTags.Comment==0,1):end) = 0;

%% Recording after BasicHeader file position
Trackers.fBasicHeader = ftell(FID); %#ok<NASGU>

% Calculating the length of the data
currentLocation = ftell(FID);
fseek(FID, -Trackers.countPacketBytes, 'eof');
NEV.MetaTags.DataDuration = fread(FID, 1, 'uint32=>double');
NEV.MetaTags.DataDurationSec = double(NEV.MetaTags.DataDuration) / double(NEV.MetaTags.SampleRes);
fseek(FID, currentLocation, 'bof');

%% Reading ExtendedHeader information
for ii=1:Trackers.countExtHeader
    ExtendedHeader = fread(FID, 32, '*uint8');
    PacketID = char(ExtendedHeader(1:8)');
    switch PacketID
        case 'ARRAYNME'
            NEV.ArrayInfo.ElectrodeName    = char(ExtendedHeader(9:end));
        case 'ECOMMENT'
            NEV.ArrayInfo.ArrayComment     = char(ExtendedHeader(9:end));
        case 'CCOMMENT'
            NEV.ArrayInfo.ArrayCommentCont = char(ExtendedHeader(9:end));
        case 'MAPFILE'
            NEV.ArrayInfo.MapFile          = char(ExtendedHeader(9:end));
        case 'NEUEVWAV'
            ElectrodeID                       = typecast(ExtendedHeader(9:10), 'uint16');
            NEV.ElectrodesInfo(ElectrodeID).ElectrodeID     = ElectrodeID;
            NEV.ElectrodesInfo(ElectrodeID).ConnectorBank   = char(ExtendedHeader(11)+64);
            NEV.ElectrodesInfo(ElectrodeID).ConnectorPin    = ExtendedHeader(12);
            df   = typecast(ExtendedHeader(13:14),'int16');
            % This is a workaround for the DigitalFactor overflow in NEV
            % files. Remove once Central is updated
            if df == 21516
                NEV.ElectrodesInfo(ElectrodeID).DigitalFactor = 152592.547;
            else
                NEV.ElectrodesInfo(ElectrodeID).DigitalFactor = df;
            end
            % End of workaround
            NEV.ElectrodesInfo(ElectrodeID).EnergyThreshold = typecast(ExtendedHeader(15:16),'uint16');
            NEV.ElectrodesInfo(ElectrodeID).HighThreshold   = typecast(ExtendedHeader(17:18),'int16');
            NEV.ElectrodesInfo(ElectrodeID).LowThreshold    = typecast(ExtendedHeader(19:20),'int16');
            NEV.ElectrodesInfo(ElectrodeID).Units           = ExtendedHeader(21);
            NEV.ElectrodesInfo(ElectrodeID).WaveformBytes   = ExtendedHeader(22);
            clear ElectrodeID;
        case 'NEUEVLBL'
            ElectrodeID                       = typecast(ExtendedHeader(9:10), 'uint16');
            NEV.ElectrodesInfo(ElectrodeID).ElectrodeLabel = char(ExtendedHeader(11:26));
            clear ElectrodeID;
        case 'NEUEVFLT'
            ElectrodeID                       = typecast(ExtendedHeader(9:10), 'uint16');
            NEV.ElectrodesInfo(ElectrodeID).HighFreqCorner = typecast(ExtendedHeader(11:14),'uint32');
            NEV.ElectrodesInfo(ElectrodeID).HighFreqOrder  = typecast(ExtendedHeader(15:18),'uint32');
            NEV.ElectrodesInfo(ElectrodeID).HighFilterType = typecast(ExtendedHeader(19:20),'uint16');
            NEV.ElectrodesInfo(ElectrodeID).LowFreqCorner  = typecast(ExtendedHeader(21:24),'uint32');
            NEV.ElectrodesInfo(ElectrodeID).LowFreqOrder   = typecast(ExtendedHeader(25:28),'uint32');
            NEV.ElectrodesInfo(ElectrodeID).LowFilterType  = typecast(ExtendedHeader(29:30),'uint16');
            clear ElectrodeID;
        case 'DIGLABEL'
            Mode                    = ExtendedHeader(25);
            NEV.IOLabels{Mode+1}    = char(ExtendedHeader(9:24).');
            clear Mode;
        case 'NSASEXEV' %% Not implemented in the Cerebus firmware.
                        %% Needs to be updated once implemented into the
                        %% firmware by Blackrock Microsystems.
            NEV.NSAS.Freq          = typecast(ExtendedHeader(9:10),'uint16');
            NEV.NSAS.DigInputConf  = char(ExtendedHeader(11));
            NEV.NSAS.AnalCh1Conf   = char(ExtendedHeader(12));
            NEV.NSAS.AnalCh1Detect = typecast(ExtendedHeader(13:14),'uint16');
            NEV.NSAS.AnalCh2Conf   = char(ExtendedHeader(15));
            NEV.NSAS.AnalCh2Detect = typecast(ExtendedHeader(16:17),'uint16');
            NEV.NSAS.AnalCh3Conf   = char(ExtendedHeader(18));
            NEV.NSAS.AnalCh3Detect = typecast(ExtendedHeader(19:20),'uint16');
            NEV.NSAS.AnalCh4Conf   = char(ExtendedHeader(21));
            NEV.NSAS.AnalCh4Detect = typecast(ExtendedHeader(22:23),'uint16');
            NEV.NSAS.AnalCh5Conf   = char(ExtendedHeader(24));
            NEV.NSAS.AnalCh5Detect = typecast(ExtendedHeader(25:26),'uint16');
        case 'VIDEOSYN'
            cnt = 1;
            if (isfield(NEV, 'VideoSyncInfo'))
                cnt = size(NEV.VideoSyncInfo, 2) + 1;
            end
            NEV.VideoSyncInfo(cnt).SourceID     = typecast(ExtendedHeader(9:10),'uint16');
            NEV.VideoSyncInfo(cnt).SourceName   = char(ExtendedHeader(11:26))';
            NEV.VideoSyncInfo(cnt).FrameRateFPS = typecast(ExtendedHeader(27:30),'single')';
            clear cnt;
        case 'TRACKOBJ'
            cnt = 1;
            if (isfield(NEV, 'ObjTrackInfo'))
                cnt = size(NEV.ObjTrackInfo, 2) + 1;
            end
            NEV.ObjTrackInfo(cnt).TrackableType = typecast(ExtendedHeader(9:10),'uint16');
            NEV.ObjTrackInfo(cnt).TrackableID   = typecast(ExtendedHeader(11:14), 'uint32');
            NEV.ObjTrackInfo(cnt).TrackableName = char(ExtendedHeader(15:30))';
            clear cnt;
        otherwise
            disp(['PacketID ' PacketID ' is invalid.']);
            disp('Please make sure this version of openNEV is compatible with your current NSP firmware.')
            fclose(FID);
            clear variables;
            if nargout
                varargout{1} = [];
            end
            return;
    end
end
NEV.MetaTags.ChannelID = [NEV.ElectrodesInfo.ElectrodeID];
clear ExtendedHeader PacketID ii;

%% Recording after ExtendedHeader file position and calculating Data Length
%  and number of data packets
fseek(FID, 0, 'eof');
Trackers.fData = ftell(FID);
Trackers.countDataPacket = (Trackers.fData - Trackers.fExtendedHeader)/Trackers.countPacketBytes;
NEV.MetaTags.PacketCount = Trackers.countDataPacket;

%%
Flags.UnparsedDigitalData = 0;

%% Reading packet headers and digital values
Timestamp = [];
PacketIDs = [];
tempClassOrReason = [];
tempDigiVals = [];
if NEV.MetaTags.PacketCount ~= 0
    fseek(FID, Trackers.fExtendedHeader, 'bof');
    tRawData  = fread(FID, [10 Trackers.countDataPacket], '10*uint8=>uint8', Trackers.countPacketBytes - 10);
    Timestamp = tRawData(1:4,:);
    Timestamp = typecast(Timestamp(:), 'uint32').';

    %% Calculate the number of packets that need to be read based on the time input parameters
    if ~exist('readTime', 'var')
        Trackers.readPackets = [1, length(Timestamp)];
    else
        [tmp,tempReadPackets] = find(Timestamp > readTime(1)*NEV.MetaTags.SampleRes,1,'first');
        if ~isempty(tempReadPackets)
            Trackers.readPackets(1) = tempReadPackets;
        else
            Trackers.readPackets(1) = NaN;
        end
        if isnan(Trackers.readPackets(1))
            fprintf('The file contains %0.2f seconds of data. The requested begining timestamp of %0.2f seconds is longer than the duration.\n', ...
                double(Timestamp(end))/double(NEV.MetaTags.SampleRes), ...
                readTime(1));
            clear variables;
            if nargout
                varargout{1} = [];
            end
            return;
        end
        [tmp,tempReadPackets] = find(Timestamp < readTime(2)*NEV.MetaTags.SampleRes,1,'last');
        if ~isempty(tempReadPackets)
            if readTime(2)*NEV.MetaTags.SampleRes > Timestamp(end)
                fprintf('The file contains %0.2f seconds of data. The requested end duration of %0.2f seconds will be adjusted to %0.2f seconds.\n', ...
                    double(Timestamp(end))/double(NEV.MetaTags.SampleRes), ...
                    readTime(2),...
                    double(Timestamp(end))/double(NEV.MetaTags.SampleRes));
            end
            Trackers.readPackets(2) = tempReadPackets;
        else
            Trackers.readPackets(2) = NaN;
        end
        clear tmp, tempReadPackets;
    end

    PacketIDs = tRawData(5:6,Trackers.readPackets(1):Trackers.readPackets(2));
    PacketIDs = typecast(PacketIDs(:), 'uint16').';
    tempClassOrReason = uint8(tRawData(7,Trackers.readPackets(1):Trackers.readPackets(2)));
    if strcmpi(Flags.digIOBits, '16bits')
        tempDigiVals      = tRawData(9:10,Trackers.readPackets(1):Trackers.readPackets(2));
        tempDigiVals      = typecast(tempDigiVals(:), 'uint16');
    else
        tempDigiVals      = uint16(tRawData(9,Trackers.readPackets(1):Trackers.readPackets(2)));
    end
    clear tRawData;
else
    Trackers.readPackets = zeros(1,2);
end

%% Defining PacketID constants
digserPacketID = 0;
neuralIndicesPacketIDBounds = [1, 16384];
commentPacketID = 65535;
videoSyncPacketID = 65534;
trackingPacketID = 65533;
patientTrigPacketID = 65532;
reconfigPacketID = 65531;

%% Parse read digital data. Please refer to help to learn about the proper
% formatting if the data.
digserIndices              = find(PacketIDs == digserPacketID);
neuralIndices              = find(neuralIndicesPacketIDBounds(2) >= PacketIDs & PacketIDs >= neuralIndicesPacketIDBounds(1));
commentIndices             = find(PacketIDs == commentPacketID);
videoSyncPacketIDIndices   = find(PacketIDs == videoSyncPacketID);
trackingPacketIDIndices    = find(PacketIDs == trackingPacketID);
patientTrigPacketIDIndices = find(PacketIDs == patientTrigPacketID);
reconfigPacketIDIndices    = find(PacketIDs == reconfigPacketID);
clear digserPacketID neuralIndicesPacketIDBounds commentPacketID ...
      videoSyncPacketID trackingPacketID patientTrigPacketID reconfigPacketID;
digserTimestamp            = Timestamp(digserIndices);
NEV.Data.Spikes.TimeStamp  = Timestamp(neuralIndices);
NEV.Data.Spikes.Electrode  = PacketIDs(neuralIndices);
clear PacketIDs;
NEV.Data.Spikes.Unit       = tempClassOrReason(neuralIndices);
%clear neuralIndices;
NEV.Data.SerialDigitalIO.InsertionReason   = tempClassOrReason(digserIndices);
clear tempClassOrReason;
DigiValues                 = tempDigiVals(digserIndices);
clear tempDigiVals;

%% Reads the waveforms if 'read' is passed to the function
if strcmpi(Flags.ReadData, 'read')
    allExtraDataPacketIndices  = [commentIndices, ...
                                  videoSyncPacketIDIndices, ...
                                  trackingPacketIDIndices, ...
                                  patientTrigPacketIDIndices, ...
                                  reconfigPacketIDIndices];

    if ~isempty(allExtraDataPacketIndices) % if there is any extra packets
        fseek(FID, Trackers.fExtendedHeader, 'bof');
        fseek(FID, (Trackers.readPackets(1)-1) * Trackers.countPacketBytes, 'cof');
        tRawData  = fread(FID, [Trackers.countPacketBytes Trackers.readPackets(2)], ...
            [num2str(Trackers.countPacketBytes) '*uint8=>uint8'], 0);
        if ~isempty(commentIndices)
            NEV.Data.Comments.TimeStamp = Timestamp(commentIndices);
            NEV.Data.Comments.TimeStampSec = double(NEV.Data.Comments.TimeStamp)/double(NEV.MetaTags.TimeRes);
            NEV.Data.Comments.CharSet = tRawData(7, commentIndices);
            NEV.Data.Comments.Color = tRawData(9:12, commentIndices);
            NEV.Data.Comments.Color = typecast(NEV.Data.Comments.Color(:), 'uint32').';
            NEV.Data.Comments.Text  = char(tRawData(13:Trackers.countPacketBytes, commentIndices).');

            % Transferring NeuroMotive Events to its own structure
            neuroMotiveEvents = find(NEV.Data.Comments.CharSet == 255);
            NEV.Data.TrackingEvents.TimeStamp = NEV.Data.Comments.TimeStamp(neuroMotiveEvents);
            NEV.Data.TrackingEvents.TimeStampSec = double(NEV.Data.TrackingEvents.TimeStamp)/double(NEV.MetaTags.TimeRes);
            NEV.Data.TrackingEvents.Text = NEV.Data.Comments.Text(neuroMotiveEvents,:);
            NEV.Data.Comments.TimeStamp(neuroMotiveEvents) = [];
            NEV.Data.Comments.TimeStampSec(neuroMotiveEvents) = [];
            NEV.Data.Comments.CharSet(neuroMotiveEvents) = [];
            NEV.Data.Comments.Color(neuroMotiveEvents) = [];
            NEV.Data.Comments.Text(neuroMotiveEvents) = [];

            clear commentIndices;
        end
        if ~isempty(videoSyncPacketIDIndices)
            NEV.Data.VideoSync.TimeStamp       = Timestamp(videoSyncPacketIDIndices);
            NEV.Data.VideoSync.FileNumber      = tRawData(7:8, videoSyncPacketIDIndices);
            NEV.Data.VideoSync.FileNumber      = typecast(NEV.Data.VideoSync.FileNumber(:), 'uint16').';
            NEV.Data.VideoSync.FrameNumber     = tRawData(9:12, videoSyncPacketIDIndices);
            NEV.Data.VideoSync.FrameNumber     = typecast(NEV.Data.VideoSync.FrameNumber(:), 'uint32').';
            NEV.Data.VideoSync.ElapsedTime     = tRawData(13:16, videoSyncPacketIDIndices);
            NEV.Data.VideoSync.ElapsedTime     = typecast(NEV.Data.VideoSync.ElapsedTime(:), 'uint32').';
            NEV.Data.VideoSync.SourceID        = tRawData(17:20, videoSyncPacketIDIndices);
            NEV.Data.VideoSync.SourceID        = typecast(NEV.Data.VideoSync.SourceID(:), 'uint32').';
            clear videoSyncPacketIDIndices;
        end
        if ~isempty(trackingPacketIDIndices)
            tmp.TimeStamp     = Timestamp(trackingPacketIDIndices);
            tmp.TimeStampSec  = double(tmp.TimeStamp)/30000;
            % This portion is commented out because it does not contain any
            % information as of yet.
            tmp.ParentID      = tRawData(7:8, trackingPacketIDIndices);
            tmp.ParentID      = typecast(tmp.ParentID(:), 'uint16').';
            tmp.NodeID        = tRawData(9:10, trackingPacketIDIndices);
            tmp.NodeID        = typecast(tmp.NodeID(:), 'uint16').';
            tmp.NodeCount     = tRawData(11:12, trackingPacketIDIndices);
            tmp.NodeCount     = typecast(tmp.NodeCount(:), 'uint16').';
            tmp.MarkerCount   = tRawData(13:14, trackingPacketIDIndices);
            tmp.MarkerCount   = typecast(tmp.MarkerCount(:), 'uint16').';

            tmp.rigidBodyPoints = tRawData(15:102, trackingPacketIDIndices);
            tmp.rigidBodyPoints = reshape(typecast(tmp.rigidBodyPoints(:), 'uint16'), 44, length(tmp.ParentID));

            objectIndex = [0 '1' '2' '3' '4' '1' '2' '3' '4'];
            if (isfield(NEV, 'ObjTrackInfo'))
                for IDX = 1:size(NEV.ObjTrackInfo,2)
                    emptyChar = find(NEV.ObjTrackInfo(IDX).TrackableName == 0, 1);
                    NEV.ObjTrackInfo(IDX).TrackableName(emptyChar) = objectIndex(IDX);
                    indicesOfEvent = find(tmp.NodeID == IDX-1);
                    if ~isempty(indicesOfEvent)
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).TimeStamp = tmp.TimeStamp(indicesOfEvent);
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).TimeStampSec = tmp.TimeStampSec(indicesOfEvent);
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).ParentID =    tmp.ParentID(indicesOfEvent);
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).NodeCount =   tmp.NodeCount(indicesOfEvent);
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCount = tmp.MarkerCount(indicesOfEvent);
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCoordinates(size(NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).TimeStamp,2)).X = [];
                        NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCoordinates(size(NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).TimeStamp,2)).X = [];
                        for xyIDX = 1:size(NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).TimeStamp,2)
                            NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCoordinates(xyIDX).X = ...
                                tmp.rigidBodyPoints(1:2:NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCount(xyIDX)*2, indicesOfEvent(xyIDX));
                            NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCoordinates(xyIDX).Y = ...
                                tmp.rigidBodyPoints(2:2:NEV.Data.Tracking.(NEV.ObjTrackInfo(IDX).TrackableName).MarkerCount(xyIDX)*2, indicesOfEvent(xyIDX));
                        end
                    end
                end
            end
            clear trackingPacketIDIndices tmp;
        end
        if ~isempty(patientTrigPacketIDIndices)
            NEV.Data.PatientTrigger.TimeStamp    = Timestamp(patientTrigPacketIDIndices);
            NEV.Data.PatientTrigger.TriggerType  = tRawData(7:8, patientTrigPacketIDIndices);
            NEV.Data.PatientTrigger.TriggerType  = typecast(NEV.Data.PatientTrigger.TriggerType(:), 'uint16').';
            clear patientTrigPacketIDIndices;
        end
        if ~isempty(reconfigPacketIDIndices)
            NEV.Data.Reconfig.TimeStamp     = Timestamp(reconfigPacketIDIndices);
            NEV.Data.Reconfig.ChangeType    = tRawData(7:8, reconfigPacketIDIndices);
            NEV.Data.Reconfig.ChangeType    = typecast(NEV.Data.Reconfig.ChangeType(:), 'uint16').';
            NEV.Data.Reconfig.CompName      = char(tRawData(9:24, reconfigPacketIDIndices));
            NEV.Data.Reconfig.ConfigChanged = char(tRawData(25:Trackers.countPacketBytes, reconfigPacketIDIndices));
            clear reconfigPacketIDIndices;
        end
    end % end if ~isempty(allExtraDataPacketIndices)

    clear Timestamp tRawData count idx;

   % now read waveform
    fseek(FID, Trackers.fExtendedHeader + 8, 'bof'); % Seek to location of spikes
    fseek(FID, (Trackers.readPackets(1)-1) * Trackers.countPacketBytes, 'cof');
    NEV.Data.Spikes.WaveformUnit = Flags.waveformUnits;
    NEV.Data.Spikes.Waveform = fread(FID, [(Trackers.countPacketBytes-8)/2 Trackers.readPackets(2)], ...
        [num2str((Trackers.countPacketBytes-8)/2) '*int16=>int16'], 8);
    NEV.Data.Spikes.Waveform(:, [digserIndices allExtraDataPacketIndices]) = [];

    clear allExtraDataPacketIndices;
    if strcmpi(Flags.waveformUnits, 'uv')
        elecDigiFactors = double(1000./[NEV.ElectrodesInfo(NEV.Data.Spikes.Electrode).DigitalFactor]);
        NEV.Data.Spikes.Waveform = bsxfun(@rdivide, double(NEV.Data.Spikes.Waveform), elecDigiFactors);
        if strcmpi(Flags.WarningStat, 'warning')
            fprintf(1,'\nThe spike waveforms are in unit of uV.\n');
            fprintf(2,'WARNING: This conversion may lead to loss of information.');
            fprintf(1,'\nRefer to help for more information.\n');
        end
    end
end
clear digserIndices;

%% Parse digital data if requested
if ~isempty(DigiValues)
    if strcmpi(Flags.ParseData, 'parse')
        try
            DigiValues = char(DigiValues);
            Inputs                     = {'Digital'; 'AnCh1'; 'AnCh2'; 'AnCh3'; 'AnCh4'; 'AnCh5'; 'PerSamp'; 'Serial'};
            AsteriskIndices   = find(DigiValues == '*');
            DataBegTimestamp = digserTimestamp(AsteriskIndices);
            splitDigiValues = regexp(DigiValues(2:end), '*', 'split')';
            for idx = 1:length(splitDigiValues)
                try
                    if isempty(find(splitDigiValues{idx} == ':', 1))
                        splitDigiValues{idx}(find(splitDigiValues{idx} == '#')) = [];
                        NEV.Data.SerialDigitalIO(idx).Value = splitDigiValues{idx};
                        NEV.Data.SerialDigitalIO(idx).Type = 'Marker';
                    else
                        [tempParsedCommand error] = parseCommand(splitDigiValues{idx});
                        if ~error
                            pcFields = fields(tempParsedCommand);
                            NEV.Data.SerialDigitalIO(idx).Value = splitDigiValues{idx};
                            for fidx = 1:length(pcFields)
                                NEV.Data.SerialDigitalIO(idx).(pcFields{fidx}) = tempParsedCommand.(pcFields{fidx});
                            end
                        else
                            NEV.Data.SerialDigitalIO(idx).Value = splitDigiValues{idx};
                            NEV.Data.SerialDigitalIO(idx).Type = 'UnparsedData';
                            Flags.UnparsedDigitalData = 1;
                        end
                    end
                catch
                    disp(['Error parsing: ' splitDigiValues{idx}]);
                    disp('Please refer to the help for more information on how to properly format the digital data for parsing.');
                end
            end
            % Populate the NEV structure with Timestamp and inputtypes for the
            % digital data
            if ~isempty(DataBegTimestamp)
                c = num2cell(DataBegTimestamp); [NEV.Data.SerialDigitalIO(1:length(NEV.Data.SerialDigitalIO)).TimeStamp] = deal(c{1:end});
                c = num2cell(DataBegTimestamp/NEV.MetaTags.SampleRes); [NEV.Data.SerialDigitalIO.TimeStampSec] = deal(c{1:end});
                c = {Inputs{NEV.Data.SerialDigitalIO.InsertionReason(AsteriskIndices)}}; [NEV.Data.SerialDigitalIO.InputType] = deal(c{1:end});
            end
            clear Inputs DigiValues digserTimestamp;
        catch
            disp(lasterr);
            disp('An error occured during reading digital data. This is due to a problem with formatting digital data.');
            disp('Refer to help ''help openNEV'' for more information on how to properly format the digital data.');
            disp('Try using openNEV with ''noparse'', i.e. openNEV(''noparse'').');
        end
    else
        NEV.Data.SerialDigitalIO.TimeStamp = digserTimestamp;
        NEV.Data.SerialDigitalIO.TimeStampSec = double(digserTimestamp)/30000;
        clear digserTimestamp;
        NEV.Data.SerialDigitalIO.UnparsedData = DigiValues;
        clear DigiValues;
    end
else
    if strcmpi(Flags.ReadData, 'read')
        if strcmpi(Flags.Report, 'report')
            disp('No digital data to read.');
        end
    end
end

if strcmpi(Flags.ParseData, 'parse')
    if Flags.UnparsedDigitalData && strcmpi(Flags.WarningStat, 'warning')
        fprintf(2, 'WARNING: The NEV file contains unparsed digital data.\n');
    end
end

%% Show a report if 'report' is passed as an argument
if strcmpi(Flags.Report, 'report')
    % Displaying report
    disp( '*** FILE INFO **************************');
    disp(['File Name           = ' NEV.MetaTags.Filename]);
    disp(['Filespec            = ' NEV.MetaTags.FileSpec]);
    disp(['Data Duration (min) = ' num2str(round(NEV.MetaTags.DataDuration/NEV.MetaTags.SampleRes/60))]);
    disp(['Packet Counts       = ' num2str(Trackers.countDataPacket)]);
    disp(' ');
    disp( '*** BASIC HEADER ***********************');
    disp(['Sample Resolution   = ' num2str(NEV.MetaTags.SampleRes)]);
    disp(['Date and Time       = '         NEV.MetaTags.DateTime]);
    disp(['Comment             = '         NEV.MetaTags.Comment(1:64)   ]);
    disp(['                      '         NEV.MetaTags.Comment(65:128) ]);
    disp(['                      '         NEV.MetaTags.Comment(129:192)]);
    disp(['                      '         NEV.MetaTags.Comment(193:256)]);
    disp(['The load time was for NEV file was ' num2str(toc, '%0.1f') ' seconds.']);
end

%% Saving the NEV structure as a MAT file for easy access
if strcmpi(Flags.SaveFile, 'save')
    if exist(matPath, 'file') == 2 && strcmpi(Flags.Overwrite, 'nooverwrite')
        disp(['File ' matPath ' already exists.']);
        overWrite = input('Would you like to overwrite (Y/N)? ', 's');
        if strcmpi(overWrite, 'y')
            disp('Saving MAT file. This may take a few seconds...');
            save(matPath, 'NEV', '-v7.3');
        else
            disp('File was not overwritten.');
        end
    elseif exist(matPath, 'file') == 2 && strcmpi(Flags.Overwrite, 'overwrite')
        disp(['File ' matPath ' already exists.']);
        disp('Overwriting the old MAT file. This may take a few seconds...');
        save(matPath, 'NEV', '-v7.3');
    else
        disp('Saving MAT file. This may take a few seconds...');
        save(matPath, 'NEV', '-v7.3');
    end
    clear overWrite;
end

if ~nargout
    assignin('base', 'NEV', NEV);
else
    varargout{1} = NEV;
end

fclose(FID);
clear Flags Trackers FID matPath;


function DoUpdateInfo(Simuh,Info)
    string=get(Simuh.Info_listbox,'String');
    time=clock;
    string{end+1,1}=[num2str(time(4)) ':' num2str(time(5)) ':' num2str(round(time(6))) '>' Info];
    set(Simuh.Info_listbox,'String',string,'Value',length(string));
end


function varargout = gui_causalBrainMovie3D_online(varargin)
%
% GUI_CAUSALBRAINMOVIE3D_ONLINE M-file for gui_causalBrainMovie3D_online.fig
%      GUI_CAUSALBRAINMOVIE3D_ONLINE, by itself, creates a new GUI_CAUSALBRAINMOVIE3D_ONLINE or raises the existing
%      singleton*.
%
%      H = GUI_CAUSALBRAINMOVIE3D_ONLINE returns the handle to a new GUI_CAUSALBRAINMOVIE3D_ONLINE or the handle to
%      the existing singleton*.
%
%      GUI_CAUSALBRAINMOVIE3D_ONLINE('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in GUI_CAUSALBRAINMOVIE3D_ONLINE.M with the given input arguments.
%
%      GUI_CAUSALBRAINMOVIE3D_ONLINE('Property','Value',...) creates a new GUI_CAUSALBRAINMOVIE3D_ONLINE or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before gui_causalBrainMovie3D_online_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to gui_causalBrainMovie3D_online_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help gui_causalBrainMovie3D_online

% Last Modified by GUIDE v2.5 19-Feb-2013 00:02:42

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @gui_causalBrainMovie3D_online_OpeningFcn, ...
                   'gui_OutputFcn',  @gui_causalBrainMovie3D_online_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before gui_causalBrainMovie3D_online is made visible.
function gui_causalBrainMovie3D_online_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to gui_causalBrainMovie3D_online (see VARARGIN)

% Choose default command line output for gui_causalBrainMovie3D_online
handles.output = hObject;
handles.gcf    = hObject;
set(hObject,'Name','BrainMovie3D Control Panel');
% set default termination behavior
handles.ExitButtonClicked = 'Cancel';

% extract some data from command-line input
if isempty(varargin)
    error('You must pass ALLEEG and Conn to gui_CausalBrainMovie3D');
end

% Extract input parameters/data and store
handles.ud.ALLEEG  = varargin{1};
handles.ud.Conn    = varargin{2};
varargin([1 2]) = [];

% set default EEGLAB background and text colors
%-----------------------------------------------
try, icadefs;
catch,
	GUIBACKCOLOR        =  [.8 .8 .8];
	GUIPOPBUTTONCOLOR   = [.8 .8 .8];
	GUITEXTCOLOR        = [0 0 0];
end;

allhandlers = hObject;

hh = findobj(allhandlers,'style', 'text');
%set(hh, 'BackgroundColor', get(hObject, 'color'), 'horizontalalignment', 'left');
set(hh, 'Backgroundcolor', GUIBACKCOLOR);
set(hh, 'foregroundcolor', GUITEXTCOLOR);
set(hObject, 'color',GUIBACKCOLOR );
% set(hh, 'horizontalalignment', g.horizontalalignment);

hh = findobj(allhandlers, 'style', 'edit');
set(hh, 'BackgroundColor', [1 1 1]); %, 'horizontalalignment', 'right');

hh =findobj(allhandlers, 'parent', hObject, 'style', 'pushbutton');
if ~strcmpi(computer, 'MAC') && ~strcmpi(computer, 'MACI') % this puts the wrong background on macs
    set(hh, 'backgroundcolor', GUIPOPBUTTONCOLOR);
    set(hh, 'foregroundcolor', GUITEXTCOLOR);
end;
hh =findobj(allhandlers, 'parent', hObject, 'style', 'popupmenu');
set(hh, 'backgroundcolor', GUIPOPBUTTONCOLOR);
set(hh, 'foregroundcolor', GUITEXTCOLOR);
hh =findobj(allhandlers, 'parent', hObject, 'style', 'checkbox');
set(hh, 'backgroundcolor', GUIBACKCOLOR);
set(hh, 'foregroundcolor', GUITEXTCOLOR);
hh =findobj(allhandlers, 'parent', hObject, 'style', 'listbox');
set(hh, 'backgroundcolor', GUIPOPBUTTONCOLOR);
set(hh, 'foregroundcolor', GUITEXTCOLOR);
hh =findobj(allhandlers, 'parent', hObject, 'style', 'radio');
set(hh, 'foregroundcolor', GUITEXTCOLOR);
set(hh, 'backgroundcolor', GUIPOPBUTTONCOLOR);
set(hObject, 'visible', 'on');

set(handles.pnlPropertyGrid,'backgroundcolor', GUIBACKCOLOR);
set(handles.pnlPropertyGrid,'foregroundcolor', GUITEXTCOLOR);

%-----------------------------------------------

drawnow

% render the PropertyGrid in the correct panel
handles.PropertyGridHandle = arg_guipanel( ...
                 handles.pnlPropertyGrid, ...
                'Function',@vis_causalBrainMovie3D, ...
                'params',{'ALLEEG',handles.ud.ALLEEG, 'Conn',handles.ud.Conn, varargin{:}});

handles.BrainMovieFigure = [];
handles.figureHandle = hObject;

% Update handles structure
guidata(hObject, handles);

% Wait for user to click OK, Cancel or close figure
% uiwait(handles.gui_BrainMovie3D);

% UIWAIT makes gui_causalBrainMovie3D_online wait for user response (see UIRESUME)
% uiwait(handles.gui_BrainMovie3D);

% --- Outputs from this function are returned to the command line.
function varargout = gui_causalBrainMovie3D_online_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

varargout = {hObject};

% --- Executes on button press in cmdCancel.
function cmdCancel_Callback(hObject, eventdata, handles)

if isempty(handles)
    % user closed the figure
    varargout = {[] hObject};
% elseif strcmpi(handles.ExitButtonClicked,'OK')
%     % user clicked OK
%     % get PropertySpecification
%     varargout = {handles.PropertyGridHandle handles.output};
else
    % user clicked cancel
    varargout = {[] handles.output};
end

evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=false;');
% evalin('base','if ishandle(figHandles.BMDisplay), close(figHandles.BMDisplay); end');

try
    close(handles.figureHandle);
catch
end

% --- Executes on button press in cmdOK.
function cmdOK_Callback(hObject, eventdata, handles)

% handles.ExitButtonClicked ='OK';
% get the property specification
pg = handles.PropertyGridHandle;
ps = pg.GetPropertySpecification;
cfg = arg_tovals(ps,false);
assignin('base','BMCFG',cfg);
assignin('base','BM_CFG_CHANGED',true);
set(handles.gcf,'UserData','newBrainmovie');
% guidata(hObject,handles);
% uiresume(handles.gui_BrainMovie3D);

% % --- Executes on slider movement.
% function slideCurTime_Callback(hObject, eventdata, handles)
%
% % get the PropertyGrid object
% % ud = get(handles.gui_BrainMovie3D,'UserData');
% % if isempty(ud)
% %     error('Could not obtain handle to PropertyGrid! Did you pass it to the UserData of the figure?');
% % end
%
% % get the property specification
% pg = handles.PropertyGridHandle;
% ps = pg.GetPropertySpecification;
% cfg = arg_tovals(ps,false);
%
% if isempty(cfg)
%     error('could not retrieve property specification from propertygrid');
% end
%
% % get the index of the frame the user wants to render
% range = get(hObject,'max')-get(hObject,'min');
% curframe = round(get(hObject,'Value'));
%
% % update the curTime textbox
% set(handles.txtCurTime,'string',handles.ud.Conn(1).erWinCenterTimes(curframe));
%
% minTime = str2num(get(handles.txtMinTime,'String'));
% maxTime = str2num(get(handles.txtMaxTime,'String'));
%
% figh = handles.BrainMovieFigure; %findobj('tag','BrainMovieFigure');
% try figure(figh)
% catch
%     figh = [];
% end
% curpos = get(findobj(figh,'tag','brain'),'CameraPosition');
% % execute the brainmovie function to render this frame
% vis_causalBrainMovie3D('ALLEEG',handles.ud.ALLEEG,'Conn',handles.ud.Conn,cfg,'MovieTimeRange',[minTime maxTime],'BrainMovieOptions',{cfg.BMopts,'visible','off','FigureHandle',figh,'LatenciesToRender',[],'FramesToRender',curframe,'outputFormat',{'framefolder','','moviename',''}});
% figh = gcf;
% set(findobj(figh,'tag','brain'),'CameraPosition',curpos);
% set(gcf,'visible','on')
% handles.BrainMovieFigure = gcf;
% guidata(hObject,handles);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ADDITIONAL CALLBACKS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% % --- Executes during object creation, after setting all properties.
% function slideCurTime_CreateFcn(hObject, eventdata, handles)
%
%
% % Hint: slider controls usually have a light gray background.
% if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
%     set(hObject,'BackgroundColor',[.9 .9 .9]);
% end

% --- Executes during object deletion, before destroying properties.
function cmdOK_DeleteFcn(hObject, eventdata, handles)
% hObject    handle to cmdOK (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%
% % --- Executes on button press in cmdHelp.
% function cmdHelp_Callback(hObject, eventdata, handles)
% % hObject    handle to cmdHelp (see GCBO)
% % eventdata  reserved - to be defined in a future version of MATLAB
% % handles    structure with handles and user data (see GUIDATA)
%
% warndlg2('Coming soon!');

% --- Executes on key press with focus on gui_BrainMovie3D or any of its controls.
function gui_BrainMovie3D_WindowKeyPressFcn(hObject, eventdata, handles)
% hObject    handle to gui_BrainMovie3D (see GCBO)
% eventdata  structure with the following fields (see FIGURE)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)

% hObject    handle to gui_BrainMovie3D (see GCBO)
% eventdata  structure with the following fields (see FIGURE)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)

% if strcmpi(eventdata.Key,'s') ...
%         && (strcmpi(eventdata.Modifier,'control') ...
%         || strcmpi(eventdata.Modifier,'command'))
%
%
%     % get current contents of property grid
%     pg    = handles.PropertyGridHandle;
%     ps    = pg.GetPropertySpecification;
%     BMCFG = arg_tovals(ps,false);
%
%     % select the path/file for saving configs
%     [fname fpath] = uiputfile('*.mat','Save BrainMovie Config File');
%     if ~fname
%         return;
%     end
%     % save the opts structure
%     save(fullfile(fpath,fname),'BMCFG');
%
%
% %     varname = inputdlg2({'Enter name of variable in workspace to store configuration'}, ...
% %                         'Save GUI configuration to workspace',1,{''});
% %
% %     if isempty(varname)
% %         return;
% %     end
% %
% %     % get the property specification
% %     pg = handles.PropertyGridHandle;
% %     ps = pg.GetPropertySpecification;
% %     cfg = arg_tovals(ps,false);
% %
% %     % save configuration structure to workspace
% %     assignin('base',varname{1},cfg);
% end
%
% % load configuration
% if strcmpi(eventdata.Key,'l') ...
%         && (strcmpi(eventdata.Modifier,'control') ...
%         || strcmpi(eventdata.Modifier,'command'))
%
%     % load the configs
%     [fname fpath] = uigetfile('*.mat','Load BrainMovie Config File');
%     if ~fname
%         return;
%     end
%     tmp   = load(fullfile(fpath,fname));
%     fn    = fieldnames(tmp);
%     BMCFG = tmp.(fn{1});
%
%     % pause the brainmovie rendering
%     evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=false;');
%
%     % close existing brainmovie figure
%     evalin('base','close(figHandles.BMDisplay);  figHandles.BMDisplay = [];');
%     % update configs in base workspace
%     assignin('base','BMCFG',BMCFG);
%     assignin('base','BM_CFG_CHANGED',true);
%     assignin('base','newBrainmovie',true);
%     % close the BrainMovie control panel (a new one will be opened)
%     close(hObject);
%     % resume the brainmovie rendering
%     evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=true;');
%
% %     % pause the brainmovie rendering
% %     evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=false;');
% %     % redraw the property grids
% %     handles = redrawPropertyGrid(hObject,handles,BMCFG);
% %     % close existing brainmovie figure
% %     evalin('base','close(figHandles.BMDisplay);  figHandles.BMDisplay = [];');
% %     % update configs in base workspace
% %     assignin('base','BMCFG',BMCFG);
% %     assignin('base','BM_CFG_CHANGED',true);
% %     % resume the brainmovie rendering
% %     evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=true;');
%
%     guidata(hObject,handles);
% end

% --- User-defined function to (re)draw Property Grids
function handles = redrawPropertyGrid(hObject,handles,BMCFG)

try
    % delete existing property grids
    delete(handles.PropertyGridHandle.Control);
catch err
end

% render the PropertyGrid in the correct panel
handles.PropertyGridHandle = arg_guipanel( ...
                 handles.pnlPropertyGrid, ...
                'Function',@vis_causalBrainMovie3D, ...
                'params',{'ALLEEG',handles.ud.ALLEEG, 'Conn',handles.ud.Conn, BMCFG});

% --- Executes on button press in cmdPause.
function cmdPause_Callback(hObject, eventdata, handles)
% hObject    handle to cmdPause (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% pause the brainmovie
% pause the brainmovie
if strcmpi(get(hObject,'String'),'pause');
    doBrainMovie = false;
    set(hObject,'String','Unpause');
else
    doBrainMovie = true;
    set(hObject,'String','Pause');
end

evalin('base',sprintf('opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=%s;',fastif(doBrainMovie,'true','false')));
guidata(hObject,handles);

% --------------------------------------------------------------------
function mnuLoad_Callback(hObject, eventdata, handles)
% hObject    handle to mnuLoad (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% load the configs
[fname fpath] = uigetfile('*.mat','Load BrainMovie Config File');
if ~fname
    return;
end
tmp   = load(fullfile(fpath,fname));
fn    = fieldnames(tmp);
BMCFG = tmp.(fn{1});

if ~isstruct(BMCFG) || ~isfield(BMCFG,'BMopts')
    fprintf('Invalid BrainMovie Config\n');
    return;
end

% pause the brainmovie rendering
evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=false;');

% close existing brainmovie figure
evalin('base','close(figHandles.BMDisplay);  figHandles.BMDisplay = [];');
% update configs in base workspace
assignin('base','BMCFG',BMCFG);
assignin('base','BM_CFG_CHANGED',true);
assignin('base','newBrainmovie',true);
% close the BrainMovie control panel (a new one will be opened)
close(handles.figureHandle);
% resume the brainmovie rendering
evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=true;');

%     % pause the brainmovie rendering
%     evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=false;');
%     % redraw the property grids
%     handles = redrawPropertyGrid(hObject,handles,BMCFG);
%     % close existing brainmovie figure
%     evalin('base','close(figHandles.BMDisplay);  figHandles.BMDisplay = [];');
%     % update configs in base workspace
%     assignin('base','BMCFG',BMCFG);
%     assignin('base','BM_CFG_CHANGED',true);
%     % resume the brainmovie rendering
%     evalin('base','opts.miscOptCfg.doSIFT.dispBrainMovie.arg_selection=true;');

guidata(handles.figureHandle,handles);

% --------------------------------------------------------------------
function mnuSave_Callback(hObject, eventdata, handles)
% hObject    handle to mnuSave (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% get current contents of property grid
pg    = handles.PropertyGridHandle;
ps    = pg.GetPropertySpecification;
BMCFG = arg_tovals(ps,false);

% select the path/file for saving configs
[fname fpath] = uiputfile('*.mat','Save BrainMovie Config File');
if ~fname
    return;
end
% save the opts structure
save(fullfile(fpath,fname),'BMCFG');

%     varname = inputdlg2({'Enter name of variable in workspace to store configuration'}, ...
%                         'Save GUI configuration to workspace',1,{''});
%
%     if isempty(varname)
%         return;
%     end
%
%     % get the property specification
%     pg = handles.PropertyGridHandle;
%     ps = pg.GetPropertySpecification;
%     cfg = arg_tovals(ps,false);
%
%     % save configuration structure to workspace
%     assignin('base',varname{1},cfg);

% --------------------------------------------------------------------
function Untitled_1_Callback(hObject, eventdata, handles)
% hObject    handle to Untitled_1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


%detects if we're running Octave
function result = isOctave()
    result = exist('OCTAVE_VERSION') ~= 0;
end


% This function sort the utterance ID, and also rearrange the corresponding
% data cell array.
%
function [ID_sort, data_sort] = groupSort(ID, data)

[ID_sort, idx] = sort(ID);
data_sort = data(idx);


function [sessionInfo] = bz_getSessionInfo(basePath,varargin)
%[sessionInfo] = bz_getSessionInfo(basePath) loads the sessionInfo metadata
%for the recording in basePath. basePath should be in the format:
%       /whateverPath/baseName/
%           a file  basePath/baseName.sessionInfo.mat
%           or      basePath/baseName.xml
%           should exist.
%If no baseName.sessionInfo.mat exists, loads from the xml.
%
%INPUT
%   basePath            directory: '/whatevetPath/baseName/'
%   (options)
%       'saveMat'       (default: prompt)
%       'noPrompts'     (default: false) prevents prompts about
%                       saving/adding metadata
%       'editGUI'       (default: false) opens a GUI to edit select
%                       sessionInfo fields (beta, please add/improve!)
%
%OUTPUT
%   sessionInfo         metadata structure
%
%2017 DLevenstein and DTingley
%% inputs and defaults
p = inputParser;
addParameter(p,'noPrompts',false,@islogical);
addParameter(p,'editGUI',false,@islogical);
addParameter(p,'saveMat',false,@islogical);
parse(p,varargin{:})
noPrompts = p.Results.noPrompts;
editGUI = p.Results.editGUI;
saveMat = p.Results.saveMat;

if ~exist('basePath','var')
    basePath = pwd;
end
baseName = bz_BasenameFromBasepath(basePath);
filename = fullfile(basePath,[baseName,'.sessionInfo.mat']);

%% Load the stuff
%d = dir('*sessionInfo*'); %all files with sessioninfo in the name
if exist(filename,'file')
    sessionInfostruct = load(filename);
    %Checks that there is a single structure in the sessionInfo file
    varsInFile = fieldnames(sessionInfostruct);
    if numel(varsInFile)==1
        sessionInfo = sessionInfostruct.(varsInFile{1});
    else
        warning('Your .sessionInfo.mat has multiple variables/structures in it... wtf.')
        sessionInfo = sessionInfostruct;
    end
    SIexist = true;  %Marks that session info exists as expected
else
   warning(['could not find file ',baseName,'.sessionInfo.mat ',...
       'running LoadParameters instead..'])
   sessionInfo = LoadParameters(basePath);
   SIexist = false;
end

%% Check sessionInfo using bz_isSessionInfo and update if necessary
bz_isSessionInfo(sessionInfo);

%Here: prompt user to add any missing sessionInfo fields and save
if editGUI
    [ sessionInfo ] = bz_sessionInfoGUI(sessionInfo);
    SIexist = false;
% elseif ~isfield(sessionInfo,'region') && ~noPrompts
%     regionadd = questdlg(['Your sessionInfo is missing regions, ',...
%         'would you like to add them?'],'Add Regions?','Yes');
%     switch regionadd
%         case 'Yes'
%             [sessionInfo] = bz_sessionInfoGUI(sessionInfo,'Regions');
%             SIexist = false;
%         case 'Cancel'
%             return
%     end
end

%Should check that sessionInfo.session.name and sesioninfo.session.path
%match basePath....  if not, prompt the user to save with the correct
%information.

%% Save sessionInfo file
%Prompt user to save basePath/baseName.sessionInfo.mat
%if loaded from xml or changed
if ~noPrompts && ~SIexist %Inform the user that they should save a file for later
    savebutton = questdlg(['Would you like to save your sessionInfo in ',...
        filename '?'],'Save sessionInfo?','Yes');
    switch savebutton
        case 'Yes'
            saveMat = true;
        case 'No'
            saveMat = false;
        case 'Cancel'
            return
    end
end

if saveMat
    disp(['saving ',baseName,'.sessionInfo.mat'])
    save(filename,'sessionInfo');
end
end


function updatelarvasamuel(hfly,hfly_extra,pos)

set(hfly,'XData',[pos.xcontour,nan,pos.xspine'],'YData',[pos.ycontour,nan,pos.yspine']);
set(hfly_extra,'XData',[pos.xhead,pos.xmid],'YData',[pos.yhead,pos.ymid]);


% APM Load Data File
%
% response = csv_load(server,app,filename)
%
% Function csv_load uploads the data file (csv) to the web-server
%   with the following arguments:
%
%   server = address of server
%      app = application name
% filename = data filename
%
% A response is returned indicating whether the file was
%   uploaded successfully to the server
%
function [response] = csv_load(server,app,filename)
   % convert to lowercase and deblank
   app = lower(deblank(app));

   % load model
   fid=fopen(filename,'r');
   tline = [];
   while 1
      aline = fgets(fid);
      if ~ischar(aline), break, end
      % remove any double quote marks
      aline = [strrep(aline,'"',' ')];
      tline = [tline aline];
   end
   fclose(fid);

   % send to server once for every 2000 characters
   ts = size(tline,2);
   block = 2000;
   cycles = ceil(ts/block);
   for i = 1:cycles,
      if i<cycles,
         csv_block = ['csva ' tline((i-1)*block+1:i*block)];
      else
         csv_block = ['csv ' tline((i-1)*block+1:end)];
      end
      response = apm(server,app,csv_block);
   end
   response = 'Successfully loaded CSV file';


function b = InitBatch(bfile)

%InitBatch - Initialize a new batch job.
%
%  USAGE
%
%    b = InitBatch(bfile)
%
%    bfile          batch file listing the parameters for each iteration
%
%  SEE
%
%    See also StartBatch, ShowBatch
%

% Copyright (C) 2007-2013 by Michaël Zugaro
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.

% Check number of parameters
if nargin < 1,
	error('Incorrect number of parameters (type ''help InitBatch'' for details).');
end

% Make sure batch file is valid
s = dbstack;
if ~isstring_FMAT(bfile) || ~exist(bfile,'file'),
	error(['Batch file not found (type ''help ' s.name ''' for details).']);
end

% Open batch file
f = fopen(bfile,'r');
if f == -1, error(['Could not open file ''' bfile '''.']); end

item = 1;
l = 1;
nFields = 0;
% Read batch file
while ~feof(f),
	field = 0;
	% Read next line
	line = fgetl(f);
	while ~isempty(line),
		% Get next field
		[token,line] = strtok(line);
		% Skip rest of line if this is a comment
		if isempty(token) | token(1) == '%', break; end
		field = field + 1;
		% Determine if this is a number, vector or string
		n = str2num(token);
		if ~isempty(n),
			% It is a number, convert it to numerical format
			b.field{item,field} = n;
		elseif token(1) ~= '[',
			% It is a string, keep it as it is
			b.field{item,field} = token;
		else
			% It is a vector (or matrix)
			[token,line] = strtok([token line],'[]');
			n = str2num(token);
			if isempty(n),
				error(['Incorrect vector or matrix in batch file (line ' int2str(l) ', element ' int2str(field) ').']);
			end
			b.field{item,field} = n;
			line = line(2:end);
		end
	end
	if field > 0,
		% Make sure all lines have the same number of elements
		if item == 1,
			nFields = field;
		elseif field ~= nFields,
			if l == 2,
				error(['Incoherent numbers of elements in batch file (' int2str(nFields)  ' elements on line 1, but ' int2str(field) ' elements on line 2).']);
			else
				error(['Incoherent numbers of elements in batch file (' int2str(nFields)  ' elements on lines 1-' int2str(l-1) ', but ' int2str(field) ' elements on line ' int2str(l) ').']);
			end
		end
		% Next item
		b.line(item) = l;
		item = item + 1;
	end
	% Update line counter (for error messages)
	l = l + 1;
end

% Close file
fclose(f);

% Reset iterator
b.currentItem = 0;
b.currentField = 0;

% Empty fields
b.log = -1;
b.mfile = [];


function [ccls cfun] = get_data_class()
% get class supported by ann
d = annmex();
ccls = class(d);

cfun = @(x) cast(x, ccls);


function out = isdelta(f)
%ISDELTA   Always returns false, since CLASSICFUNs don't have delta functions.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

    out = false;
end


function cache_dir = resolve_cache_dir(model, model_dir, opts)
%
% This file is part of the code that implements the following ICCV2015 accepted paper:
% title: "Object detection via a multi-region & semantic segmentation-aware CNN model"
% authors: Spyros Gidaris, Nikos Komodakis
% institution: Universite Paris Est, Ecole des Ponts ParisTech
% Technical report: http://arxiv.org/abs/1505.01749
% code: https://github.com/gidariss/mrcnn-object-detection
%
%
% AUTORIGHTS
% --------------------------------------------------------
% Copyright (c) 2015 Spyros Gidaris
%
% "Object detection via a multi-region & semantic segmentation-aware CNN model"
% Technical report: http://arxiv.org/abs/1505.01749
% Licensed under The MIT License [see LICENSE for details]
% ---------------------------------------------------------

cache_dir_base = fullfile(model_dir, 'cache_dir');
mkdir_if_missing(cache_dir_base);
if ~isempty(opts.cache_dir)
    assert(isempty(opts.cache_dir_name));
    cache_dir = opts.cache_dir;
elseif ~isempty(opts.cache_dir_name)
    assert(isempty(opts.cache_dir));
    cache_dir = fullfile(cache_dir_base, opts.cache_dir_name);
else
    if opts.use_detection_svms
        cache_dir = fullfile(cache_dir_base, 'detection_svms');
    else
        cache_dir = fullfile(cache_dir_base, 'softmax');
    end
end
mkdir_if_missing(cache_dir);
end


% Max Jaderberg 2011

function result = run_dot_m( file_to_run, arguments )
%RUN_DOT_M Runs the given .m file with the argument struct given
%   For exmaple run_dot_m('/path/to/function.m', args);
%   args is a struct containing the arguments. function.m must take only
%   one parameter, the argument structure

    [dir, func_name, ext] = fileparts(file_to_run);

    if ~size(ext)
        result = 'Error: Need to give path to .m file';
        return
    end

    if ~strcmp(ext, '.m')
        result = 'Error: Need to give path to .m file';
        return
    end

%     Add function path to current path
    addpath(dir);
    if isstruct(arguments)
        result = feval(func_name, arguments);
    else
        result = feval(func_name);
    end
end


function planC = save_planC(planC,optS,saveflag,optFileName)
%"save_planC"
%   Saves plan using GUI interface.  Can either behave as Save or SaveAs,
%   where Save prompts to overwrite source planC file and SaveAs raises a
%   file location menu.  saveflag is either 'save' or 'saveas'.  If save is
%   called, the filename is assumed to be contained in stateS.CERRFile.
%
%   If 'save' is called, .bz2 files are automatically compressed and
%   replaced if they existed (assuming the user agrees when prompted).
%
%   If 'saveas' is called and a .bz2 file is specified, it is also
%   automatically compressed.  If a .mat file is specified, the user is
%   asked if compression is desired.
%
%     A.I. Blanco.
%LM:  14 Oct 02, JOD.
%     26 Jun 04, JRA, Added saveas/save distinction.
%     20 Jul 04, JRA, If saveas is used, now updates current file state.
%     20 Aug 04, JRA, Automatic compression if .bz2 file specified.
%     10 Sep 04, JRA, Now check for Matlab7 and saves according to optS.
%     27 Apr 05, JRA, Added tarball handling.
%     26 Jul 06, APA, Removed tarball handling since remote variables are
%     now stored in a subdirectory ...\.._store.
%     09 Jul 07, APA, Used Matlab's built-in tar function to tar CERR plan
%     and the remotely stored files.
%     17 Dec 07, APA, Added option to compress CERR plan using zip utility.
%     11 Jan 08, APA, (incorporated KU's changes) Fixed case where file could
%                     be overwritten without a warning if filename is entered
%                     without an extension. In case of error during save attempt
%                     (eg., permission denied), added ability to re-try.
%
%Usage:
%   zipped = save_planC(planC,optS, saveflag)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR 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 General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

global stateS

indexS = planC{end};

%Determine if we want 'save', or 'saveas...' behavior.
if ~exist('saveflag','var')
    saveflag = 'saveas';
elseif ~any(strcmpi(saveflag, {'save','saveas','passed'}))
    error('Incorrect call to save_planC.m');
end

%Store the revision and timeSaved into header of planC
planC = updateRevision(planC);

permission = 0;
while permission == 0   %Use while statement in case permission to overwrite file is denied.   KU 12/15/07
    %Acquire save filename according to saveFlag type.
    switch upper(saveflag)
        case 'SAVE'
            button = questdlg(['Are you sure you wish to save changes made to ' stateS.CERRFile ' ?'], 'Overwrite', 'Yes', 'No', 'No');
            if ~strcmpi(button, 'yes')
                CERRStatusString(['Save cancelled. Ready.']);
                return;
            end
            saveFile = stateS.CERRFile;

        case 'SAVEAS'

            deciding = 1;
            while deciding    %Use while statement in case filename is entered without an extension.   KU 12/8/07

                whichPlan = fliplr(strtok(fliplr(fileparts(char(planC{indexS.header}.archive))),'\/'));

                try
                    directory = fileparts(stateS.CERRFile);
                catch
                    directory = cd;
                end
                if isempty(directory)
                    directory = cd;
                end
                wd = cd;
                cd(directory);
                fileTypeC = {'*.mat', 'Native Matlab/Octave (*.mat)';
                    '*.mat.bz2, *.mat.zip)','Compressed (*.mat.zip, *.mat.zip)'};
                [fname,pname] = uiputfile(fileTypeC,['Save the ' whichPlan ' data as:']);
                cd(wd);
                if isequal(fname,0) || isequal(pname,0)
                    CERRStatusString('Save cancelled. Ready.');
                    return;
                end
                if ~isempty(strfind(fname,'.'))
                    saveFile = fullfile(pname,fname);
                    deciding = 0;
                else     %in case filename is entered without an extension.   KU 12/8/07
                    fname = [fname,'.mat'];
                    list = dir(fullfile(pname, '*.*'));
                    match = 0;
                    for i = 1:size(list,1)
                        if list(i).isdir==0 && strcmpi(fname, list(i).name)
                            match = 1;
                            button = questdlg([[pname fname],' already exists! Do you want to replace it?'],'Save file as?','yes','no','no');
                            if strcmpi(button, 'yes')
                                saveFile = fullfile(pname,fname);
                                deciding = 0;
                                break
                            end
                        end
                    end

                    if match == 0
                        saveFile = fullfile(pname,fname);
                        deciding = 0;
                    end
                end
            end

            %get storage directory for remote variables in the original plan
            remotePath = '';
            if isfield(stateS,'CERRFile') && ~isempty(stateS.CERRFile)
                [pathstr, name, ext] = fileparts(stateS.CERRFile);
                remotePath = fullfile(pathstr,[name,'_store']);
            end

        case 'PASSED'
            saveFile = optFileName;
    end

    %Add .mat if required, parse other valid extensions if they exist.  If
    %invalid extensions exist, they are considered part of the root filename
    %and proper extensions are appended to them.
    [ext, roots] = getFileExtensions(saveFile);
    if ~isempty(ext)
        if strcmpi(ext(1), '.mat')
            zipFile     = 0;
            saveFile    = [roots{1} '.mat'];
        elseif length(ext) > 2 && strcmpi(ext(1), '.tar') && ...
                (strcmpi(ext(2), '.bz2') || strcmpi(ext(2), '.zip')) && strcmpi(ext(3), '.mat')
            zipFile     = 1;
            saveFile    = [roots{3} '.mat'];
        elseif length(ext) > 1 && (strcmpi(ext(1), '.bz2') || ...
                strcmpi(ext(1), '.zip')) && strcmpi(ext(2), '.mat')
            zipFile     = 1;
            saveFile    = [roots{2} '.mat'];
        else
            saveFile    = [saveFile '.mat'];
            zipFile     = 0;
        end
    else
        saveFile    = [saveFile '.mat'];
        zipFile     = 0;
    end

    %Zip if required, prompting if saveAs w/o .bz2 suffix specified.
    if zipFile
        ans = 'yes';
        %[jnk1,jnk2,ext] = fileparts(stateS.CERRFile);
        %if ~strcmpi(ext,'bz2') || ~strcmpi(ext,'zip')
        extSave = ext;
        indMat = strcmpi('.mat',extSave);
        extSave(indMat) = [];
        extSave = extSave{1};
        if ~strcmpi(extSave,'bz2') || ~strcmpi(extSave,'zip')
            if ~isfield(stateS,'optS')
                stateS.optS = opts4Exe([getCERRPath,'CERROptions.json']);
            end
            extSave = stateS.optS.CompressType;
        end
    elseif ~zipFile && strcmpi(saveflag, 'passed')
        ans = 'no';
    elseif ~zipFile && strcmpi(saveflag, 'saveas')
        ans = questdlg('Zip the .mat file using bz2/zip?');
        if ~isfield(stateS,'optS')
            stateS.optS = opts4Exe([getCERRPath,'CERROptions.json']);
        end
        extSave = stateS.optS.CompressType;
    else
        ans = 'no';
    end

    if strcmpi(ans,'yes')
        zipFile = 1;
    end
    %Save the planC to the specified file.
    try
        [saveFile, planC] = writeToDisk(planC, saveFile, zipFile);
        permission = 1;
    catch
        h = warndlg(lasterr,'Error Saving File!','modal');
        uiwait(h);
    end
end

%Remind CERR where the current file is, in case of filename change.
stateS.CERRFile = saveFile;

[pathstr, name, ext] = fileparts(stateS.CERRFile);
switch lower(ans)
    case 'yes'
        if strcmpi(extSave,'bz2')
            CERRStatusString(['Compressing ' saveFile '...']);
            outstr = gnuCERRCompression(saveFile,'compress');
            if (~ischar(outstr)) % Function returned as zero when compression complete
                errordlg('File not found by compression routine.', 'CERR compression');
                return;
            else
                stateS.CERRFile = [saveFile '.bz2'];
            end
        elseif strcmpi(extSave,'zip')
            zip(saveFile,saveFile)
            delete(saveFile)
            stateS.CERRFile = [saveFile '.zip'];
        end
        if exist(fullfile(pathstr,[name,'_store']),'dir')==7
            copyfile(fullfile(pathstr,[name,'_store']),fullfile(pathstr,[name,'.mat_store']),'f')
            rmdir(fullfile(pathstr,[name,'_store']),'s')
            [planC, stateS] = updateRemotePaths(planC, stateS, stateS.CERRFile);
        end
end

%tar the files if required
if isfield(stateS,'reqdRemoteFiles') && ~isempty(stateS.reqdRemoteFiles)
    filesToTar = stateS.reqdRemoteFiles;
    if ~isempty(filesToTar)
        dirToTar = fileparts(filesToTar{1});
    end
    %filesToTar{end+1} = stateS.CERRFile;
    tar([stateS.CERRFile,'.tar'],{dirToTar stateS.CERRFile})
    %delete the .mat or .bz2 file since it is in tarred format now
    delete(stateS.CERRFile)
else
    %delete the tar file since no remote files exist
    if exist([stateS.CERRFile,'.tar'],'file')
        %try
            delete([stateS.CERRFile,'.tar'])
        %end
    end
end

if strcmpi(saveflag,'SAVEAS')
    if exist(remotePath,'dir') == 7
        rmdir(remotePath,'s')
    end
end

CERRStatusString(['Saved ' name ext '. Ready. (' datestr(now) ')']);

try
    stateS.planMerged = 0;
end

function [saveFile, planC] = writeToDisk(planC, saveFile, zipFile)
%"writeToDisk"
%   Save the planC to disk.  If remote variables exist, handle them.

global stateS
if iscell(planC)
    indexS = planC{end};
end

%Get a list of remote variable files using the save flag to store.
% remoteFiles = listRemoteFiles(planC, 0);
remoteFiles = listRemoteScanAndDose(planC);

%Prepare a list of files that will be added to _store directory.
filesLocal = [];
remoteFullFile = {};
remotePathLocal = {};
for i = 1:length(remoteFiles)
    switch upper(remoteFiles(i).storageType)
        case {'LOCAL'}
            remotePathLocal{i} = remoteFiles(i).remotePath;
            filesLocal{i} = remoteFiles(i).filename;
            remoteFullFile{i} = fullfile(remotePathLocal{i},filesLocal{i});
    end
end

[pathstr, name, ext] = fileparts(saveFile);
CERRStatusString(['Saving ' name ext '...']);

% save Remotely stored files

if zipFile
    name = [name '.mat'];
end
if ~isempty(filesLocal) && ~exist(fullfile(pathstr,[name,'_store']),'dir')
    mkdir(fullfile(pathstr,[name,'_store']))
elseif isempty(filesLocal)
    if length(remotePathLocal) > 1
        uniqRemothPathC = unique(remotePathLocal);
        for iPath = 1:length(uniqRemothPathC)
            if exist(uniqRemothPathC{iPath},'dir') == 7
                rmdir(uniqRemothPathC{iPath},'s')
            end
        end
    end
end

for i = 1:length(filesLocal)
    if ~isequal(remoteFullFile{i},fullfile(pathstr,[name,'_store'],filesLocal{i}))
        copyfile(remoteFullFile{i},fullfile(pathstr,[name,'_store'],filesLocal{i}))
    end
end

% delete unnecessary remote files in original plan that were created
% temporarily
for i = 1:length(remoteFullFile)
    if isfield(stateS,'reqdRemoteFiles') && ~ismember(remoteFullFile{i},stateS.reqdRemoteFiles) && ...
            ~isequal(fileparts(remoteFullFile{i}),fullfile(pathstr,[name,'_store']))
        delete(remoteFullFile{i})
    end
end

%delete unnecessary remote files in original plan that changed
if ~isempty(stateS) && isfield(stateS,'reqdRemoteFiles')
    for i = 1:length(stateS.reqdRemoteFiles)
        if ~ismember(stateS.reqdRemoteFiles(i),remoteFullFile) && ...
                isequal(fileparts(stateS.reqdRemoteFiles{i}),fullfile(pathstr,[name,'_store']))
            delete(stateS.reqdRemoteFiles{i})
        end
    end
end

[planC, stateS] = updateRemotePaths(planC, stateS, saveFile, zipFile);

%Save functions... modified to work with matlab 7
varInfoS = whos('planC');
if varInfoS.bytes > 2*10^9 %Use save version 7.3 for plans > 2GB
    saveOpt = '-v7.3';
else
saveOpt = getSaveInfo;     %Default save version (defined in CERROptions.m)
end
if ~isempty(saveOpt)
    save(saveFile, 'planC', saveOpt);
else
    save(saveFile, 'planC');
end

function [ext, root] = getFileExtensions(filename)
%"getFileExtensions"
%   Returns the file extensions of filename, each in an individual cell, in
%   reverse order.  Also returns the root filename strings for each
%   iteration of the while loop.

ext  = {};
root = {};

[pathstr,fname,fext] = fileparts(filename);
while ~isempty(fext)
    ext{end+1} = fext;
    root{end+1} = fullfile(pathstr, fname);
    [pathstr,fname,fext] = fileparts(fullfile(pathstr, fname));
end

function [planC, stateS] = updateRemotePaths(planC, stateS, saveFile, zipFile)
%"updateRemotePaths"
% update remote path in planC for remote doseArrays and scanArrays. Also
% update fullFiles in stateS

if iscell(planC)
    indexS = planC{end};
else
    return;
end

if ~exist('zipFile','var')
    zipFile = 0;
end
[pathstr, name, ext] = fileparts(saveFile);
if zipFile
    name = [name '.mat'];
end

stateS.reqdRemoteFiles = {};
for i = 1:length(planC{indexS.scan})
    if ~isLocal(planC{indexS.scan}(i).scanArray)
        planC{indexS.scan}(i).scanArray.remotePath = fullfile(pathstr,[name,'_store']);
        stateS.reqdRemoteFiles{end+1} = fullfile(pathstr,[name,'_store'],planC{indexS.scan}(i).scanArray.filename);
        planC{indexS.scan}(i).scanArraySuperior.remotePath = fullfile(pathstr,[name,'_store']);
        stateS.reqdRemoteFiles{end+1} = fullfile(pathstr,[name,'_store'],planC{indexS.scan}(i).scanArraySuperior.filename);
        planC{indexS.scan}(i).scanArrayInferior.remotePath = fullfile(pathstr,[name,'_store']);
        stateS.reqdRemoteFiles{end+1} = fullfile(pathstr,[name,'_store'],planC{indexS.scan}(i).scanArrayInferior.filename);
    end
end
for i = 1:length(planC{indexS.dose})
    if ~isLocal(planC{indexS.dose}(i).doseArray)
        planC{indexS.dose}(i).doseArray.remotePath = fullfile(pathstr,[name,'_store']);
        stateS.reqdRemoteFiles{end+1} = fullfile(pathstr,[name,'_store'],planC{indexS.dose}(i).doseArray.filename);
    end
end

for i = 1:length(planC{indexS.IM})
    if ~isempty(planC{indexS.IM}(i).IMDosimetry.beams) && ~isLocal(planC{indexS.IM}(i).IMDosimetry.beams(1).beamlets)
        for iBeam = 1:length(planC{indexS.IM}(i).IMDosimetry.beams)
            planC{indexS.IM}(i).IMDosimetry.beams(iBeam).beamlets.remotePath = fullfile(pathstr,[name,'_store']);
            stateS.reqdRemoteFiles{end+1} = fullfile(pathstr,[name,'_store'],planC{indexS.IM}(i).IMDosimetry.beams(iBeam).beamlets.filename);
        end
    end
end


%roscore=rosmatlab.roscore()

%node = rosmatlab.node('MyNode', '10.0.0.76')
% publisher = node.addPublisher('MyTopic', 'std_msgs/String');
% subscriber = node.addSubscriber('MyTopic', 'std_msgs/String', 1);
% msg = node.newMessage('std_msgs/String');
% msg.setData('hello world!');
% subscriber.setOnNewMessageListeners(@function3)
% publisher.publish(msg)

msg = rosmessage('std_msgs/String');
msg.Data = 'hello world!';

% pub = rospublisher('/MyTopic');
% pub.send(msg);

rospublisher('/MyTopic', msg);


function this = plot(this, module, varargin)
% Plots different aspects of MrSeries using MrImage-plot functions
%
%   Y = MrSeries()
%   Y.plot(inputs)
%
% This is a method of class MrSeries.
%
% IN
%
% OUT
%
% EXAMPLE
%   plot
%
%   See also MrSeries

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-07-08
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

if ~exist('module', 'var')
    module = 'data';
end

switch module
    case {'data', 'mean', 'sd', 'snr', 'coeffVar', 'diffLastFirst'}
        this.(module).plot(varargin{:});
    otherwise
        error('tapas:uniqc:MrSeries:InvalidPlotModule', ...
            'plotting module %s not implemented for MrSeries', ...
            module);
end


classdef nme_gen3p_acc < mp.nme_gen3p & mp.form_acc

%   MATPOWER
%   Copyright (c) 2021, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%     properties
%     end     %% properties

%     methods
%     end     %% methods
end         %% classdef