function dataS = populate_planC_field(cellName, dcmdir_patient, optS, varargin)
%"populate_planC_field"
%   Given the name of a child cell to planC, such as 'scan', 'dose',
%   'comment', etc. return a copy of that cell with all fields properly
%   populated with data from the files contained in a dcmdir.PATIENT
%   structure.
%
%JRA 06/15/06
%YWU Modified 03/01/08
%NAV 07/19/16 updated to dcm4che3
%   replaced dcm2ml_element with getTagValue
%   and used getValue instead of get
%
%Usage:
%   dataS = populate_planC_field(cellName, dcmdir);
%
% 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/>.

%Get template for the requested cell.
persistent rtPlans scanOriS
structS = initializeCERR(cellName);
names   = fieldnames(structS);

dataS = [];
switch cellName
    case 'header'
        rtPlans = []; % Clear persistent object
        for i = 1:length(names)
            dataS.(names{i}) = populate_planC_header_field(names{i}, dcmdir_patient);
        end

    case 'scan'

        disp('Loading Scan ...')

        % supportedModalities = {'CT'};
        scansAdded = 0;

        %Extract all series contained in this patient.
        [seriesC, typeC] = extract_all_series(dcmdir_patient);
        % Extract CT, MR, PET, NM... in that order, since frame of
        % reference is reused
        ctIndV = strcmpi(typeC,'CT');
        mrIndV = strcmpi(typeC,'MR');
        newIndV = [];
        newIndV = [newIndV, find(ctIndV), find(mrIndV), find(~(ctIndV | mrIndV))];
        seriesC = seriesC(newIndV);
        typeC = typeC(newIndV);

        %ctSeries = length(find(seriesC(strcmpi(typeC, 'CT'))==1));

        %Initialize structure to store image orientation per scan.
        scanOriS = struct();
        %Place each series (CT, MR, etc.) into its own array element.
        for seriesNum = 1:length(seriesC)
            % Test IOP here to find if it is "nominal" or "non-nominal"
            % % %             outIOP = getTest_Scan_IOP(seriesC{seriesNum}.Data(1).file);
            if ismember(typeC{seriesNum},{'CT','OT','NM','MR','PT','ST','MG','SM'})

                %Populate each field in the scan structure.
                for i = 1:length(names)
                    dataS(scansAdded+1).(names{i}) = ...
                        populate_planC_scan_field(names{i}, ...
                        seriesC{seriesNum}, typeC{seriesNum}, seriesNum, optS);
                end

                if isempty(dataS(scansAdded+1).scanInfo(1).rescaleIntercept)
                    dataS(scansAdded+1).scanInfo(1).rescaleIntercept = 0;
                end

                %Apply ReScale Intercept and Slope
                scanArray3M = zeros(size(dataS(scansAdded+1).scanArray),'single');
                numSlcs = size(dataS(scansAdded+1).scanArray,3);
                rescaleSlopeV = ones(numSlcs,1);
                realWorldImageFlag = false;
                for slcNum = 1:numSlcs
                    rescaleSlope = dataS(scansAdded+1).scanInfo(slcNum).rescaleSlope;
                    rescaleIntrcpt = dataS(scansAdded+1).scanInfo(slcNum).rescaleIntercept;
                    realWorldValueSlope = dataS(scansAdded+1).scanInfo(slcNum).realWorldValueSlope;
                    realWorldValueIntercept = dataS(scansAdded+1).scanInfo(slcNum).realWorldValueIntercept;
                    realWorldMeasurCodeMeaning = dataS(scansAdded+1).scanInfo(slcNum).realWorldMeasurCodeMeaning;
                    if ~isempty(realWorldValueSlope) && strcmpi(typeC{seriesNum}, 'MR') && ...
                            ~strcmpi(realWorldMeasurCodeMeaning,'no units')
                        realWorldImageFlag = true;
                        scanArray3M(:,:,slcNum) = ...
                            single(dataS(scansAdded+1).scanArray(:,:,slcNum)) * single(realWorldValueSlope) + single(realWorldValueIntercept);
                    else
                        scanArray3M(:,:,slcNum) = ...
                            single(dataS(scansAdded+1).scanArray(:,:,slcNum)) * single(rescaleSlope) + single(rescaleIntrcpt);
                    end
                    rescaleSlopeV(slcNum) = rescaleSlope;
                end
                minScanVal = min(scanArray3M(:));
                ctOffset = max(0,-minScanVal);
                scanArray3M = scanArray3M + ctOffset;
                minScanVal = min(scanArray3M(:));
                maxScanVal = max(scanArray3M(:));
                if ~realWorldImageFlag && ~any(abs(rescaleSlopeV-1) > eps*1e5) % convert to uint if rescale slope is not 1.
                   if minScanVal >= -32768 && maxScanVal <= 32767
                       scanArray3M = uint16(scanArray3M);
                   else
                       scanArray3M = uint32(scanArray3M);
                   end
                end
                for slcNum = 1:numSlcs
                    dataS(scansAdded+1).scanInfo(slcNum).CTOffset = ctOffset;
                end
                dataS(scansAdded+1).scanArray = scanArray3M;

                %Apply scale slope & intercept for Philips data if not
                %realWorldValue
                if strcmpi(typeC{seriesNum}, 'MR') && ...
                        strcmpi(optS.store_scan_as_mr_philips_precise_value, 'yes')
                    % Ref: Chenevert, Thomas L., et al. "Errors in quantitative image analysis due to platform-dependent image scaling."
                    manufacturer = dataS(scansAdded+1).scanInfo(1).manufacturer;
                    if ~isempty(strfind(lower(manufacturer),'philips')) && ...
                            ~isempty(dataS(scansAdded+1).scanInfo(1).scaleSlope) && ...
                            ~realWorldImageFlag
                        scaleSlope = dataS(scansAdded+1).scanInfo(1).scaleSlope;
                        dataS(scansAdded+1).scanArray = single(dataS(scansAdded+1).scanArray)./(rescaleSlope*scaleSlope);
                    end
                end

                scansAdded = scansAdded + 1;

            elseif strcmpi(typeC{seriesNum}, 'US')
                %Populate each field in the scan structure.
                for i = 1:length(names)
                    dataS(scansAdded+1).(names{i}) = populate_planC_USscan_field(names{i}, seriesC{seriesNum}, typeC{seriesNum});
                end
                scansAdded = scansAdded + 1;
            end
            scanOriS(scansAdded).scanUID = dataS(scansAdded).scanUID;
            scanOriS(scansAdded).imageOrientationPatient = dataS(scansAdded).scanInfo(1).imageOrientationPatient;
        end

    case 'structures'
        [seriesC, typeC]    = extract_all_series(dcmdir_patient);
        structsAdded          = 0;

        % Get scan object from varargin if it is not empty.
        % for inserting structures to existing planC
        if ~isempty(varargin)
            scanOriS = varargin{1};
        end

        %hWaitbar = waitbar(0,'Loading Structures Please wait...');

        %strSeries = length(find(strcmpi(typeC, 'RTSTRUCT')==1));

        disp('Loading RTSTRUCT ...')

        %Place each structure into its own array element.
        for seriesNum = 1:length(seriesC)
            %if ismember(typeC{seriesNum}, supportedTypes)
            if strcmpi(typeC{seriesNum}, 'RTSTRUCT')

                RTSTRUCT = seriesC{seriesNum}.Data;
                for k = 1:length(RTSTRUCT)
                    strobj  = scanfile_mldcm(RTSTRUCT(k).file);

                    % StructureSetROISequence
                    SSRS = strobj.getValue(805699616); %org.dcm4che3.data.Tag.StructureSetROISequence; %hex2dec('30060020')
                    numSSRS = SSRS.size();
                    roiNummV = zeros(1,numSSRS,'int32');
                    for iStr = 1:numSSRS
                        ssObj = SSRS.get(iStr - 1);
                        roiNummV(iStr) = ssObj.getInts(805699618); %org.dcm4che3.data.Tag.ROINumber; % vr=IS
                    end

                    %ROI Contour Sequence.
                    RCS = strobj.getValue(805699641); %org.dcm4che3.data.Tag.ROIContourSequence; % hex2dec('30060039')

                    %If non-empty sequence, get item count, else set to zero
                    if ~isempty(RCS)
                        nStructures = RCS.size();
                    else
                        nStructures = 0;
                    end

                    % ROI = strobj.getInt(org.dcm4che2.data.Tag.ROIContourSequence);

                    structuresToImportC = optS.structuresToImport;

                    structuresImportMatchCriteria = optS.structuresImportMatchCriteria;

                    %curStructNum = 1; %wy modified for suppport multiple RS files
                    for structNum = 1:nStructures

                        cObj = RCS.get(structNum - 1);

                        %Referenced ROI Number
                        %RRN = getTagValue(cObj, '30060084');
                        RRN = cObj.getInts(805699716); %org.dcm4che3.data.Tag.ReferencedROINumber; %IS

                        indSSRS = find(ismember(roiNummV,RRN));

                        % Get Structure name
                        ssObj = SSRS.get(indSSRS - 1);
                        %structureName = getTagValue(ssObj, '30060026');
                        structureName = char(ssObj.getStrings(805699622)); %org.dcm4che3.data.Tag.ROIName;

                        if ~isempty(structuresToImportC)
                            matchIndex = getMatchingIndex(structureName,...
                                structuresToImportC,...
                                structuresImportMatchCriteria);
                        else
                            matchIndex = 1;
                        end

                        if isempty(matchIndex)
                            continue;
                        end

                        %Populate each field in the structure field set
                        for i = 1:length(names)
                            dataS(structsAdded+1).(names{i}) = ...
                                populate_planC_structures_field(names{i}, ...
                                RTSTRUCT, scanOriS, strobj, ssObj, cObj, optS);
                        end
                        %curStructNum = curStructNum + 1;
                        structsAdded = structsAdded + 1;

                        %waitbar(structsAdded/(nStructures*length(RTSTRUCT)*strSeries), hWaitbar, 'Loading structures, Please wait...');

                        %a temporary limit of 52 structs
                        %if (structsAdded>=52)
                        %    return;
                        %end

                    end
                end

            end

        end
        %close(hWaitbar);
        %pause(0.1);

        %     case 'structureArray'
        %         populate_planC_structureArray_field(fieldName, dcmdir);

    case 'dose'
        [seriesC, typeC]    = extract_all_series(dcmdir_patient);
        dosesAdded          = 0;
        frameOfRefUIDC       = {};
        %Place each RTDOSE into its own array element.
        for seriesNum = 1:length(seriesC)
            %             if ismember(typeC{seriesNum}, supportedTypes)
            if strcmpi(typeC{seriesNum}, 'RTDOSE')

                RTDOSE = seriesC{seriesNum}.Data; %wy RTDOSE{1} for import more than one dose files;
                for doseNum = 1:length(RTDOSE)
                    doseobj  = scanfile_mldcm(RTDOSE(doseNum).file);

                    % Frame of Reference UID
                    %frameOfRefUID = getTagValue(doseobj, '00200052');
                    frameOfRefUID = char(doseobj.getStrings(2097234)); %org.dcm4che3.data.Tag.FrameOfReferenceUID;

                    %check if it is a DVH
                    dvhsequence = populate_planC_dose_field('dvhsequence', ...
                        RTDOSE(doseNum), doseobj, rtPlans, optS);

                    %Check if doesArray is present
                    dose3M = populate_planC_dose_field('doseArray', ...
                        RTDOSE(doseNum), doseobj, rtPlans, optS);

                    if isempty(dvhsequence) || ~isempty(dose3M)
                        %Populate each field in the dose structure.
                        for i = 1:length(names)
                            dataSeriesS(doseNum).(names{i}) = ...
                                populate_planC_dose_field(names{i}, ...
                                RTDOSE(doseNum), doseobj, rtPlans, optS);
                        end

                        if isempty(frameOfRefUIDC)
                            frameOfRefUIDC{1} = frameOfRefUID;
                        else
                            frameOfRefUIDC{end+1} = frameOfRefUID;
                        end

                        % Check if frame of reference UID matches any
                        % existing doses and add slices.

                        % If frame of reference UID does not match, then
                        % create a new dose
                        %dosesAdded = dosesAdded + 1;
                    end
                end

                multiFrameC = cellfun(@(x) x > 1,{dataSeriesS.numberMultiFrameImages},'UniformOutput',false);
                multiFrameIndV = [];
                for iM = 1:length(multiFrameC)
                    if isempty(multiFrameC{iM}) || (~isempty(multiFrameC{iM}) && multiFrameC{iM}==0)
                        multiFrameIndV(iM) = 0;
                    else
                        multiFrameIndV(iM) = 1;
                    end
                end
                multiFrameDoseNumsV = find(multiFrameIndV);
                for doseNum = multiFrameDoseNumsV
                    dosesAdded = dosesAdded + 1;
                    if isempty(dataS)
                        dataS = dataSeriesS(doseNum);
                    else
                        dataS(dosesAdded) = dataSeriesS(doseNum);
                    end
                end

                singleFrameIndV = ~multiFrameIndV;
                dataSeriesS = dataSeriesS(singleFrameIndV);
                doseSummationTypes = unique({dataSeriesS.doseSummationType});
                for doseTypeNum = 1:length(doseSummationTypes)
                    doseSumType = doseSummationTypes{doseTypeNum};
                    indV = strcmpi(doseSumType,{dataSeriesS.doseSummationType});
                    doseSeries = dataSeriesS(indV);
                    switch doseSumType
                        case 'BEAM'
                            beamNumberV = unique([doseSeries.refBeamNumber]);
                            for iB = 1:length(beamNumberV)
                                beamNum = beamNumberV(iB);
                                indV = [doseSeries.refBeamNumber] == beamNum;
                                doseS = doseSeries(indV);
                                [dataS,dosesAdded] = addDoseToSeries(doseS,dataS,dosesAdded);
                            end
                        case 'FRACTION'
                            fractionNumberV = unique(doseSeries.refFractionGroupNumber);
                            for iF = 1:length(fractionNumberV)
                                fractionNumber = fractionNumberV(iF);
                                indV = [doseSeries.refFractionGroupNumber] == fractionNumber;
                                doseS = doseSeries(indV);
                                [dataS,dosesAdded] = addDoseToSeries(doseS,dataS,dosesAdded);
                            end
                        otherwise
                            doseS = doseSeries;
                            [dataS,dosesAdded] = addDoseToSeries(doseS,dataS,dosesAdded);

                    end

                end

            end

        end

        % Get doses that have same frame of reference UIDs
        %[uniqFrameOfRefUIDC,jnk,uniqIndV] = unique(frameOfRefUIDC);
        %dataS(find(uniqIndV==1))

    case 'DVH'
        [seriesC, typeC]    = extract_all_series(dcmdir_patient);
        supportedTypes      = {'DVH'};
        dvhsAdded           = 0;

        %Place each RTDOSE into its own array element.
        for seriesNum = 1:length(seriesC)

            %             if ismember(typeC{seriesNum}, supportedTypes)
            if strcmpi(typeC{seriesNum}, 'RTDOSE')

                RTDOSE = seriesC{seriesNum}.Data; %wy RTDOSE{1} for import more than one dose files;
                for doseNum = 1:length(RTDOSE)
                    doseobj  = scanfile_mldcm(RTDOSE(doseNum).file);

                    %check if it is a DVH
                    dvhsequence = populate_planC_dose_field('dvhsequence', ...
                        RTDOSE(doseNum), doseobj, rtPlans, optS);

                    if ~isempty(dvhsequence) && ~dvhsequence.isEmpty

                        structureNameC = {};
                        structureNumberV = [];

                        % get a list of Structure Names
                        for seriesNumStr = 1:length(seriesC)

                            if strcmpi(typeC{seriesNumStr}, 'RTSTRUCT')

                                RTSTRUCT = seriesC{seriesNumStr}.Data;
                                for k = 1:length(RTSTRUCT)
                                    strobj  = scanfile_mldcm(RTSTRUCT(k).file);

                                    % StructureSetROISequence
                                    SSRS = strobj.getValue(805699616); %org.dcm4che3.data.Tag.StructureSetROISequence; %hex2dec('30060020');
                                    numSSRS = SSRS.size();
                                    for js = 1:numSSRS
                                        % Get Structure name
                                        ssObj = SSRS.get(js - 1);

                                        %structureName = getTagValue(ssObj, '30060026');
                                        structureNameC{end+1} = char(ssObj.getStrings(805699622)); %org.dcm4che3.data.Tag.ROIName;
                                        structureNumberV(end+1) = ssObj.getInts(805699618); %org.dcm4che3.data.Tag.ROINumber;

                                    end
                                end

                            end

                        end

                        %DVH_items = fieldnames(dvhsequence);
                        DVH_items = dvhsequence.size();
                        for i = 1:DVH_items
                            dvhsAdded = dvhsAdded + 1;
                            for j = 1:length(names)
                                dataS(dvhsAdded).(names{j}) = populate_planC_DVH_field(names{j}, RTDOSE(doseNum), doseobj, rtPlans);
                            end
                            dvhObj = dvhsequence.get(i-1);
                            dataS(dvhsAdded).volumeType = char(dvhObj.getStrings(805568513)); %org.dcm4che3.data.Tag.DVHType;
                            dataS(dvhsAdded).doseType = char(dvhObj.getStrings(805568516)); %org.dcm4che3.data.Tag.DoseType;
                            dataS(dvhsAdded).doseUnits = char(dvhObj.getStrings(805568514)); %org.dcm4che3.data.Tag.DoseUnits;

                            %dataS(dvhsAdded+1).volumeType = dvhsequence.(['Item_',num2str(i)]).DVHType;
                            %dataS(dvhsAdded+1).doseType = dvhsequence.(['Item_',num2str(i)]).DoseType;
                            %dataS(dvhsAdded+1).doseUnits = dvhsequence.(['Item_',num2str(i)]).DoseUnits;

                            dvhReferencedROISequence = dvhObj.getValue(805568608); %org.dcm4che3.data.Tag.DVHReferencedROISequence;
                            if ~isempty(dvhReferencedROISequence) %isfield(dvhsequence.(['Item_',num2str(i)]),'DVHReferencedROISequence')
                                %structureNumber = dvhsequence.(['Item_',num2str(i)]).DVHReferencedROISequence.Item_1.ReferencedROINumber;
                                structureNumber = dvhReferencedROISequence.get(0).getInts(805699716); %org.dcm4che3.data.Tag.ReferencedROINumber); % .(['Item_',num2str(i)]).DVHReferencedROISequence.Item_1.ReferencedROINumber;
                                indROINumber = find(structureNumberV==structureNumber);
                                if ~isempty(indROINumber)
                                    %dataS(dvhsAdded+1).structureName = structureNameC{indROINumber};
                                    dataS(dvhsAdded).structureName = structureNameC{indROINumber};
                                end
                            end
                            %dataS(dvhsAdded+1).doseScale = dvhsequence.(['Item_',num2str(i)]).DVHDoseScaling;
                            dataS(dvhsAdded).doseScale = dvhObj.getDoubles(805568594); %org.dcm4che3.data.Tag.DVHDoseScaling;
                            %binWidthsV = dvhsequence.(['Item_',num2str(i)]).DVHData(1:2:end);
                            binWidthsV = dvhObj.getDoubles(805568600); %org.dcm4che3.data.Tag.DVHData;
                            binWidthsV = binWidthsV(1:2:end);
                            volumeBinsV = dvhObj.getDoubles(805568600); %org.dcm4che3.data.Tag.DVHData;
                            volumeBinsV = volumeBinsV(2:2:end);

                            %maxDVHDose = dvhsequence.(['Item_',num2str(i)]).DVHMaximumDose;
                            %minDVHDose = dvhsequence.(['Item_',num2str(i)]).DVHMinimumDose;
                            doseBinsV = [];
                            doseBinsV(1) = 0;
                            for iBin = 2:length(binWidthsV)
                                doseBinsV(iBin) = doseBinsV(iBin-1) + binWidthsV(iBin);
                            end
                            dataS(dvhsAdded).DVHMatrix(:,1) = doseBinsV(:);
                            if strcmpi(dataS(dvhsAdded).volumeType,'cumulative')
                                if length(doseBinsV) > 1
                                    %volumeBinsV = dvhsequence.(['Item_',num2str(i)]).DVHData(2:2:end);
                                    volumeBinsV = diff(volumeBinsV(1)-volumeBinsV);
                                    volumeBinsV = [volumeBinsV(1); volumeBinsV(:)];
                                else
                                    volumeBinsV = dataS(dvhsAdded).DVHMatrix(:,1)*NaN;
                                end
                            else
                                %volumeBinsV = dvhsequence.(['Item_',num2str(i)]).DVHData(2:2:end);
                                volumeBinsV = volumeBinsV(:);
                            end
                            dataS(dvhsAdded).DVHMatrix(:,2) = volumeBinsV;
                            %dvhsAdded = dvhsAdded + 1;
                        end

                    end
                end
            end

        end

        %     case 'digitalFilm'
        %         populate_planC_digitalFilm_field(fieldName, dcmdir);
        %     case 'RTTreatment'
        %         populate_planC_RTTreatment_field(fieldName, dcmdir);
        %     case 'IM'
        %         populate_planC_IM_field(fieldName, dcmdir);

    case 'beams'
        [seriesC, typeC] = extract_all_series(dcmdir_patient);
        %%%DK Re-writing RTPLAN file to not use MATLAB image processing
        %%%tool.

        seriesNum = find(strncmpi('RTPLAN', typeC, 6));

        planCount = 0;

        for j = 1:length(seriesNum)
            RTPLAN = seriesC{seriesNum(j)}.Data;

            for planNum = 1:length(RTPLAN)

                planCount = planCount + 1;

                planobj  = scanfile_mldcm(RTPLAN(planNum).file);

                %Populate each field in the dose structure.
                for i = 1:length(names)
                    dataS(planCount).(names{i}) = populate_planC_beams_field(names{i}, RTPLAN(planNum), planobj);
                end

            end

        end
        rtPlans= dataS;

    case 'beamGeometry'

        dataS = initializeCERR('beamGeometry');

        for i = 1:length(rtPlans)
            beamGeometryS = populate_planC_beamGeometry_field(rtPlans(i), dataS);

            for j = 1:length(beamGeometryS)
                dataS = dissimilarInsert(dataS, beamGeometryS(j), length(dataS)+1);
            end
        end

        %%%OLD RTPLAN import code. commented DK?
        % %         %Place RTPLAN into planC{indexS.beams}
        % %         plansAdded = 0;
        % %         for seriesNum = 1:length(seriesC)
        % %             RTPLAN = seriesC{seriesNum}.Data;
        % %             for planNum =  1:length(RTPLAN)
        % %
        % %                 if strcmpi(typeC{seriesNum}, 'RTPLAN')
        % %
        % %                     try
        % %                         if plansAdded == 0m
        % %                             dataS =
        % dicominfo(seriesC{seriesNum}.Data(planNum).file);
        % %                         else
        % %                             dataS(plansAdded + 1) = dicominfo(seriesC{seriesNum}.Data(planNum).file);
        % %                         end
        % %                         plansAdded = plansAdded + 1;
        % %                         warning('Matlab''s Image Processing Toolbox was used to read RTPLAN...')
        % %                     catch
        % %                         warning('Matlab''s Image Processing Toolbox is required to read RTPLAN. Ignoring...')
        % %                     end
        % %
        % %                 end
        % %
        % %             end
        % %         end

    case 'GSPS'
        [seriesC, typeC]    = extract_all_series(dcmdir_patient);
        supportedTypes      = {'PR'};
        gspsAdded          = 0;

        %hWaitbar = waitbar(0,'Loading GSPS. Please wait...');

        numGspsSeries = length(find(strcmpi(typeC, 'PR')==1));

        %Place each structure into its own array element.
        for seriesNum = 1:length(seriesC)

            %if ismember(typeC{seriesNum}, supportedTypes)
            if strcmpi(typeC{seriesNum}, 'PR')
                GSPS = seriesC{seriesNum}.Data;
                for k = 1:length(GSPS)
                    gspsobj  = scanfile_mldcm(GSPS(k).file);

                    %Graphic Annotation Sequence.
                    el = gspsobj.getValue(7340033); %org.dcm4che3.data.Tag.GraphicAnnotationSequence; %hex2dec('00700001')

                    % ROI = strobj.getInt(org.dcm4che2.data.Tag.ROIContourSequence);

                    if ~isempty(el)
                        nGsps = el.size();
                    else
                        nGsps = 0;
                    end
                    curGspsNum = 1;
                    for j = 1:nGsps

                        %Populate each field in the structure field set
                        for i = 1:length(names)
                            dataS(gspsAdded+1).(names{i}) = populate_planC_gsps_field(names{i}, GSPS, curGspsNum, gspsobj);
                        end
                        curGspsNum = curGspsNum + 1;
                        gspsAdded = gspsAdded + 1;

                        %waitbar(gspsAdded/(nGsps*length(GSPS)*numGspsSeries), hWaitbar, 'Loading Annotations, Please wait...');

                    end
                end
            end

        end
        %close(hWaitbar);
        pause(0.1);

    case 'registration'
        [seriesC, typeC]    = extract_all_series(dcmdir_patient);
        supportedTypes      = {'REG'};
        registrationsAdded  = 0;
        frameOfRefUIDC      = {};
        %Place each REG into its own array element.
        for seriesNum = 1:length(seriesC)

            %             if ismember(typeC{seriesNum}, supportedTypes)
            if strcmpi(typeC{seriesNum}, 'REG')

                REG = seriesC{seriesNum}.Data; %wy RTDOSE{1} for import more than one dose files;
                for regNum = 1:length(REG)
                    regobj  = scanfile_mldcm(REG(regNum).file);

                    % Frame of Reference UID
                    %frameOfRefUID = getTagValue(regobj, '00200052');
                    frameOfRefUID = char(regobj.getStrings(2097234)); %org.dcm4che3.data.Tag.FrameOfReferenceUID;

                    %Populate each field in the dose structure.
                    for i = 1:length(names)
                        dataS(registrationsAdded+1).(names{i}) = populate_planC_registration_field(names{i}, REG(regNum), regobj);
                    end

                    if isempty(frameOfRefUIDC)
                        frameOfRefUIDC{1} = frameOfRefUID;
                    else
                        frameOfRefUIDC{end+1} = frameOfRefUID;
                    end

                    % Check if frame of reference UID matches any
                    % existing doses and add slices.

                    % If frame of reference UID does not match, then
                    % create a new dose
                    registrationsAdded = registrationsAdded + 1;
                end
            end

        end

    case 'importLog'
        %Implementation is unnecessary.

    case 'CERROptions'
        pathStr = getCERRPath;
        optName = [pathStr 'CERROptions.json'];
        dataS = opts4Exe(optName);

    case 'indexS'
        %Implementation is unnecessary.

    otherwise
        % disp(['DICOM Import has no methods defined for import into the planC{indexS.' cellName '} structure, leaving empty.']);
end

end

function [dataS,dosesAdded] = addDoseToSeries(doseS,dataS,dosesAdded)
if length(doseS) > 1
    doseArray = [];
    zValues = [];
    for slcNum = 1:length(doseS)
        doseArray(:,:,slcNum) = doseS(slcNum).doseArray;
        zValues(slcNum) = doseS(slcNum).zValues;
    end
    % Sort doseArray as per zValues
    [zValues,indSort] = sort(zValues);
    doseArray(:,:,1:end) = doseArray(:,:,indSort);
    doseS(1).doseArray = doseArray;
    doseS(1).zValues = zValues;
end
dosesAdded = dosesAdded + 1;

if isempty(dataS)
    dataS = doseS(1);
else
    dataS(dosesAdded) = doseS(1);
end

end


function set1001d(m)

% sets parameters of DT3157 according to
% http://cmp.felk.cvut.cz/cmp/hardware/pulnix.html#TM-1001digital
% for single camera directly connected to frame grabber

% m=openfg('DT3157');
   setfg(m,'FrameType','NonInterlaced')
   setfg(m,'CameraType',4);				% 8 bit mode
   setfg(m,'ClockSource','External')	%(switch camera to NSP)
   setfg(m,'SyncSentinel','Off')

   setfg(m,'ExtOnLoToHi',0)   % pixel clock sampling edge
   setfg(m,'LineOnLoToHi',0)  % line data valid after edge
   setfg(m,'FieldOnLoToHi',0) % frame data valid after edge

   setfg(m,'TotalPixPerLine',1280)
   setfg(m,'FirstActivePixel',142)
   setfg(m,'ActivePixelCount',1008)

   setfg(m,'TotalLinesperFld',1100)
   setfg(m,'FirstActiveLine',24)
   setfg(m,'ActiveLineCount',1017)

   setfg(m,'FrameLeft',0)
   setfg(m,'HorFrameInc',1)
   setfg(m,'FrameWidth',1008)
   setfg(m,'FrameTop',0)
   setfg(m,'VerFrameInc',1)
   setfg(m,'FrameHeight',1017)

   setfg(m,'digioconfig',255);
   setfg(m,'digitalio',255)


function startup()
% startup()
% --------------------------------------------------------
% Faster R-CNN
% Copyright (c) 2015, Shaoqing Ren
% Licensed under The MIT License [see LICENSE for details]
% --------------------------------------------------------

    curdir = fileparts(mfilename('fullpath'));
    addpath(genpath(fullfile(curdir, 'utils')));
    addpath(genpath(fullfile(curdir, 'functions')));
    addpath(genpath(fullfile(curdir, 'bin')));
    addpath(genpath(fullfile(curdir, 'experiments')));
    addpath(genpath(fullfile(curdir, 'imdb')));

    mkdir_if_missing(fullfile(curdir, 'datasets'));

    mkdir_if_missing(fullfile(curdir, 'external'));

    caffe_path = fullfile(curdir, 'external', 'caffe', 'matlab');
    if exist(caffe_path, 'dir') == 0
        error('matcaffe is missing from external/caffe/matlab; See README.md');
    end
    addpath(genpath(caffe_path));

    mkdir_if_missing(fullfile(curdir, 'imdb', 'cache'));

    mkdir_if_missing(fullfile(curdir, 'output'));

    mkdir_if_missing(fullfile(curdir, 'models'));

    fprintf('fast_rcnn startup done\n');
end


function [ out ] = opt_selectField( in, field )
% opt_selectField:
%
% Description:
%     This function takes fields of input data structure altogether
%
% Example:
%     field={'x','t','fs','y_dec','y_logic','y_class','class', 'chan'};
%     out=opt_selectField(EEG,field);
%
% Input:
%     in    - EEG data structure
%     field - Fields you want to take
% Output:
%     out   - Data structure with fields combined
%
% Min-Ho, Lee
% mhlee@image.korea.ac.kr
%

if isempty(field)
    out=struct;
else
    for i=1:length(field)
        if isfield(in,field{i})
            out.(field{i})=in.(field{i});
        end
    end

end

end


function kern = whiteblockKernExpandParam(kern, params)

% WHITEBLOCKKERNEXPANDPARAM Fill WHITEBLOCK kernel structure with params.
% FORMAT
% DESC returns a white noise block kernel structure filled with the
% parameters in the given vector. This is used as a helper function to
% enable parameters to be optimised in, for example, the NETLAB
% optimisation functions.
% ARG kern : the kernel structure in which the parameters are to be
% placed.
% ARG param : vector of parameters which are to be placed in the
% kernel structure.
% RETURN kern : kernel structure with the given parameters in the
% relevant locations.
%
% SEEALSO : whiteblockKernParamInit
%
% COPYRIGHT : Mauricio A. Alvarez, 2010

% KERN

kern.variance = params;


function test_bug2027

% MEM 2gb
% WALLTIME 00:20:00
% DEPENDENCY read_4d_hdr ft_read_header ft_channelselection

% Bug: ft_read_header returns only 152 MEG channels in the hdr.label
% This goes wrong at a low level, i.e. in read_4d_hdr

% reproduce
datasets = {
  dccnpath('/home/common/matlab/fieldtrip/data/test/bug2027/colorado/e,rfhp1.0Hz,COH')
  dccnpath('/home/common/matlab/fieldtrip/data/test/bug2027/glasgow/e,rfDC')
  dccnpath('/home/common/matlab/fieldtrip/data/test/bug2027/marseille/e,rfhp1.0Hz,COH')
  dccnpath('/home/common/matlab/fieldtrip/data/test/bug2027/stlouis/e,rfhp1.0Hz,COH')
  dccnpath('/home/common/matlab/fieldtrip/data/test/bug2027/konstanz/c,rfhp0.1Hz')
};

nummeg = [248 248 248 248 148];
for k = 1:numel(datasets)
  hdr = ft_read_header(dccnpath(datasets{k}));
  assert(numel(ft_channelselection('MEG',hdr.label))==nummeg(k));
end


function [count, failures] = estimate_failures(trajectory, sequence)
% estimate_failures Computes number of failures score
%
% Scans the trajectory for instances of tracker failure and returns the number of failures.
%
% Input:
% - trajectory (cell): A trajectory as a cell array of regions.
% - sequence (cell): A valid sequence descriptor.
%
% Output:
% - count (integer): Number of failures
% - failures (integer vector): Indices of frames where the tracker failed
%

failures = find(cellfun(@(x) numel(x) == 1 && x == 2, trajectory, 'UniformOutput', true));

count = numel(failures);


function c = horzcat(varargin)
% Implements the horzcat ([a b]) operator on image_vector objects across voxels.
% Requires that each object has an equal number of columns and voxels
%
% :Usage:
% ::
%
%    function s = horzcat(varargin)
%
% :Examples:
% ::
%
%    c = [dat1 dat2];
%
% ..
%    Programmer Notes
%    Created 3/14/14 by Luke Chang
% ..

error('This method is deprecated.  Use image_math instead.');

dat = [];
for i = 1:nargin
    %Check if image_vector object
    if ~isa(varargin{i}, 'image_vector')
        error('Input Data is not an image_vector object')
    end

    dat = [dat, varargin{i}.dat];
end

c = varargin{1};
c.dat = dat;


function value = p10_i4 ( action, name, value )

%*****************************************************************************80
%
%% P10_I4 sets or gets I4 parameters for problem 10.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 March 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string ACTION,
%    'D' to set a parameter to its default value.
%    'S' to set a parameter,
%    'G' to get a parameter,
%    'I' to increment a parameter.
%
%    Input, string NAME, the name of the variable.
%    '#' is the number of calls to the integrand routine.
%
%    Input/output, integer VALUE.
%    * If ACTION = 'I', then VALUE is an input quantity, and is the
%      new value to be added to NAME.
%    * If ACTION = 'S', then VALUE is an input quantity, and is the
%      new value to be assigned to NAME.
%    * If ACTION = 'G', then VALUE is an output quantity, and is the
%      current value of NAME.
%
  persistent calls;

  if ( action == 'D' | action == 'd' )

    if ( name == '#' | name == '*' )
      calls = 0;
    end

  elseif ( action == 'G' | action == 'g' )

    if ( name == '#' )
      value = calls;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P10_I4 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P10_I4 - Fatal error!' );
    end

  elseif ( action == 'I' | action == 'i' )

    if ( name == '#' )
      calls = calls + value;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P10_I4 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P10_I4 - Fatal error!' );
    end

  elseif ( action == 'S' | action == 's' )

    if ( name == '#' )
      calls = value;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P10_I4 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P10_I4 - Fatal error!' );
    end

  else

    fprintf ( 1, '\n' );
    fprintf ( 1, 'P10_I4 - Fatal error!\n' );
    fprintf ( 1, '  Unrecognized action = "%s".\n', action );
    error ( 'P10_I4 - Fatal error!' );

  end

  return
end


function varargout = nii_tool(cmd, varargin)
% Basic function to create, load and save NIfTI file.
%
% rst = nii_tool('cmd', para);
%
% To list all command, type
%  nii_tool ?
%
% To get help information for each command, include '?' in cmd, for example:
%  nii_tool init?
%  nii_tool('init?')
%
% Here is a list of all command:
%
% nii_tool('default', 'version', 1, 'rgb_dim', 1);
% nii = nii_tool('init', img);
% nii = nii_tool('update', nii, mat);
% nii_tool('save', nii, filename, force_3D);
% hdr = nii_tool('hdr', filename);
% img = nii_tool('img', filename_or_hdr);
% ext = nii_tool('ext', filename_or_hdr);
% nii = nii_tool('load', filename_or_hdr);
% nii = nii_tool('cat3D', filenames);
% nii_tool('RGBStyle', 'afni');
%
% Detail for each command is described below.
%
% oldVal = nii_tool('default', 'version', 1, 'rgb_dim', 1);
% oldVal = nii_tool('default', struct('version', 1, 'rgb_dim', 1));
%
% - Set/query default NIfTI version and/or rgb_dim. To check the setting, run
% nii_tool('default') without other input. The input for 'default' command can
% be either a struct with fields of 'version' and/or 'rgb_dim', or
% parameter/value pairs. See nii_tool('RGBstyle') for meaning of rgb_dim.
%
% Note that the setting will be saved for future use. If one wants to change the
% settting temporarily, it is better to return the oldVal, and to restore it
% after done:
%
%  oldVal = nii_tool('default', 'version', 2); % set version 2 as default
%  % 'init' and 'save' NIfTI using above version
%  nii_tool('default', 'version', oldVal); % restore default setting
%
% The default version setting affects 'init' command only. If you 'load' a NIfTI
% file, modify it, and then 'save' it, the version will be the same as the
% original file, unless it is changed explicitly (see help for 'save' command).
% All 'load' command ('load', 'hdr', 'ext', 'img') will read any version
% correctly, regardless of version setting.
%
%
% nii = nii_tool('init', img, RGB_dim);
%
% - Initialize nii struct based on img, normally 3D or 4D array. Most fields in
% the returned nii.hdr contain default values, and need to be updated based on
% dicom or other information. Important ones include pixdim, s/qform_code and
% related parameters.
%
% The NIfTI datatype will depend on data type of img. Most Matlab data types are
% supported, including 8/16/32/64 bit signed and unsigned integers, single and
% double floating numbers. Single/double complex and logical array are also
% supported.
%
% The optional third input, RGB_dim, is needed only if img contains RGB/RGBA
% data. It specifies which dimension in img encodes RGB or RGBA. In other words,
% if a non-empty RGB_dim is provided, img will be interpreted as RGB/RGBA data.
%
% Another way to signify RGB/RGBA data is to permute color dim to 8th-dim of img
% (RGB_dim of 8 can be omitted then). Since NIfTI img can have up to 7 dim,
% nii_tool chooses to store RGB/RGBA in 8th dim. Although this looks lengthy
% (4th to 7th dim are often all ones), nii_tool can deal with up to 7 dim
% without causing any confusion. This is why the returned nii.img always stores
% RGB in 8th dim.
%
%
% nii = nii_tool('update', nii, mat);
%
% - Update nii.hdr according to nii.img. This is useful if one changes nii.img
% type or dimension. The 'save' command calls this internally, so it is not
% necessary to call this before 'save'. A useful case to call 'update' is that
% one likes to use nii struct without saving it to a file, and 'update' will
% make nii.hdr.dim and others correct.
%
% If the 3rd input, new transformation matrix is provided, it will be set as the
% sform transformation matrix.
%
%
% hdr = nii_tool('hdr', filename);
%
% - Return hdr struct of the provided NIfTI file. This is useful to check NIfTI
% hdr, and it is much faster than 'load', especially for .gz file.
%
%
% img = nii_tool('img', filename_or_hdr);
%
% - Return image data in a NIfTI file. The second input can be NIfTI file name,
% or hdr struct returned by nii_tool('hdr', filename).
%
%
% ext = nii_tool('ext', filename_or_hdr);
%
% - Return NIfTI extension in a NIfTI file. The second input can be NIfTI file
% name, or hdr struct returned by nii_tool('hdr', filename). The returned ext
% will have field 'edata_decoded' if 'ecode' is of known type, such as dicom
% (2), text (4 or 6) or Matlab (40).
%
% Here is an example to add data in myFile.mat as extension to nii struct, which
% can be from 'init' or 'load':
%
%  fid = fopen('myFile.mat'); % open the MAT file
%  myEdata = fread(fid, inf, '*uint8'); % load all bytes as byte column
%  fclose(fid);
%  len = int32(numel(myEdata)); % number of bytes in int32
%  myEdata = [typecast(len, 'uint8')'; myEdata]; % include len in myEdata
%  nii.ext.ecode = 40; % 40 for Matlab extension
%  nii.ext.edata = myEdata; % myEdata must be uint8 array
%
% nii_tool will take care of rest when you 'save' nii to a file.
%
% In case a NIfTI ext causes problem (for example, some FSL builds have problem
% in reading NIfTI img with ecode>30), one can remove the ext easily:
%
%  nii = nii_tool('load', 'file_with_ext.nii'); % load the file with ext
%  nii.ext = []; % or nii = rmfield(nii, 'ext'); % remove ext
%  nii_tool('save', nii, 'file_without_ext.nii'); % save it
%
%
% nii = nii_tool('load', filename_or_hdr);
%
% - Load NIfTI file into nii struct. The returned struct includes NIfTI 'hdr'
% and 'img', as well as 'ext' if the file contains NIfTI extension.
%
% nii_tool returns nii.img with the same data type as stored in the file, while
% numeric values in hdr are in double precision for convenience.
%
%
% nii_tool('save', nii, filename, force_3D);
%
% - Save struct nii into filename. The format of the file is determined by the
% file extension, such as .img, .nii, .img.gz, .nii.gz etc. If filename is not
% provided, nii.hdr.file_name must contain a file name. Note that 'save' command
% always overwrites file in case of name conflict.
%
% If filename has no extension, '.nii' will be used as default.
%
% If the 4th input, force_3D, is true (default false), the output file will be
% 3D only, which means multiple volume data will be split into multiple files.
% This is the format SPM likes. You can use this command to convert 4D into 3D
% by 'load' a 4D file, then 'save' it as 3D files. The 3D file names will have
% 5-digit like '_00001' appended to indicate volume index.
%
% The NIfTI version can be set by nii_tool('default'). One can override the
% default version by specifying it in nii.hdr.version. To convert between
% versions, load a NIfTI file, specify new version, and save it. For example:
%
%  nii = nii_tool('load', 'file_nifti1.nii'); % load version 1 file
%  nii.hdr.version = 2; % force to NIfTI-2
%  nii_tool('save', nii, 'file_nifti2.nii'); % save as version 2 file
%
% Following example shows how to change data type of a nii file:
%  nii = nii_tool('load', 'file_int16.nii'); % load int16 type file
%  nii.img = single(nii.img); % change data type to single/float32
%  nii_tool('save', nii, 'file_float.nii'); % nii_tool will take care of hdr
%
%
% nii = nii_tool('cat3D', files);
%
% - Concatenate SPM 3D files into a 4D dataset. The input 'files' can be cellstr
% with file names, or char with wildcards (* or ?). If it is cellstr, the volume
% order in the 4D data corresponds to those files. If wildcards are used, the
% volume order is based on alphabetical order of file names.
%
% Note that the files to be concatenated must have the same datatype, dim, voxel
% size, scaling slope and intercept, transformation matrix, etc. This is
% normally true if files are for the same dicom series.
%
% Following example shows how to convert a series of 3D files into a 4D file:
%
%  nii = nii_tool('cat3D', './data/fSubj2-0003*.nii'); % load files for series 3
%  nii_tool('save', nii, './data/fSubj2-0003_4D.nii'); % save as a 4D file
%
%
% oldStyle = nii_tool('RGBStyle', 'afni');
%
% - Set/query the method to read/save RGB or RGBA NIfTI file. The default method
% can be set by nii_tool('default', 'rgb_dim', dimN), where dimN can be 1, 3 or
% 4, or 'afni', 'mricron' or 'fsl', as explained below.
%
% The default is 'afni' style (or 1), which is defined by NIfTI standard, but is
% not well supported by fslview till v5.0.8 or mricron till v20140804.
%
% If the second input is set to 'mricron' (or 3), nii_tool will save file using
% the old RGB fashion (dim 3 for RGB). This works for mricron v20140804 or
% earlier.
%
% If the second input is set to 'fsl' (or 4), nii_tool will save RGB or RGBA
% layer into 4th dimension, and the file is not encoded as RGB data, but as
% normal 4D NIfTI. This violates the NIfTI rule, but it seems it is the only way
% to work for fslview (at least till fsl v5.0.8).
%
% If no new style (second input) is provided, it means to query the current
% style (one of 'afni', 'mricron' and 'fsl').
%
% The GUI method to convert between different RGB style can be found in
% nii_viewer. Following shows how to convert other style into fsl style:
%
%  nii_tool('RGBStyle', 'afni'); % we are loading afni style RGB
%  nii = nii_tool('load', 'afni_style.nii'); % load RGB file
%  nii_tool('RGBStyle', 'fsl'); % switch to fsl style for later save
%  nii_tool('save', nii, 'fslRGB.nii'); % fsl can read it as RGB
%
% Note that, if one wants to convert fsl style (non-RGB file by NIfTI standard)
% to other styles, an extra step is needed to change the RGB dim from 4th to 8th
% dim before 'save':
%
%  nii = nii_tool('load', 'fslStyleFile.nii'); % it is normal NIfTI
%  nii.img = permute(nii.img, [1:3 5:8 4]); % force it to be RGB data
%  nii_tool('RGBStyle', 'afni'); % switch to NIfTI RGB style if needed
%  nii_tool('save', nii, 'afni_RGB.nii'); % now AFNI can read it as RGB
%
% Also note that the setting by nii_tool('RGBStyle') is effective only for
% current Matlab session. If one clears all or starts a new Matlab session, the
% default style by nii_tool('default') will take effect.
%
% See also NII_VIEWER, NII_XFORM, DICM2NII

% More information for NIfTI format:
% Official NIfTI website: http://nifti.nimh.nih.gov/
% Another excellent site: http://brainder.org/2012/09/23/the-nifti-file-format/

% History (yymmdd)
% 150109 Write it based on Jimmy Shen's NIfTI tool (xiangrui.li@gmail.com)
% 150202 Include renamed pigz files for Windows
% 150203 Fix closeFile and deleteTmpFile order
% 150205 Add hdr.machine: needed for .img fopen
% 150208 Add 4th input for 'save', allowing to save SPM 3D files
% 150210 Add 'cat3D' to load SPM 3D files
% 150226 Assign all 8 char for 'magic' (version 2 needs it)
% 150321 swapbytes(nByte) for ecode=40 with big endian
% 150401 Add 'default' to set/query version and rgb_dim default setting
% 150514 read_ext: decode txt edata by dicm2nii.m
% 150517 func_handle: provide a way to use gunzipOS etc from outside
% 150617 auto detect rgb_dim 1&3 for 'load' etc using ChrisR method
% 151025 Change subfunc img2datatype as 'update' for outside access
% 151109 Include dd.exe from WinAVR-20100110 for partial gz unzip
% 151205 Partial gunzip: fix fname with space & unknown pigz | dd error.
% 151222 Take care of img for intent_code 2003/2004: anyone uses it?
% 160110 Use matlab pref method to replace para file.
% 160120 check_gzip: use "" for included pigz; ignore dd error if err is false.
% 160326 fix setpref for older Octave: set each parameter separately.
% 160531 fopen uses 'W' for 'w': performance benefit according to Yair.
% 160701 subFuncHelp: bug fix for mfile case.
% 161018 gunzipOS: use unique name for outName, to avoid problem with parfor.
% 161025 Make included linux pigz executible; fix "dd" for windows.
% 161031 gunzip_mem(), nii_bytes() for hdr/ext read: read uint8 then parse;
%        Replace hdr.machine with hdr.swap_endian.
% 170212 Extract decode_ext() from 'ext' cmd so call it in 'update' cmd.
% 170215 gunzipOS: use -c > rather than copyfile for performance.
% 170322 gzipOS: stop using background gz to avoid file not exist error.
% 170410 read_img(): turn off auto RGB dim detection, and use rgb_dim.
% 170714 'save': force to version 2 if img dim exceeds 2^15-1.
% 170716 Add functionSignatures.json file for tab auto-completion.
% 171031 'LocalFunc' makes eaiser to call local functions.
% 171206 Allow file name ext other than .nii, .hdr, .img.
% 180104 check_gzip: add /usr/local/bin to PATH for unix if needed.
% 180119 use jsystem for better speed.
% 180710 bug fix for cal_max/cal_min in 'update'.
% 210302 take care of unicode char in hdr (Thx Yong).

persistent C para; % C columns: name, length, format, value, offset
if isempty(C)
    [C, para] = niiHeader;
    if exist('OCTAVE_VERSION', 'builtin')
        warning('off', 'Octave:fopen-mode'); % avoid 'W' warning
        more off;
    end
end

if ~ischar(cmd)
    error('Provide a string command as the first input for nii_tool');
end
if any(cmd=='?'), subFuncHelp(mfilename, cmd); return; end

if strcmpi(cmd, 'init')
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end
    nii.hdr = cell2struct(C(:,4), C(:,1));
    nii.img = varargin{1};
    if numel(size(nii.img))>8
        error('NIfTI img can have up to 7 dimension');
    end
    if nargin>2
        i = varargin{2};
        if i<0 || i>8 || mod(i,1)>0, error('Invalid RGB_dim number'); end
        nii.img = permute(nii.img, [1:i-1 i+1:8 i]); % RGB to dim8
    end
    nii.hdr.file_name = inputname(2);
    if isempty(nii.hdr.file_name), nii.hdr.file_name = 'tmpNII'; end
    varargout{1} = nii_tool('update', nii); % set datatype etc

elseif strcmpi(cmd, 'save')
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end
    nii = varargin{1};
    if ~isstruct(nii) || ~isfield(nii, 'hdr') || ~isfield(nii, 'img')
        error(['nii_tool(''save'') needs a struct from nii_tool(''init'')' ...
            ' or nii_tool(''load'') as the second input']);
    end

    % Check file name to save
    if nargin>2
        fname = varargin{2};
        if ~ischar(fname), error('Invalid name for NIfTI file: %s', fname); end
    elseif isfield(nii.hdr, 'file_name')
        fname = nii.hdr.file_name;
    else
        error('Provide a valid file name as the third input');
    end
    if ~ispc && strncmp(fname, '~/', 2) % matlab may err with this abbrevation
        fname = [getenv('HOME') fname(2:end)];
    end
    [pth, fname, fext] = fileparts(fname);
    do_gzip = strcmpi(fext, '.gz');
    if do_gzip
        [~, fname, fext] = fileparts(fname); % get .nii .img .hdr
    end
    if isempty(fext), fext = '.nii'; end % default single .nii file
    fname = fullfile(pth, fname); % without file ext
    if nargout, varargout{1} = []; end
    isNii = strcmpi(fext, '.nii'); % will use .img/.hdr if not .nii

    % Deal with NIfTI version and sizeof_hdr
    niiVer = para.version;
    if isfield(nii.hdr, 'version'), niiVer = nii.hdr.version; end
    if niiVer<2 && any(nii.hdr.dim(2:end) > 32767), niiVer = 2; end

    if niiVer == 1
        nii.hdr.sizeof_hdr = 348; % in case it was loaded from other version
    elseif niiVer == 2
        nii.hdr.sizeof_hdr = 540; % version 2
    else
        error('Unsupported NIfTI version: %g', niiVer);
    end

    if niiVer ~= para.version
        C0 = niiHeader(niiVer);
    else
        C0 = C;
    end

    % Update datatype/bitpix/dim in case nii.img is changed
    [nii, fmt] = nii_tool('update', nii);

    % This 'if' block: lazy implementation to split into 3D SPM files
    if nargin>3 && ~isempty(varargin{3}) && varargin{3} && nii.hdr.dim(5)>1
        if do_gzip, fext = [fext '.gz']; end
        nii0 = nii;
        for i = 1:nii.hdr.dim(5)
            fname0 = sprintf('%s_%05g%s', fname, i, fext);
            nii0.img = nii.img(:,:,:,i,:,:,:,:); % one vol
            if i==1 && isfield(nii, 'ext'), nii0.ext = nii.ext;
            elseif i==2 && isfield(nii0, 'ext'), nii0 = rmfield(nii0, 'ext');
            end
            nii_tool('save', nii0, fname0);
        end
        return;
    end

    % re-arrange img for special datatype: RGB/RGBA/Complex.
    if any(nii.hdr.datatype == [128 511 2304]) % RGB or RGBA
        if para.rgb_dim == 1 % AFNI style
            nii.img = permute(nii.img, [8 1:7]);
        elseif para.rgb_dim == 3 % old mricron style
            nii.img = permute(nii.img, [1 2 8 3:7]);
        elseif para.rgb_dim == 4 % for fslview
            nii.img = permute(nii.img, [1:3 8 4:7]); % violate nii rule
            dim = size(nii.img);
            if numel(dim)>6 % dim7 is not 1
                i = find(dim(5:7)==1, 1, 'last') + 4;
                nii.img = permute(nii.img, [1:i-1 i+1:8 i]);
            end
            nii = nii_tool('update', nii);  % changed to non-RGB datatype
        end
    elseif any(nii.hdr.datatype == [32 1792]) % complex single/double
        nii.img = [real(nii.img(:))'; imag(nii.img(:))'];
    end

    % Check nii extension: update esize to x16
    nExt = 0; esize = 0;
    nii.hdr.extension = [0 0 0 0]; % no nii ext
    if isfield(nii, 'ext') && isstruct(nii.ext) ...
            && isfield(nii.ext(1), 'edata') && ~isempty(nii.ext(1).edata)
        nExt = numel(nii.ext);
        nii.hdr.extension = [1 0 0 0]; % there is nii ext
        for i = 1:nExt
            if ~isfield(nii.ext(i), 'ecode') || ~isfield(nii.ext(i), 'edata')
                error('NIfTI header ext struct must have ecode and edata');
            end

            n0 = numel(nii.ext(i).edata) + 8; % 8 byte for esize and ecode
            n1 = ceil(n0/16) * 16; % esize: multiple of 16
            nii.ext(i).esize = n1;
            nii.ext(i).edata(end+(1:n1-n0)) = 0; % pad zeros
            esize = esize + n1;
        end
    end

    % Set magic, vox_offset, and open file for .nii or .hdr
    if isNii
        % version 1 will take only the first 4
        nii.hdr.magic = sprintf('n+%g%s', niiVer, char([0 13 10 26 10]));
        nii.hdr.vox_offset = nii.hdr.sizeof_hdr + 4 + esize;
        fid = fopen(strcat(fname, fext), 'W');
    else
        nii.hdr.magic = sprintf('ni%g%s', niiVer, char([0 13 10 26 10]));
        nii.hdr.vox_offset = 0;
        fid = fopen(strcat(fname, '.hdr'), 'W');
    end

    % Write nii hdr
    for i = 1:size(C0,1)
        if isfield(nii.hdr, C0{i,1})
            val = nii.hdr.(C0{i,1});
        else % niiVer=2 omit some fields, also take care of other cases
            val = C0{i,4};
        end
        fmt0 = C0{i,3};
        if strcmp(C0{i,3}, 'char') && ~isempty(val)
            if ~ischar(val), val = char(val); end % avoid val=[] error etc
            val = unicode2native(val); % may have more bytes than numel(val)
            fmt0 = 'uint8';
        end
        n = numel(val);
        len = C0{i,2};
        if n>len
            val(len+1:n) = []; % remove extra, normally for char
        elseif n<len
            val(n+1:len) = 0; % pad 0, normally for char
        end
        fwrite(fid, val, fmt0);
    end

    % Write nii ext: extension is in hdr
    for i = 1:nExt % nExt may be 0
        fwrite(fid, nii.ext(i).esize, 'int32');
        fwrite(fid, nii.ext(i).ecode, 'int32');
        fwrite(fid, nii.ext(i).edata, 'uint8');
    end

    if isNii
        n = nii.hdr.vox_offset - ftell(fid);
        if n<0 % seen n=-1 for unknown reason
            fseek(fid, n, 'cof');
        elseif n>0
            fwrite(fid, zeros(n,1), 'uint8');
        end
    else
        fclose(fid); % done with .hdr
        fid = fopen(strcat(fname, '.img'), 'W');
    end

    % Write nii image
    fwrite(fid, nii.img, fmt);
    fclose(fid); % all written

    % gzip if asked
    if do_gzip
        if isNii
            gzipOS(strcat(fname, '.nii'));
        else
            gzipOS(strcat(fname, '.hdr')); % better not to compress .hdr
            gzipOS(strcat(fname, '.img'));
        end
    end

elseif strcmpi(cmd, 'hdr')
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end
    if ~ischar(varargin{1})
        error('nii_tool(''hdr'') needs nii file name as second input');
    end

    fname = nii_name(varargin{1}, '.hdr'); % get .hdr if it is .img
    [b, fname] = nii_bytes(fname, 600); % v2: 544+10 gzip header
    varargout{1} = read_hdr(b, C, fname);

elseif any(strcmpi(cmd, {'img' 'load'}))
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end

    if ischar(varargin{1}) % file name input
        fname = nii_name(varargin{1}, '.hdr');
        nii = struct;
    elseif isstruct(varargin{1}) && isfield(varargin{1}, 'file_name')
        nii.hdr = varargin{1};
        fname = nii.hdr.file_name;
    else
        error(['nii_tool(''%s'') needs a file name or hdr struct from ' ...
            'nii_tool(''hdr'') as second input'], cmd);
    end

    if strcmpi(cmd, 'load')
    	[ext, nii.hdr] = nii_tool('ext', varargin{1});
        if ~isempty(ext), nii.ext = ext; end
    elseif ~isfield(nii, 'hdr')
    	nii.hdr = nii_tool('hdr', fname);
    end

    nii.img = read_img(nii.hdr, para);
    if strcmpi(cmd, 'load')
        varargout{1} = nii;
    else % img
        varargout{1} = nii.img;
    end

elseif strcmpi(cmd, 'ext')
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end

    if ischar(varargin{1}) % file name input
        fname = nii_name(varargin{1}, '.hdr');
        hdr = nii_tool('hdr', fname);
    elseif isstruct(varargin{1}) && isfield(varargin{1}, 'file_name')
        hdr = varargin{1};
        fname = hdr.file_name;
    else
        error(['nii_tool(''%s'') needs a file name or hdr struct from ' ...
            'nii_tool(''hdr'') as second input'], cmd);
    end

    if isempty(hdr.extension) || hdr.extension(1)==0
        varargout{1} = [];
    else
        if hdr.vox_offset>0, nByte = hdr.vox_offset + 64; % .nii arbituary +64
        else, nByte = inf;
        end
        b = nii_bytes(fname, nByte);
        varargout{1} = read_ext(b, hdr);
    end
    if nargout>1, varargout{2} = hdr; end

elseif strcmpi(cmd, 'RGBStyle')
    styles = {'afni' '' 'mricron' 'fsl'};
    curStyle = styles{para.rgb_dim};
    if nargin<2, varargout{1} = curStyle; return; end % query only
    irgb = varargin{1};
    if isempty(irgb), irgb = 1; end % default as 'afni'
    if ischar(irgb)
        if strncmpi(irgb, 'fsl', 3), irgb = 4;
        elseif strncmpi(irgb, 'mricron', 4), irgb = 3;
        else, irgb = 1;
        end
    end
    if ~any(irgb == [1 3 4])
        error('nii_tool(''RGBStyle'') can have 1, 3, or 4 as second input');
    end
    if nargout, varargout{1} = curStyle; end % return old one
    para.rgb_dim = irgb; % no save to pref

elseif strcmpi(cmd, 'cat3D')
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end
    fnames = varargin{1};
    if ischar(fnames) % guess it is like run1*.nii
        f = dir(fnames);
        f = sort({f.name});
        fnames = strcat([fileparts(fnames) '/'], f);
    end

    n = numel(fnames);
    if n<2 || (~iscellstr(fnames) && (exist('strings', 'builtin') && ~isstring(fnames)))
        error('Invalid input for nii_tool(''cat3D''): %s', varargin{1});
    end

    nii = nii_tool('load', fnames{1}); % all for first file
    nii.img(:,:,:,2:n) = 0; % pre-allocate
    % For now, omit all consistence check between files
    for i = 2:n, nii.img(:,:,:,i) = nii_tool('img', fnames{i}); end
    varargout{1} = nii_tool('update', nii); % update dim

elseif strcmpi(cmd, 'default')
    flds = {'version' 'rgb_dim'}; % may add more in the future
    pf = getpref('nii_tool_para');
    for i = 1:numel(flds), val.(flds{i}) = pf.(flds{i}); end
    if nargin<2, varargout{1} = val; return; end % query only
    if nargout, varargout{1} = val; end % return old val
    in2 = varargin;
    if ~isstruct(in2), in2 = struct(in2{:}); end
    nam = fieldnames(in2);
    for i = 1:numel(nam)
        ind = strcmpi(nam{i}, flds);
        if isempty(ind), continue; end
        para.(flds{ind}) = in2.(nam{i});
        setpref('nii_tool_para', flds{ind}, in2.(nam{i}));
    end
    if val.version ~= para.version, C = niiHeader(para.version); end

elseif strcmpi(cmd, 'update') % old img2datatype subfunction
    if nargin<2, error('nii_tool(''%s'') needs second input', cmd); end
    nii = varargin{1};
    if ~isstruct(nii) || ~isfield(nii, 'hdr') || ~isfield(nii, 'img')
        error(['nii_tool(''update'') needs a struct from nii_tool(''init'')' ...
            ' or nii_tool(''load'') as the second input']);
    end

    dim = size(nii.img);
    ndim = numel(dim);
    dim(ndim+1:7) = 1;

    if nargin>2 % set new sform mat
        R = varargin{2};
        if size(R,2)~=4, error('Invalid matrix dimension.'); end
        nii.hdr.srow_x = R(1,:);
        nii.hdr.srow_y = R(2,:);
        nii.hdr.srow_z = R(3,:);
    end

    if ndim == 8 % RGB/RGBA data. Change img type to uint8/single if needed
        valpix = dim(8);
        if valpix == 4 % RGBA
            typ = 'RGBA'; % error info only
            nii.img = uint8(nii.img); % NIfTI only support uint8 for RGBA
        elseif valpix == 3 % RGB, must be single or uint8
            typ = 'RGB';
            if max(nii.img(:))>1, nii.img = uint8(nii.img);
            else, nii.img = single(nii.img);
            end
        else
            error('Color dimension must have length of 3 for RGB or 4 for RGBA');
        end

        dim(8) = []; % remove color-dim so numel(dim)=7 for nii.hdr
        ndim = find(dim>1, 1, 'last'); % update it
    elseif isreal(nii.img)
        typ = 'real';
        valpix = 1;
    else
        typ = 'complex';
        valpix = 2;
    end

    if islogical(nii.img), imgFmt = 'ubit1';
    else, imgFmt = class(nii.img);
    end
    ind = find(strcmp(para.format, imgFmt) & para.valpix==valpix);

    if isempty(ind) % only RGB and complex can have this problem
        error('nii_tool does not support %s image of ''%s'' type', typ, imgFmt);
    elseif numel(ind)>1 % unlikely
        error('Non-unique datatype found for %s image of ''%s'' type', typ, imgFmt);
    end

    fmt = para.format{ind};
    nii.hdr.datatype = para.datatype(ind);
    nii.hdr.bitpix = para.bitpix(ind);
    nii.hdr.dim = [ndim dim];

    mx = double(max(nii.img(:)));
    mn = double(min(nii.img(:)));
    if nii.hdr.cal_min>mx || nii.hdr.cal_max<mn % reset wrong value
        nii.hdr.cal_min = 0;
        nii.hdr.cal_max = 0;
    end

    if nii.hdr.sizeof_hdr == 348
        nii.hdr.glmax = round(mx); % we may remove these
        nii.hdr.glmin = round(mn);
    end

    if isfield(nii, 'ext')
        try swap = nii.hdr.swap_endian; catch, swap = false; end
        nii.ext = decode_ext(nii.ext, swap);
    end

    varargout{1} = nii;
    if nargout>1, varargout{2} = fmt; end
elseif strcmp(cmd, 'func_handle') % make a local function avail to outside
    varargout{1} = str2func(varargin{1});
elseif strcmp(cmd, 'LocalFunc') % call  local function from outside
    [varargout{1:nargout}] = feval(varargin{:});
else
    error('Invalid command for nii_tool: %s', cmd);
end
% End of main function

%% Subfunction: all nii header in the order in NIfTI-1/2 file
function [C, para] = niiHeader(niiVer)
pf = getpref('nii_tool_para');
if isempty(pf)
    setpref('nii_tool_para', 'version', 1);
    setpref('nii_tool_para', 'rgb_dim', 1);
    pf = getpref('nii_tool_para');
end
if nargin<1 || isempty(niiVer), niiVer = pf.version; end

if niiVer == 1
    C = {
    % name              len  format     value           offset
    'sizeof_hdr'        1   'int32'     348             0
    'data_type'         10  'char'      ''              4
    'db_name'           18  'char'      ''              14
    'extents'           1   'int32'     16384           32
    'session_error'     1   'int16'     0               36
    'regular'           1   'char'      'r'             38
    'dim_info'          1   'uint8'     0               39
    'dim'               8   'int16'     ones(1,8)       40
    'intent_p1'         1   'single'    0               56
    'intent_p2'         1   'single'    0               60
    'intent_p3'         1   'single'    0               64
    'intent_code'       1   'int16'     0               68
    'datatype'          1   'int16'     0               70
    'bitpix'            1   'int16'     0               72
    'slice_start'       1   'int16'     0               74
    'pixdim'            8   'single'    zeros(1,8)      76
    'vox_offset'        1   'single'    352             108
    'scl_slope'         1   'single'    1               112
    'scl_inter'         1   'single'    0               116
    'slice_end'         1   'int16'     0               120
    'slice_code'        1   'uint8'     0               122
    'xyzt_units'        1   'uint8'     0               123
    'cal_max'           1   'single'    0               124
    'cal_min'           1   'single'    0               128
    'slice_duration'    1   'single'    0               132
    'toffset'           1   'single'    0               136
    'glmax'             1   'int32'     0               140
    'glmin'             1   'int32'     0               144
    'descrip'           80  'char'      ''              148
    'aux_file'          24  'char'      ''              228
    'qform_code'        1   'int16'     0               252
    'sform_code'        1   'int16'     0               254
    'quatern_b'         1   'single'    0               256
    'quatern_c'         1   'single'    0               260
    'quatern_d'         1   'single'    0               264
    'qoffset_x'         1   'single'    0               268
    'qoffset_y'         1   'single'    0               272
    'qoffset_z'         1   'single'    0               276
    'srow_x'            4   'single'    [1 0 0 0]       280
    'srow_y'            4   'single'    [0 1 0 0]       296
    'srow_z'            4   'single'    [0 0 1 0]       312
    'intent_name'       16  'char'      ''              328
    'magic'             4   'char'      'n+1'           344
    'extension'         4   'uint8'     [0 0 0 0]       348
    };

elseif niiVer == 2
    C = {
    'sizeof_hdr'        1   'int32'     540             0
    'magic'             8   'char'      'n+2'           4
    'datatype'          1   'int16'     0               12
    'bitpix'            1   'int16'     0               14
    'dim'               8   'int64'     ones(1,8)       16
    'intent_p1'         1   'double'    0               80
    'intent_p2'         1   'double'    0               88
    'intent_p3'         1   'double'    0               96
    'pixdim'            8   'double'    zeros(1,8)      104
    'vox_offset'        1   'int64'     544             168
    'scl_slope'         1   'double'    1               176
    'scl_inter'         1   'double'    0               184
    'cal_max'           1   'double'    0               192
    'cal_min'           1   'double'    0               200
    'slice_duration'    1   'double'    0               208
    'toffset'           1   'double'    0               216
    'slice_start'       1   'int64'     0               224
    'slice_end'         1   'int64'     0               232
    'descrip'           80  'char'      ''              240
    'aux_file'          24  'char'      ''              320
    'qform_code'        1   'int32'     0               344
    'sform_code'        1   'int32'     0               348
    'quatern_b'         1   'double'    0               352
    'quatern_c'         1   'double'    0               360
    'quatern_d'         1   'double'    0               368
    'qoffset_x'         1   'double'    0               376
    'qoffset_y'         1   'double'    0               384
    'qoffset_z'         1   'double'    0               392
    'srow_x'            4   'double'    [1 0 0 0]       400
    'srow_y'            4   'double'    [0 1 0 0]       432
    'srow_z'            4   'double'    [0 0 1 0]       464
    'slice_code'        1   'int32'     0               496
    'xyzt_units'        1   'int32'     0               500
    'intent_code'       1   'int32'     0               504
    'intent_name'       16  'char'      ''              508
    'dim_info'          1   'uint8'     0               524
    'unused_str'        15  'char'      ''              525
    'extension'         4   'uint8'     [0 0 0 0]       540
    };
else
    error('Nifti version %g is not supported', niiVer);
end
if nargout<2, return; end

%   class      datatype bitpix  valpix
D = {
    'ubit1'     1       1       1 % neither mricron nor fsl support this
    'uint8'     2       8       1
    'int16'     4       16      1
    'int32'     8       32      1
    'single'    16      32      1
    'single'    32      64      2 % complex
    'double'    64      64      1
    'uint8'     128     24      3 % RGB
    'int8'      256     8       1
    'single'    511     96      3 % RGB, not in NIfTI standard?
    'uint16'    512     16      1
    'uint32'    768     32      1
    'int64'     1024    64      1
    'uint64'    1280    64      1
%   'float128'  1536    128     1 % long double, for 22nd century?
    'double'    1792    128     2 % complex
%   'float128'  2048    256     2 % long double complex
    'uint8'     2304    32      4 % RGBA
    };

para.format   =  D(:,1)';
para.datatype = [D{:,2}];
para.bitpix   = [D{:,3}];
para.valpix   = [D{:,4}];
para.rgb_dim  = pf.rgb_dim; % dim of RGB/RGBA in NIfTI FILE
para.version  = niiVer;

%% Subfunction: use pigz or system gzip if available (faster)
function gzipOS(fname)
persistent cmd; % command to gzip
if isempty(cmd)
    cmd = check_gzip('gzip');
    if ischar(cmd)
        cmd = @(nam){cmd '-nf' nam};
    elseif islogical(cmd) && ~cmd
        fprintf(2, ['None of system pigz, gzip or Matlab gzip available. ' ...
            'Files are not compressed into gz.\n']);
    end
end

if islogical(cmd)
    if cmd, gzip(fname); deleteFile(fname); end
    return;
end

[err, str] = jsystem(cmd(fname));
if err && ~exist(strcat(fname, '.gz'), 'file')
    try
        gzip(fname); deleteFile(fname);
    catch
        fprintf(2, 'Error during compression: %s\n', str);
    end
end

%% Deal with pigz/gzip on path or in nii_tool folder, and matlab gzip/gunzip
function cmd = check_gzip(gz_unzip)
m_dir = fileparts(mfilename('fullpath'));
% away from pwd, so use OS pigz if both exist. Avoid error if pwd changed later
if strcmpi(pwd, m_dir), cd ..; clnObj = onCleanup(@() cd(m_dir)); end
if isunix
    pth1 = getenv('PATH');
    if isempty(strfind(pth1, '/usr/local/bin'))
        pth1 = [pth1 ':/usr/local/bin'];
        setenv('PATH', pth1);
    end
end

% first, try system pigz
[err, ~] = jsystem({'pigz' '-V'});
if ~err, cmd = 'pigz'; return; end

% next, try pigz included with nii_tool
cmd = [m_dir '/pigz'];
if ismac % pigz for mac is not included in the package
    if strcmp(gz_unzip, 'gzip')
        fprintf(2, [' Please install pigz for fast compression: ' ...
            'http://macappstore.org/pigz/\n']);
    end
elseif isunix % linux
    [st, val] = fileattrib(cmd);
    if st && ~val.UserExecute, fileattrib(cmd, '+x'); end
end

[err, ~] = jsystem({cmd '-V'});
if ~err, return; end

% Third, try system gzip/gunzip
[err, ~] = jsystem({gz_unzip '-V'}); % gzip/gunzip on system path?
if ~err, cmd = gz_unzip; return; end

% Lastly, use Matlab gzip/gunzip if java avail
cmd = usejava('jvm');

%% check dd command, return empty if not available
function dd = check_dd
m_dir = fileparts(mfilename('fullpath'));
if strcmpi(pwd, m_dir), cd ..; clnObj = onCleanup(@() cd(m_dir)); end
[err, ~] = jsystem({'dd' '--version'});
if ~err, dd = 'dd'; return; end % dd with linix/mac, and maybe windows

if ispc % rename it as exe
    dd = [m_dir '\dd'];
    [err, ~] = jsystem({dd '--version'});
    if ~err, return; end
end
dd = '';

%% Try to use in order of pigz, system gunzip, then matlab gunzip
function outName = gunzipOS(fname, nByte)
persistent cmd dd pth uid; % command to run gupzip, dd tool, and temp_path
if isempty(cmd)
    cmd = check_gzip('gunzip'); % gzip -dc has problem in PC
    if ischar(cmd)
        cmd = @(nam)sprintf('"%s" -nfdc "%s" ', cmd, nam); % f for overwrite
    elseif islogical(cmd) && ~cmd
        cmd = [];
        error('None of system pigz, gunzip or Matlab gunzip is available');
    end
    dd = check_dd;
    if ~isempty(dd)
        dd = @(n,out)sprintf('| "%s" count=%g of="%s"', dd, ceil(n/512), out);
    end

    if ispc % matlab tempdir could be slow due to cd in and out
        pth = getenv('TEMP');
        if isempty(pth), pth = pwd; end
    else
        pth = getenv('TMP');
        if isempty(pth), pth = getenv('TMPDIR'); end
        if isempty(pth), pth = '/tmp'; end % last resort
    end
    uid = @()sprintf('_%s_%03x', datestr(now, 'yymmddHHMMSSfff'), randi(999));
end

fname = char(fname);
if islogical(cmd)
    outName = gunzip(fname, pth);
    outName = outName{1};
    return;
end

[~, outName, ext] = fileparts(fname);
if strcmpi(ext, '.gz') % likely always true
    [~, outName, ext1] = fileparts(outName);
    outName = [outName uid() ext1];
else
    outName = [outName uid()];
end
outName = fullfile(pth, outName);
if ~isempty(dd) && nargin>1 && ~isinf(nByte) % unzip only part of data
    try
        [err, ~] = system([cmd(fname) dd(nByte, outName)]);
        if err==0, return; end
    end
end

[err, str] = system([cmd(fname) '> "' outName '"']);
% [err, str] = jsystem({'pigz' '-nfdc' fname '>' outName});
if err
    try
    	outName = gunzip(fname, pth);
    catch
        error('Error during gunzip:\n%s', str);
    end
end

%% cast bytes into a type, swapbytes if needed
function out = cast_swap(b, typ, swap)
out = typecast(b, typ);
if swap, out = swapbytes(out); end
out = double(out); % for convenience

%% subfunction: read hdr
function hdr = read_hdr(b, C, fname)
n = typecast(b(1:4), 'int32');
if     n==348, niiVer = 1; swap = false;
elseif n==540, niiVer = 2; swap = false;
else
    n = swapbytes(n);
    if     n==348, niiVer = 1; swap = true;
    elseif n==540, niiVer = 2; swap = true;
    else, error('Not valid NIfTI file: %s', fname);
    end
end

if niiVer>1, C = niiHeader(niiVer); end % C defaults for version 1
for i = 1:size(C,1)
    try a = b(C{i,5}+1 : C{i+1,5});
    catch
        if C{i,5}==numel(b), a = [];
        else, a = b(C{i,5} + (1:C{i,2})); % last item extension is in bytes
        end
    end
    if strcmp(C{i,3}, 'char')
        a = deblank(native2unicode(a));
    else
        a = cast_swap(a, C{i,3}, swap);
        a = double(a);
    end
    hdr.(C{i,1}) = a;
end

hdr.version = niiVer; % for 'save', unless user asks to change
hdr.swap_endian = swap;
hdr.file_name = fname;

%% subfunction: read ext, and decode it if known ecode
function ext = read_ext(b, hdr)
ext = []; % avoid error if no ext but hdr.extension(1) was set
nEnd = hdr.vox_offset;
if nEnd == 0, nEnd = numel(b); end % .hdr file

swap = hdr.swap_endian;
j = hdr.sizeof_hdr + 4; % 4 for hdr.extension
while j < nEnd
    esize = cast_swap(b(j+(1:4)), 'int32', swap); j = j+4; % x16
    if isempty(esize) || mod(esize,16), return; end % just to be safe
    i = numel(ext) + 1;
    ext(i).esize = esize; %#ok<*AGROW>
    ext(i).ecode = cast_swap(b(j+(1:4)), 'int32', swap); j = j+4;
    ext(i).edata = b(j+(1:esize-8))'; % -8 for esize & ecode
    j = j + esize - 8;
end
ext = decode_ext(ext, swap);

%% subfunction
function ext = decode_ext(ext, swap)
% Decode edata if we know ecode
for i = 1:numel(ext)
    try, if ~isempty(ext(i).edata_decoded'), continue; end; end % done
    if ext(i).ecode == 40 % Matlab: any kind of matlab variable
        nByte = cast_swap(ext(i).edata(1:4), 'int32', swap); % MAT data
        tmp = [tempname '.mat']; % temp MAT file to save edata
        fid1 = fopen(tmp, 'W');
        fwrite(fid1, ext(i).edata(5:nByte+4)); % exclude padded zeros
        fclose(fid1);
        deleteMat = onCleanup(@() deleteFile(tmp)); % delete temp file after done
        ext(i).edata_decoded = load(tmp); % load into struct
    elseif any(ext(i).ecode == [4 6 32 44]) % 4 AFNI, 6 plain text, 32 CIfTI, 44 MRS (json)
        str = char(ext(i).edata(:)');
        if isempty(strfind(str, 'dicm2nii.m'))
            ext(i).edata_decoded = deblank(str);
        else % created by dicm2nii.m
            ss = struct;
            ind = strfind(str, [';' char([0 10])]); % strsplit error in Octave
            ind = [-2 ind]; % -2+3=1: start of first para
            for k = 1:numel(ind)-1
                a = str(ind(k)+3 : ind(k+1));
                a(a==0) = []; % to be safe. strtrim wont remove null
                a = strtrim(a);
                if isempty(a), continue; end
                try
                    eval(['ss.' a]); % put all into struct
                catch
                    try
                        a = regexp(a, '(.*?)\s*=\s*(.*?);', 'tokens', 'once');
                        ss.(a{1}) = a{2};
                    catch me
                        fprintf(2, '%s\n', me.message);
                        fprintf(2, 'Unrecognized text: %s\n', a);
                    end
                end
            end
            flds = fieldnames(ss); % make all vector column
            for k = 1:numel(flds)
                val = ss.(flds{k});
                if isnumeric(val) && isrow(val), ss.(flds{k}) = val'; end
            end
            ext(i).edata_decoded = ss;
        end
    elseif ext(i).ecode == 2 % dicom
        tmp = [tempname '.dcm'];
        fid1 = fopen(tmp, 'W');
        fwrite(fid1, ext(i).edata);
        fclose(fid1);
        deleteDcm = onCleanup(@() deleteFile(tmp));
        ext(i).edata_decoded = dicm_hdr(tmp);
    end
end

%% subfunction: read img
% memory gunzip may be slow and error for large img, so use file unzip
function img = read_img(hdr, para)
ind = para.datatype == hdr.datatype;
if ~any(ind)
    error('Datatype %g is not supported by nii_tool.', hdr.datatype);
end

dim = hdr.dim(2:8);
dim(hdr.dim(1)+1:7) = 1; % avoid some error in file
dim(dim<1) = 1;
valpix = para.valpix(ind);

fname = nii_name(hdr.file_name, '.img'); % in case of .hdr/.img pair
fid = fopen(fname);
sig = fread(fid, 2, '*uint8')';
if isequal(sig, [31 139]) % .gz
    fclose(fid);
    fname = gunzipOS(fname);
    cln = onCleanup(@() deleteFile(fname)); % delete gunzipped file
    fid = fopen(fname);
end

% if ~exist('cln', 'var') && valpix==1 && ~hdr.swap_endian
%     m = memmapfile(fname, 'Offset', hdr.vox_offset, ...
%         'Format', {para.format{ind}, dim, 'img'});
%     nii = m.Data;
%     return;
% end

if hdr.swap_endian % switch between LE and BE
    [~, ~, ed] = fopen(fid); % default endian: almost always ieee-le
    fclose(fid);
    if isempty(strfind(ed, '-le')), ed = strrep(ed, '-be', '-le'); %#ok<*STREMP>
    else, ed = strrep(ed, '-le', '-be');
    end
    fid = fopen(fname, 'r', ed); % re-open with changed endian
end

fseek(fid, hdr.vox_offset, 'bof');
img = fread(fid, prod(dim)*valpix, ['*' para.format{ind}]); % * to keep original class
fclose(fid);

if any(hdr.datatype == [128 511 2304]) % RGB or RGBA
%     a = reshape(single(img), valpix, n); % assume rgbrgbrgb...
%     d1 = abs(a - a(:,[2:end 1])); % how similar are voxels to their neighbor
%     a = reshape(a, prod(dim(1:2)), valpix*prod(dim(3:7))); % rr...rgg...gbb...b
%     d2 = abs(a - a([2:end 1],:));
%     j = (sum(d1(:))>sum(d2(:)))*2 + 1; % 1 for afni, 3 for mricron
    j = para.rgb_dim; % auto detection may get wrong for noisy background
    dim = [dim(1:j-1) valpix dim(j:7)]; % length=8 now
    img = reshape(img, dim);
    img = permute(img, [1:j-1 j+1:8 j]); % put RGB(A) to dim8
elseif any(hdr.datatype == [32 1792]) % complex single/double
    img = reshape(img, [2 dim]);
    img = complex(permute(img(1,:,:,:,:,:,:,:), [2:8 1]), ... % real
                  permute(img(2,:,:,:,:,:,:,:), [2:8 1]));    % imag
else % all others: valpix=1
    if hdr.datatype==1, img = logical(img); end
    img = reshape(img, dim);
end

% RGB triplet in 5th dim OR RGBA quadruplet in 5th dim
c = hdr.intent_code;
if (c == 2003 && dim(5) == 3) || (c == 2004  && dim(5) == 4)
    img = permute(img, [1:4 6:8 5]);
end

%% Return requested fname with ext, useful for .hdr and .img files
function fname = nii_name(fname, ext)
if strcmpi(ext, '.img')
    i = regexpi(fname, '.hdr(.gz)?$');
    if ~isempty(i), fname(i(end)+(0:3)) = ext; end
elseif strcmpi(ext, '.hdr')
    i = regexpi(fname, '.img(.gz)?$');
    if ~isempty(i), fname(i(end)+(0:3)) = ext; end
end

%% Read NIfTI file as bytes, gunzip if needed, but ignore endian
function [b, fname] = nii_bytes(fname, nByte)
if nargin<2, nByte = inf; end
[fid, err] = fopen(fname); % system default endian
if fid<1, error([err ': ' fname]); end
b = fread(fid, nByte, '*uint8')';
fname = fopen(fid);
fclose(fid);
if isequal(b(1:2), [31 139]) % gz, tgz file
    b = gunzip_mem(b, fname, nByte)';
end

%% subfunction: get help for a command
function subFuncHelp(mfile, cmd)
str = fileread(which(mfile));
i = regexp(str, '\n\s*%', 'once'); % start of 1st % line
str = regexp(str(i:end), '.*?(?=\n\s*[^%])', 'match', 'once'); % help text
str = regexprep(str, '\r?\n\s*%', '\n'); % remove '\r' and leading %

dashes = regexp(str, '\n\s*-{1,4}\s+') + 1; % lines starting with 1 to 4 -
if isempty(dashes), disp(str); return; end % Show all help text

prgrfs = regexp(str, '(\n\s*){2,}'); % blank lines
nTopic = numel(dashes);
topics = ones(1, nTopic+1);
for i = 1:nTopic
    ind = regexpi(str(1:dashes(i)), [mfile '\s*\(']); % syntax before ' - '
    if isempty(ind), continue; end % no syntax before ' - ', assume start with 1
    ind = find(prgrfs < ind(end), 1, 'last'); % previous paragraph
    if isempty(ind), continue; end
    topics(i) = prgrfs(ind) + 1; % start of this topic
end
topics(end) = numel(str); % end of last topic

cmd = strrep(cmd, '?', ''); % remove ? in case it is in subcmd
if isempty(cmd) % help for main function
    disp(str(1:topics(1))); % subfunction list before first topic
    return;
end

expr = [mfile '\s*\(\s*''' cmd ''''];
for i = 1:nTopic
    if isempty(regexpi(str(topics(i):dashes(i)), expr, 'once')), continue; end
    disp(str(topics(i):topics(i+1)));
    return;
end

fprintf(2, ' Unknown command for %s: %s\n', mfile, cmd); % no cmd found

%% gunzip bytes in memory if possible. 2nd/3rd input for fallback file gunzip
% Trick: try-block avoid error for partial file unzip.
function bytes = gunzip_mem(gz_bytes, fname, nByte)
bytes = [];
try
    bais = java.io.ByteArrayInputStream(gz_bytes);
    try, gzis = java.util.zip.GZIPInputStream(bais); %#ok<*NOCOM>
    catch, try, gzis = java.util.zip.InflaterInputStream(bais); catch me; end
    end
    buff = java.io.ByteArrayOutputStream;
    try org.apache.commons.io.IOUtils.copy(gzis, buff); catch me; end
    gzis.close;
    bytes = typecast(buff.toByteArray, 'uint8');
    if isempty(bytes), error(me.message); end
catch
    if nargin<3 || isempty(nByte), nByte = inf; end
    if nargin<2 || isempty(fname)
        fname = [tempname '.gz']; % temp gz file
        fid = fopen(fname, 'W');
        if fid<0, return; end
        cln = onCleanup(@() deleteFile(fname)); % delete temp gz file
        fwrite(fid, gz_bytes, 'uint8');
        fclose(fid);
    end

    try %#ok<*TRYNC>
        fname = gunzipOS(fname, nByte);
        fid = fopen(fname);
        bytes = fread(fid, nByte, '*uint8');
        fclose(fid);
        deleteFile(fname); % unzipped file
    end
end

%% Delete file in background
function deleteFile(fname)
if ispc, system(['start "" /B del "' fname '"']);
else, system(['rm "' fname '" &']);
end

%% faster than system: based on https://github.com/avivrosenberg/matlab-jsystem
function [err, out] = jsystem(cmd)
% cmd is cell str, no quotation marks needed for file names with space.
cmd = cellstr(cmd);
try
    pb = java.lang.ProcessBuilder(cmd);
    pb.redirectErrorStream(true); % ErrorStream to InputStream
    process = pb.start();
    scanner = java.util.Scanner(process.getInputStream).useDelimiter('\A');
    if scanner.hasNext(), out = char(scanner.next()); else, out = ''; end
    err = process.exitValue; % err = process.waitFor() may hang
    if err, error('java.lang.ProcessBuilder error'); end
catch % fallback to system() if java fails like for Octave
    cmd = regexprep(cmd, '.+? .+', '"$0"'); % double quotes if with middle space
    [err, out] = system(sprintf('%s ', cmd{:}, '2>&1'));
end

%% Return true if input is char or single string (R2016b+)
function tf = ischar(A)
tf = builtin('ischar', A);
if tf, return; end
if exist('strings', 'builtin'), tf = isstring(A) && numel(A)==1; end
%%


% APM Specify Options
%
% response = apm_option(server,app,name,value)
%
% Function apm_option sends an option specification
%   to the APM server with the following arguments:
%
%   server = address of server
%      app = application name
%     name = option name
%    value = option value
%
% A response is returned indicating whether the option
%   was successfully recorded
%
% Either global options (NLC.option_name) or parameter/variable
%   options (NAME.option_name) are able to be specified with this
%   function. A full list of available options with their default
%   values are available here:
%
% Global Options:
%    http://apmonitor.com/wiki/index.php/Main/DbsGlobal
%
% Variable Options:
%    http://apmonitor.com/wiki/index.php/Main/DbsVariable
%
function response = apm_option(server,app,name,value)
   aline = ['option ' deblank(char(name)) ' = ' num2str(value)];
   response = apm(server,app,aline);


function test_suite = test_binomial1

%   Run specific demo, save values and compare the results to the expected.
%   Works for both xUnit Test Framework package by Steve Eddins and for
%   the built-in Unit Testing Framework (as of Matlab version 2013b).
%
%   See also
%     TEST_ALL, DEMO_BINOMIAL1
%
% Copyright (c) 2011-2012 Ville Tolvanen
% Copyright (c) 2014 Tuomas Sivula

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

  % Check which package to use
  if exist('initTestSuite', 'file')
    initTestSuite_path = which('initTestSuite');
  else
    initTestSuite_path = '';
  end
  if ~isempty(initTestSuite_path) && ...
     exist([initTestSuite_path(1:end-15) 'runtests'], 'file')
    % xUnit package
    initTestSuite;
  else
    % Built-in package
    % Use all functions except the @setup
    tests = localfunctions;
    tests = tests(~cellfun(@(x)strcmp(func2str(x),'setup'),tests));
    test_suite = functiontests(tests);
  end
end

% -------------
%     Tests
% -------------

function testRunDemo(testCase)
  % Run the correspondin demo and save the values. Note this test has to
  % be run at lest once before the other test may succeed.
  rundemo(getName())
end

function testPredictiveMeanVarianceAndDensity(testCase)
  % Test predictive mean, variance and density
  verifyVarsEqual(testCase, getName(), ...
    'RelTolElement', 0.05, 'RelTolRange', 0.01)
end

% ------------------------
%     Helper functions
% ------------------------

function testCase = setup
  % Helper function to suply empty array into variable testCase as an
  % argument for each test function, if using xUnit package. Not to be
  % used with built-in test framework.
  testCase = [];
end

function name = getName
  % Helperfunction that returns the name of the demo, e.g. 'binomial1'.
  name = mfilename;
  name = name(6:end);
end


% readegilocs() - look up locations for EGI EEG dataset.
%
% Usage:
%   >> EEG = readegilocs(EEG);
%   >> EEG = readegilocs(EEG, fileloc);
%
% Inputs:
%   EEG        - EEGLAB data structure
%
% Optional input:
%   fileloc    - EGI channel location file
%
% Outputs:
%   EEG        - EEGLAB data structure with channel location structure
%
% Author: Arnaud Delorme, SCCN/UCSD
%
% See also: pop_readegi(), readegi()

% Copyright (C) 12 Nov 2002 Arnaud Delorme, Salk Institute, arno@salk.edu
%
% 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 2 of the License, or
% (at your option) any later version.
%
% This program 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 this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function EEG = readegilocs(EEG, fileloc);

if nargin < 1
    help readegilocs;
    return;
end;

% importing channel locations
% ---------------------------
found = 1;
if nargin < 2
    switch EEG.nbchan
        case { 32 33 }, fileloc = 'GSN-HydroCel-32.sfp';
        case { 64 65 }, fileloc = 'GSN65v2_0.sfp';
        case { 128 129 }, fileloc = 'GSN129.sfp';
        case { 256 257 }, fileloc = 'GSN-HydroCel-257.sfp';
        otherwise, found = 0;
    end;
end;
if found
    fprintf('EGI channel location automatically detected %s ********* WARNING please check that this the proper file\n', fileloc);
    if EEG.nbchan == 64 || EEG.nbchan == 65 || EEG.nbchan == 256 || EEG.nbchan == 257
        fprintf( [ 'Warning: this function assumes you have a 64-channel system Version 2\n' ...
                   '         if this is not the case, update the channel location with the proper file' ]);
    end;
    % remove the last channel for 33 channels

    peeglab = fileparts(which('eeglab.m'));
    fileloc = fullfile(peeglab, 'sample_locs', fileloc);
    locs = readlocs(fileloc);
    locs(1).type = 'FID';
    locs(2).type = 'FID';
    locs(3).type = 'FID';
    locs(end).type = 'REF';
    if EEG.nbchan == 256 || EEG.nbchan == 257
        if EEG.nbchan == 256
            chaninfo.nodatchans = locs([end]);
            locs([end]) = [];
        end;
    elseif mod(EEG.nbchan,2) == 0,
        chaninfo.nodatchans = locs([1 2 3 end]);
        locs([1 2 3 end]) = [];
    else
        chaninfo.nodatchans = locs([1 2 3]);
        locs([1 2 3]) = [];
    end; % remove reference
    chaninfo.filename = fileloc;
    EEG.chanlocs   = locs;
    EEG.urchanlocs = locs;
    EEG.chaninfo   = chaninfo;
end;


classdef TestObjectnessBING
    %TestObjectnessBING

    methods (Static)
        function test_1
            saliency = cv.ObjectnessBING();

            resdir = tempname();
            cObj = onCleanup(@() deleteDir(resdir));
            saliency.setTrainingPath(get_training_path());
            saliency.setBBResDir(resdir);

            img = imread(fullfile(mexopencv.root(),'test','balloon.jpg'));
            objectnessBoundingBox = saliency.computeSaliency(img);
            validateattributes(objectnessBoundingBox, {'cell'}, {'vector'});
            cellfun(@(bb) validateattributes(bb, {'numeric'}, ...
                {'vector', 'integer', 'numel',4}), objectnessBoundingBox);

            objectnessValues = saliency.getObjectnessValues();
            validateattributes(objectnessValues, {'numeric'}, ...
                {'vector', 'numel',numel(objectnessBoundingBox)});
        end
    end

end

function deleteDir(fname)
    if isdir(fname)
        if mexopencv.isOctave()
            %HACK: dont ask for confirmation
            confirm_recursive_rmdir(false, 'local');
        end
        rmdir(fname, 's');
    end
end

function training_path = get_training_path()
    training_path = fullfile(mexopencv.root(),'test','ObjectnessTrainedModel');
    if ~isdir(training_path)
        % download trained models from GitHub
        files = {
            'ObjNessB2W8HSV.idx.yml.gz'
            'ObjNessB2W8HSV.wS1.yml.gz'
            'ObjNessB2W8HSV.wS2.yml.gz'
            'ObjNessB2W8I.idx.yml.gz'
            'ObjNessB2W8I.wS1.yml.gz'
            'ObjNessB2W8I.wS2.yml.gz'
            'ObjNessB2W8MAXBGR.idx.yml.gz'
            'ObjNessB2W8MAXBGR.wS1.yml.gz'
            'ObjNessB2W8MAXBGR.wS2.yml.gz'
        };
        mkdir(training_path);
        for i=1:numel(files)
            url = 'https://cdn.rawgit.com/opencv/opencv_contrib/3.2.0/modules/saliency/samples/ObjectnessTrainedModel/';
            urlwrite([url files{i}], fullfile(training_path,files{i}));
        end
    end
end


function [foundFiles, iDepth] = file_find( baseDir, filePattern, maxDepth, isSingle, iDepth )
% FILE_FIND: Find a file recursively.
%
% USAGE:  [foundFiles, iFoundDepth] = file_find( baseDir, filePattern, maxDepth=Inf, isSingle=1 )
%
% INPUT:
%    - baseDir     : Full path to the directory to search
%    - filePattern : Name of the target file (wild chars allowed)
%    - maxDepth    : Maximum folder depth from the baseDir
%    - isSingle    : If 1, only search for one file, exit when it's found
%    - iDepth      : Current recursion level
%
% OUTPUT:
%    - foundFiles : Full path to files, or [] if no file were found

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2008-2022

% Parse inputs
if (nargin < 5) || isempty(iDepth)
    iDepth = 1;
end
if (nargin < 4) || isempty(isSingle)
    isSingle = 1;
end
if (nargin < 3) || isempty(maxDepth)
    maxDepth = Inf;
end
if (nargin < 2)
    error('foundFiles = file_find( baseDir, filePattern, maxDepth, isSingle );');
end
% Default return value
if isSingle
    foundFiles = [];
else
    foundFiles = {};
end

% Base dir not valid
if isempty(baseDir) || ~isdir(baseDir) || (baseDir(1) == '.')
    return;
% Pattern is a subfolder of the base dir
elseif isSingle && ~any(filePattern == '*') && isdir(fullfile(baseDir, filePattern))
    foundFiles = fullfile(baseDir, filePattern);
    return;
else
    % Try to find required file
    listDir = dir(fullfile(baseDir, filePattern));
    if ~isempty(listDir)
        if isSingle
            foundFiles = fullfile(baseDir, listDir(1).name);
        else
            for i = 1:length(listDir)
                foundFiles = cat(2, foundFiles, {fullfile(baseDir, listDir(i).name)});
            end
        end
        return
    end
    % If reached the recursion limit
    if (maxDepth <= 1)
        return
    end
    % Get subdirectories
    listDir = dir(fullfile(baseDir, '*'));
    listDir([listDir.isdir] == 0) = [];
    % Process each subdirectory
    for i = 1:length(listDir)
        if (listDir(i).name(1) ~= '.')
            newDir = fullfile(baseDir, listDir(i).name);
            [foundRec, iFoundDepth] = file_find(newDir, filePattern, maxDepth - 1, isSingle, iDepth + 1);
            if ~isempty(foundRec)
                iDepth = iFoundDepth;
                % Looking for one file only: return
                if isSingle
                    foundFiles = foundRec;
                    return;
                % Otherwise: Keep searching
                else
                    foundFiles = cat(2, foundFiles, foundRec);
                end
            end
        end
    end
end
iDepth = [];


function lrdata = getlrdata(F)
F = flatten(F);
k = 1;
lrdata = [];
for i = 1:length(F.clauses)
    if F.clauses{i}.type == 14
        lrdata{end+1} =  F.clauses{i}.data;
    end
end