% Defines a stimulus
%
% Author: Jonathan Karr
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 11/30/2009
classdef Stimuli < edu.stanford.covert.cell.kb.KnowledgeBaseObject
    properties
        compartments    = edu.stanford.covert.cell.kb.Compartment.empty(0, 0);
        values          = [];
        initialTimes    = [];
        finalTimes      = [];

        regulatedProteinMonomers = edu.stanford.covert.cell.kb.ProteinMonomer.empty(0, 0);
        regulatedProteinComplexs = edu.stanford.covert.cell.kb.ProteinComplex.empty(0, 0);
    end

    methods
        function this = Stimuli(knowledgeBase, wid, wholeCellModelID, name,...
                comments, crossReferences)

            if nargin == 0; return; end;

            this = edu.stanford.covert.cell.kb.Stimuli.empty(size(wid, 1), 0);
            if size(wid,1) == 0; return; end;

            this(size(wid, 1), 1) = edu.stanford.covert.cell.kb.Stimuli;
            for i = 1:size(wid, 1)
                this(i, 1).idx = i;
                this(i, 1).knowledgeBase = knowledgeBase;
                this(i, 1).wid = wid(i);
                this(i, 1).wholeCellModelID = wholeCellModelID{i};
                this(i, 1).name = name{i};
                if exist('comments', 'var') && ~isempty(comments); this(i,1).comments = comments{i}; end;
                if exist('crossReferences', 'var')
                    if size(crossReferences, 1) > 1
                        this(i, 1).crossReferences = crossReferences(i);
                    else
                        this(i, 1).crossReferences = struct;
                        fields = fieldnames(crossReferences);
                        for j = 1:size(fields,1)
                            vals = crossReferences.(fields{j});
                            this(i,1).crossReferences.(fields{j}) = vals(i);
                        end
                    end
                end
            end
        end

        function serializeLinks(this)
            for i = 1:numel(this)
                this(i).compartments             = this.serializeLinksHelper(this(i).compartments);
                this(i).values                   = this.serializeLinksHelper(this(i).values);
                this(i).initialTimes             = this.serializeLinksHelper(this(i).initialTimes);
                this(i).finalTimes               = this.serializeLinksHelper(this(i).finalTimes);

                this(i).regulatedProteinMonomers = this.serializeLinksHelper(this(i).regulatedProteinMonomers);
                this(i).regulatedProteinComplexs = this.serializeLinksHelper(this(i).regulatedProteinComplexs);

                serializeLinks@edu.stanford.covert.cell.kb.KnowledgeBaseObject(this(i));
            end
        end

        function deserializeLinks(this, kb)
            for i = 1:numel(this)
                this(i).compartments             = this.deserializeLinksHelper(this(i).compartments, kb.compartments);
                this(i).values                   = this.deserializeLinksHelper(this(i).values);
                this(i).initialTimes             = this.deserializeLinksHelper(this(i).initialTimes);
                this(i).finalTimes               = this.deserializeLinksHelper(this(i).finalTimes);

                this(i).regulatedProteinMonomers = this.deserializeLinksHelper(this(i).regulatedProteinMonomers, kb.proteinMonomers);
                this(i).regulatedProteinComplexs = this.deserializeLinksHelper(this(i).regulatedProteinComplexs, kb.proteinComplexs);

                deserializeLinks@edu.stanford.covert.cell.kb.KnowledgeBaseObject(this(i), kb);
            end
        end

        function deleteLinks(this)
            for i = 1:numel(this)
                this(i).compartments             = [];
                this(i).values                   = [];
                this(i).initialTimes             = [];
                this(i).finalTimes               = [];

                this(i).regulatedProteinMonomers = [];
                this(i).regulatedProteinComplexs = [];

                deleteLinks@edu.stanford.covert.cell.kb.KnowledgeBaseObject(this(i));
            end
        end
    end
end


function spok_install
%SPOK_INSTALL compiles and installs the SPOK mexFunction
% Your current working directory must be the "spok" directory for this function
% to work.
%
% Example:
%   spok_install
%
% See also sparse, spok, spok_test

% Copyright 2008, Tim Davis, University of Florida

help spok_install
mex spok.c spok_mex.c
addpath (pwd)

fprintf ('Added the following directory to your path:\n') ;
disp (pwd) ;
fprintf ('Use pathtool and click "save" to save this for future sessions.\n') ;


function model = robTwoDynamicsSetLatentValues(model, X)

% ROBTWODYNAMICSSETLATENTVALUES Set the latent values inside the model.

% FGPLVM

model = robOneDynamicsSetLatentValues(model, X);


function fileName = dnetWriteResult(model, dataSet, number)

% DNETWRITERESULT Write a DNET result.
% FORMAT
% DESC writes a DNET result.
% ARG model : the model to write.
% ARG dataSet : the name of the data set to write.
% ARG number : the number of the DNET run to write.
% RETURN fileName : the file name used to write.
%
% SEEALSO : dnetLoadResult
%
% COPYRIGHT : Neil D. Lawrence, 2009

% MLTOOLS

dataSet(1) = upper(dataSet(1));
type = model.type;
type(1) = upper(type(1));
fileName = ['dem' dataSet type num2str(number)];

[mapping, dnetInfo] = dnetDeconstruct(model);

save(fileName, 'mapping', 'dnetInfo');


function obj = modelDownsampleRawMovies(obj)
	% Downsamples calcium imaging files and moves them to appropriate folders. Best for Inscopix nVista v1.0 and v2.0 data but should work for any HDF5 file.
	% Biafra Ahanonu
	% inputs
		%
	% outputs
		%

	% changelog
		% 2015.11.25 - Added ability to downsample TIFFs, at the moment assumes that they will be done the inscopix way.
		% 2017.02.18 - slight change in how determination of already existing movies is performed
		% 2019.07.11 [19:53:47] - Added support for ISXD Inscopix files via Inscopix Data Processing Software. Also changed way program is run to loop over files and check for each extension in the case a folder as multiple types of files that need to be downsampled (e.g. HDF5, TIF, ISXD).
		% 2019.11.18 [17:28:48] - Input dialog for folder list or GUI to manually entering multiple folders to downsample. Easier for users than current purely comma separated single line list.
		% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
		% 2021.12.08 [22:11:00] - Additional updates to handle CIAtah v4.0 API switch.
		% 2022.02.25 [15:40:56] - Change TIF finding to split apart the information file before using for regular expression.

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	try
		[newFolderListCell] = subfxnSelectFolders();
		if ~isempty(newFolderListCell)
			obj.downsampleRawOptions.folderListInfo = strjoin(newFolderListCell,',');
		end

		downsampleSettings = inputdlg(...
			{...
				'Folder(s) where raw HDF5s are located [from previous prompts]. Use comma to separate multiple source folders:',...
				'Folder to save downsampled HDF5s to:',...
				'Decompression source root folder(s). Use comma to separate multiple source folders:',...
				'Downsample factor (in x-y):',...
				'Regexp for HDF5 (e.g. "recording.*.hdf5"), TIFF (e.g. "recording.*.tif"), ISXD/Inscopix (e.g. ".*.isxd") files:',...
				'HDF5 hierarchy name where movie is stored:',...
				'Max chunk size (MB)',...
				'Regexp for folders:',...
				'Regexp for base filename (use txt log name):',...
				'Extension for base filename:',...
				'Location to store raw HDF5 (leave empty to skip):',...
				'Raw downsample factor (in x-y):',...
				'Output HDF5 dataset name:'
			},...
			'downsample settings',[1 100],...
			{...
				obj.downsampleRawOptions.folderListInfo,...
				obj.downsampleRawOptions.downsampleSaveFolder,...
				obj.downsampleRawOptions.downsampleSrcFolder,...
				obj.downsampleRawOptions.downsampleFactor,...
				obj.downsampleRawOptions.fileFilterRegexp,...
				obj.downsampleRawOptions.datasetName,...
				obj.downsampleRawOptions.maxChunkSize,...
				obj.downsampleRawOptions.srcFolderFilterRegexp,...
				obj.downsampleRawOptions.srcSubfolderFileFilterRegexp,...
				obj.downsampleRawOptions.srcSubfolderFileFilterRegexpExt,...
				obj.downsampleRawOptions.downsampleSaveFolderTwo,...
				obj.downsampleRawOptions.downsampleFactorTwo,...
				obj.downsampleRawOptions.outputDatasetName,...
			});
		obj.downsampleRawOptions.folderListInfo = downsampleSettings{1};
		obj.downsampleRawOptions.downsampleSaveFolder = downsampleSettings{2};
		obj.downsampleRawOptions.downsampleSrcFolder = downsampleSettings{3};
		obj.downsampleRawOptions.downsampleFactor = downsampleSettings{4};
		obj.downsampleRawOptions.fileFilterRegexp = downsampleSettings{5};
		obj.downsampleRawOptions.datasetName = downsampleSettings{6};
		obj.downsampleRawOptions.maxChunkSize = downsampleSettings{7};
		obj.downsampleRawOptions.srcFolderFilterRegexp = downsampleSettings{8};
		obj.downsampleRawOptions.srcSubfolderFileFilterRegexp = downsampleSettings{9};
		obj.downsampleRawOptions.srcSubfolderFileFilterRegexpExt = downsampleSettings{10};
		obj.downsampleRawOptions.downsampleSaveFolderTwo = downsampleSettings{11};
		obj.downsampleRawOptions.downsampleFactorTwo = downsampleSettings{12};
		obj.downsampleRawOptions.outputDatasetName = downsampleSettings{13};

		% dsOpt = obj.downsampleRawOptions;

		folderListInfo = strsplit(downsampleSettings{1},',');
		cellfun(@display,folderListInfo);
		nFolders = length(folderListInfo);
		% ~isempty(regexp(downsampleOptions.fileFilterRegexp,'(.hdf5|.h5)'))
		% ~isempty(regexp(downsampleOptions.fileFilterRegexp,'(.tiff|.tif)'))
		% ============================
		for folderNo = 1:nFolders
			display(repmat('=',1,42))
			display([num2str(folderNo) '\' num2str(nFolders) ': ' folderListInfo{folderNo}])
			% downsample if all decompressed files are in the same folder
			downsampleOptions.downsampleSaveFolder = downsampleSettings{2};
			downsampleOptions.datasetName = downsampleSettings{6};
			downsampleOptions.maxChunkSize = str2num(downsampleSettings{7});
			downsampleOptions.downsampleFactor = str2num(downsampleSettings{4});
			downsampleOptions.fileFilterRegexp = downsampleSettings{5};
			downsampleOptions.outputDatasetName = downsampleSettings{13};

			downsampleOptions.downsampleSaveFolderTwo = downsampleSettings{11};
			downsampleOptions.downsampleFactorTwo = str2num(downsampleSettings{12});

			movieList = getFileList([folderListInfo{folderNo} filesep], downsampleOptions.fileFilterRegexp);
			nMoviesH = length(movieList);

			destFolderAll = {};
			destFolderAll2 = {};
			for movieNo = 1:nMoviesH
				movieName = movieList{movieNo};
				[pathstr,name,ext] = fileparts(movieName);
				fprintf('Downsampling %d/%d: %s\n',movieNo,nMoviesH,movieName);
				if ~isempty(regexp(ext,'(.hdf5|.h5)'))
					display('Downsampling HDF5s...')
					downsampleHDFMovieFxnObj({movieName},downsampleOptions);
				elseif ~isempty(regexp(ext,'(.isxd)'))
					display('Downsampling ISXDs...')
					[downsampleSaveFolderMod, downsampleSaveFolderTwoMod] = downsampleIsxdMovieFxnObj({movieName},downsampleOptions);
					destFolderAll{end+1} = downsampleSaveFolderMod;
					destFolderAll2{end+1} = downsampleSaveFolderTwoMod;
				elseif ~isempty(regexp(ext,'(.tiff|.tif)'))
					if movieNo==1
						display('Downsampling TIFFs...')
						folderPath = folderListInfo{folderNo};
						downsampleOptions.srcSubfolderFileFilterRegexp = downsampleSettings{9};
						downsampleOptions.srcSubfolderFileFilterRegexpExt = downsampleSettings{10};
						downsampleTiffMovieFxnObj(folderPath,downsampleOptions);
					end
				end
			end

			% if ~isempty(regexp(downsampleOptions.fileFilterRegexp,'(.hdf5|.h5)'))
			% 	display('Downsampling HDF5s...')
			% 	downsampleHDFMovieFxnObj(movieList,downsampleOptions);
			% elseif ~isempty(regexp(downsampleOptions.fileFilterRegexp,'(.isxd)'))
			% 	display('Downsampling ISXDs...')
			% 	downsampleIsxdMovieFxnObj(movieList,downsampleOptions);
			% elseif ~isempty(regexp(downsampleOptions.fileFilterRegexp,'(.tiff|.tif)'))
			% 	display('Downsampling TIFFs...')
			% 	folderPath = folderListInfo{folderNo};
			% 	downsampleOptions.srcSubfolderFileFilterRegexp = downsampleSettings{9};
			% 	downsampleOptions.srcSubfolderFileFilterRegexpExt = downsampleSettings{10};
			% 	downsampleTiffMovieFxnObj(folderPath,downsampleOptions);
			% end
			% ioptions.folderListInfo = [folderListInfo{folderNo} filesep];
			% ioptions.downsampleSaveFolder = [downsampleSettings{2} filesep];
			% ioptions.downsampleFactor = str2num(downsampleSettings{4});
			% ioptions.fileFilterRegexp = downsampleSettings{5};
			% ioptions.datasetName = downsampleSettings{6};
			% ioptions.maxChunkSize = str2num(downsampleSettings{7});
			% ioptions.runArg = 'downsampleMovie';
			% ostruct = controllerAnalysis('options',ioptions);
		end
		% re-create folder structure
		clear ioptions
		% run HDF5 or TIFF
		%
		downsampleSrcFolder = strsplit(downsampleSettings{3},',');
		downsampleSaveFolder = [downsampleSettings{2} filesep];
		% used to determine which folders to copy from src to dest
		ioptions.srcFolderFilterRegexp = downsampleSettings{8};
		% this regexp is used to search the destination directory
		ioptions.srcSubfolderFileFilterRegexp = downsampleSettings{9};
		%
		ioptions.srcSubfolderFileFilterRegexpExt = downsampleSettings{10};
		[success destFolders] = moveFilesToFolders(downsampleSrcFolder,char(downsampleSaveFolder(:))','options',ioptions);
		destFolders = unique(destFolders);
		if isempty(destFolderAll)
		else
			destFolders = unique(destFolderAll);
		end
		% ============================
		if ~isempty(downsampleSettings{11})
			% movie second set of movie files
			downsampleSaveFolder = [downsampleSettings{11} filesep];
			% used to determine which folders to copy from src to dest
			ioptions.srcFolderFilterRegexp = downsampleSettings{8};
			% this regexp is used to search the destination directory
			ioptions.srcSubfolderFileFilterRegexp = downsampleSettings{9};
			%
			ioptions.srcSubfolderFileFilterRegexpExt = downsampleSettings{10};
			[success destFolders2] = moveFilesToFolders(downsampleSrcFolder,char(downsampleSaveFolder(:))','options',ioptions);

			if isempty(destFolderAll2)
			else
				destFolders2 = unique(destFolderAll2);
			end
		end
		% ============================
		% if ~isempty(downsampleSettings{11})
		%     % now same the semi-downsampled raw files
		%     for folderNo = 1:nFolders
		%         % downsample if all decompressed files are in the same folder
		%         downsampleOptions.downsampleSaveFolder = [downsampleSettings{11} filesep];
		%         downsampleOptions.datasetName = downsampleSettings{6};
		%         downsampleOptions.maxChunkSize = str2num(downsampleSettings{7});
		%         downsampleOptions.downsampleFactor = str2num(downsampleSettings{12});
		%         downsampleOptions.fileFilterRegexp = downsampleSettings{5};

		%         movieList = getFileList([folderListInfo{folderNo} filesep], downsampleOptions.fileFilterRegexp);

		%         downsampleHDFMovieFxnObj(movieList,downsampleOptions);
		%     end
		%     % re-create folder structure
		%     clear ioptions
		%     %
		%     downsampleSaveFolder = [downsampleSettings{11} filesep];
		%     % used to determine which folders to copy from src to dest
		%     ioptions.srcFolderFilterRegexp = downsampleSettings{8};
		%     % this regexp is used to search the destination directory
		%     ioptions.srcSubfolderFileFilterRegexp = downsampleSettings{9};
		%     %
		%     ioptions.srcSubfolderFileFilterRegexpExt = downsampleSettings{10};
		%     [success destFolders2] = moveFilesToFolders(strsplit(downsampleSettings{3},','),char(downsampleSaveFolder(:))','options',ioptions);
		%     % destFolders = unique(destFolders);
		% end
		% destFolders
		% class(destFolders)

		% add folder info to inputFolders class property
		if isempty(obj.inputFolders)
			if iscell(destFolders)
				nAddFolders = length(destFolders);
				for addFolderNo = 1:nAddFolders
					obj.inputFolders{addFolderNo,1} = destFolders{addFolderNo};
				end
			else
				nAddFolders = 1;
				obj.inputFolders{1,1} = destFolders;
			end
			obj.dataPath = obj.inputFolders;
		else
			if iscell(destFolders)
				nAddFolders = length(destFolders);
				for addFolderNo = 1:nAddFolders
					obj.inputFolders{end+addFolderNo,1} = destFolders{addFolderNo};
				end
			else
				nAddFolders = 1;
				obj.inputFolders{end+1,1} = destFolders;
			end
			% obj.inputFolders = [obj.inputFolders(:); destFolders(:)];
			% obj.inputFolders = cat(obj.inputFolders,destFolders);
			obj.dataPath = obj.inputFolders;
			% obj.dataPath = cat(obj.dataPath,destFolders);
		end
		obj.modelGetFileInfo();
	catch err
		obj.foldersToAnalyze = [];
		display(repmat('@',1,7))
		disp(getReport(err,'extended','hyperlinks','on'));
		display(repmat('@',1,7))
	end
end
function downsampleTiffMovieFxnObj(folderPath,options)
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	options.deflateLevel = 1;

	% get list of regular expressions
	fileRegexpListOriginal = getFileList([folderPath filesep],options.srcSubfolderFileFilterRegexp);
	fileRegexpList = regexp(fileRegexpListOriginal, options.srcSubfolderFileFilterRegexp,'match');
	options.srcSubfolderFileFilterRegexp
	cellfun(@display,fileRegexpList);
	% fileRegexpList = getFileList([folderPath filesep], options.srcFolderFilterRegexp);
	nFileRegexp = length(fileRegexpList);

	% go over each set of regexp
	for fileRegexpNo=1:nFileRegexp
		disp('==========================================')
		thisFileRegexp = fileRegexpList{fileRegexpNo};
		thisFileRegexp = regexprep(thisFileRegexp,options.srcSubfolderFileFilterRegexpExt,'');
		thisFileRegexp = thisFileRegexp{1};
		display(thisFileRegexp)
		[~,thisFileRegexp,~] = fileparts(thisFileRegexp);
		% get a list of tiff associated with regexp
		[folderPath filesep]
		[thisFileRegexp '.*.tif$']
		movieList = getFileList([folderPath filesep], [thisFileRegexp '.*.tif$']);
		% due to naming convention, shift last to 1st
		movieList = circshift(movieList,[0 1]);
		cellfun(@display,movieList);
		% get path string
		[pathstr,name,ext] = fileparts(fileRegexpListOriginal{fileRegexpNo});
		newFilename = [options.downsampleSaveFolder filesep 'concat_' name '.h5'];
		display(['save HDF5: ' newFilename])
		% load and downsample each tiff
		nMovies = length(movieList);
		nMovies
		for movieNo=1:nMovies
			display(repmat('+',1,21))
			display(['downsampling ' num2str(movieNo) '/' num2str(nMovies)])
			inputFilePath = movieList{movieNo};
			display(['input: ' inputFilePath]);
			% [pathstr,name,ext] = fileparts(inputFilePath);

			% downsampleFilename = [pathstr '\concat_' name '.h5']
			doptions.downsampleDimension = 'space';
			doptions.downsampleType = 'bilinear';
			doptions.downsampleFactor = options.downsampleFactor;
			[inputMovie] = downsampleMovie(inputFilePath, 'options',doptions);
			% save the movie
			% save or append each tiff to corresponding HDF5 file
			if movieNo==1
				createHdf5File(newFilename, options.outputDatasetName, inputMovie,'deflateLevel',options.deflateLevel);
			else
				appendDataToHdf5(newFilename, options.outputDatasetName, inputMovie);
			end
		end
		srcFilenameTxt = [pathstr filesep name '.txt'];
		srcFilenameXml = [pathstr filesep name '.xml'];
		destFilenameTxt = [options.downsampleSaveFolder filesep name '.txt']
		destFilenameXml = [options.downsampleSaveFolder filesep name '.xml']
		if exist(srcFilenameTxt,'file')
			display([srcFilenameTxt ' > ' destFilenameTxt])
			copyfile(srcFilenameTxt,destFilenameTxt)
		elseif exist(srcFilenameXml,'file')
			display([srcFilenameXml ' > ' destFilenameXml])
			copyfile(srcFilenameXml,destFilenameXml)
		end
	end
end
function downsampleHDFMovieFxnObj(movieList,options)
	% downsamples an HDF5 movie, normally the raw recording files

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	% display(movieList)
	cellfun(@display,movieList);
	nMovies = length(movieList);
	for movieNo=1:nMovies
		display(repmat('+',1,21))
		display(['downsampling ' num2str(movieNo) '/' num2str(nMovies)])
		inputFilePath = movieList{movieNo};
		display(['input: ' inputFilePath]);
		[pathstr,name,ext] = fileparts(inputFilePath);
		downsampleFilename = [pathstr '\concat_' name '.h5'];
		srcFilenameTxt = [pathstr filesep name '.txt'];
		srcFilenameXml = [pathstr filesep name '.xml'];
		downsampleFilenameAlt = [options.downsampleSaveFolder '\concat_' name '.h5'];

		fprintf('downsampleFilename = %s\nsrcFilenameTxt = %s\nsrcFilenameXml = %s\n',downsampleFilename,srcFilenameTxt,srcFilenameXml)

		% currentDateTimeStr = datestr(now,'yyyymmdd_HHMM','local');
		% mkdir([thisDir filesep 'processing_info'])
		% diarySaveStr = [thisDir filesep 'processing_info' filesep currentDateTimeStr '_preprocess.log'];
		% display(['saving diary: ' diarySaveStr])
		% diary(diarySaveStr);

		try
			% see if file already exists at the destination
			if isempty(options.downsampleSaveFolder)&exist(downsampleFilename,'file')==0
				% check whether to downsample in different location
				if isempty(options.downsampleSaveFolder)
					downsampleHdf5Movie(inputFilePath, 'inputDatasetName', options.datasetName, 'maxChunkSize', options.maxChunkSize,'downsampleFactor',options.downsampleFactor,'outputDatasetName',options.outputDatasetName,'saveFolderTwo',options.downsampleSaveFolderTwo,'downsampleFactorTwo',options.downsampleFactorTwo);
				else
					downsampleHdf5Movie(inputFilePath, 'inputDatasetName', options.datasetName, 'maxChunkSize', options.maxChunkSize,'saveFolder',options.downsampleSaveFolder,'downsampleFactor',options.downsampleFactor,'outputDatasetName',options.outputDatasetName,'saveFolderTwo',options.downsampleSaveFolderTwo,'downsampleFactorTwo',options.downsampleFactorTwo);

					% copy information files to new downsample folder location
					destFilenameTxt = [options.downsampleSaveFolder filesep name '.txt'];
					destFilenameXml = [options.downsampleSaveFolder filesep name '.xml'];
					fprintf('destFilenameTxt = %s\destFilenameXml = %s\n',destFilenameTxt,destFilenameXml)
					if exist(srcFilenameTxt,'file')
						copyfile(srcFilenameTxt,destFilenameTxt)
					elseif exist(srcFilenameXml,'file')
						copyfile(srcFilenameXml,destFilenameXml)
					end
					if ~isempty(options.downsampleSaveFolderTwo)
						destFilenameTxt = [options.downsampleSaveFolderTwo filesep name '.txt']
						destFilenameXml = [options.downsampleSaveFolderTwo filesep name '.xml']
						if exist(srcFilenameTxt,'file')
							copyfile(srcFilenameTxt,destFilenameTxt)
						elseif exist(srcFilenameXml,'file')
							copyfile(srcFilenameXml,destFilenameXml)
						end
					end
				end
			elseif ~isempty(options.downsampleSaveFolder)&exist(downsampleFilenameAlt,'file')==0
				downsampleHdf5Movie(inputFilePath, 'inputDatasetName', options.datasetName, 'maxChunkSize', options.maxChunkSize,'saveFolder',options.downsampleSaveFolder,'downsampleFactor',options.downsampleFactor,'outputDatasetName',options.outputDatasetName,'saveFolderTwo',options.downsampleSaveFolderTwo,'downsampleFactorTwo',options.downsampleFactorTwo);
				destFilenameTxt = [options.downsampleSaveFolder filesep name '.txt']
				destFilenameXml = [options.downsampleSaveFolder filesep name '.xml']
				if exist(srcFilenameTxt,'file')
					copyfile(srcFilenameTxt,destFilenameTxt)
				elseif exist(srcFilenameXml,'file')
					copyfile(srcFilenameXml,destFilenameXml)
				end

				% copy information files to new downsample folder location
				if ~isempty(options.downsampleSaveFolderTwo)
					destFilenameTxt = [options.downsampleSaveFolderTwo filesep name '.txt']
					destFilenameXml = [options.downsampleSaveFolderTwo filesep name '.xml']
					if exist(srcFilenameTxt,'file')
						copyfile(srcFilenameTxt,destFilenameTxt)
					elseif exist(srcFilenameXml,'file')
						copyfile(srcFilenameXml,destFilenameXml)
					end
				end
			else
				display(['skipping: ' inputFilePath])
			end
		catch err
			display(repmat('@',1,7))
			disp(getReport(err,'extended','hyperlinks','on'));
			display(repmat('@',1,7))
		end
	end
end
function [downsampleSaveFolderMod, downsampleSaveFolderTwoMod] = downsampleIsxdMovieFxnObj(movieList,options)
	% downsamples an HDF5 movie, normally the raw recording files

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	% display(movieList)
	cellfun(@display,movieList);
	nMovies = length(movieList);
	for movieNo=1:nMovies
		display(repmat('+',1,21))
		display(['downsampling ' num2str(movieNo) '/' num2str(nMovies)])
		inputFilePath = movieList{movieNo};
		display(['input: ' inputFilePath]);
		[pathstr,name,ext] = fileparts(inputFilePath);
		downsampleFilename = [pathstr '\concat_' name '.h5'];
		srcFilenameTxt = [pathstr filesep name '.txt'];
		srcFilenameXml = [pathstr filesep name '.xml'];
		srcFilenameJson = [pathstr filesep 'session.json'];
		downsampleFilenameAlt = [options.downsampleSaveFolder '\concat_' name '.h5'];

		[~,folderName,~] = fileparts(strip(pathstr,'right',filesep));
		if isempty(options.downsampleSaveFolder)
			downsampleSaveFolderMod = options.downsampleSaveFolder;
		else
			downsampleSaveFolderMod = [options.downsampleSaveFolder filesep folderName];
		end
		if isempty(options.downsampleSaveFolderTwo)
			downsampleSaveFolderTwoMod = options.downsampleSaveFolderTwo;
		else
			downsampleSaveFolderTwoMod = [options.downsampleSaveFolderTwo filesep folderName];
		end
		if ~exist(downsampleSaveFolderMod,'dir');mkdir(downsampleSaveFolderMod);end
		if ~exist(downsampleSaveFolderTwoMod,'dir');mkdir(downsampleSaveFolderTwoMod);end

		if ~isempty(options.downsampleSaveFolder)
			% Copy JSON files
			jsonFileList = getFileList(pathstr,'.*.json');
			if ~isempty(jsonFileList)
				for jsonNo = 1:length(jsonFileList)
					jsonSrcFile = jsonFileList{jsonNo};
					[~,jsonName,ext] = fileparts(jsonSrcFile);
					jsonDestFile = [downsampleSaveFolderMod filesep jsonName ext];
					if exist(jsonSrcFile,'file')
						fprintf('jsonSrcFile = %s\jsonDestFile = %s\n',jsonSrcFile,jsonDestFile)
						copyfile(jsonSrcFile,jsonDestFile)
					end
					if ~isempty(options.downsampleSaveFolderTwo)
						jsonDestFile = [downsampleSaveFolderTwoMod filesep jsonName ext];
						if exist(jsonSrcFile,'file')
							fprintf('jsonSrcFile = %s\jsonDestFile = %s\n',jsonSrcFile,jsonDestFile)
							copyfile(jsonSrcFile,jsonDestFile)
						end
					end
				end
			end
		end
		% fprintf('downsampleFilename = %s\nsrcFilenameTxt = %s\nsrcFilenameXml = %s\n',downsampleFilename,srcFilenameTxt,srcFilenameXml)

		% currentDateTimeStr = datestr(now,'yyyymmdd_HHMM','local');
		% mkdir([thisDir filesep 'processing_info'])
		% diarySaveStr = [thisDir filesep 'processing_info' filesep currentDateTimeStr '_preprocess.log'];
		% display(['saving diary: ' diarySaveStr])
		% diary(diarySaveStr);

		try
			% see if file already exists at the destination
			if isempty(options.downsampleSaveFolder)&exist(downsampleFilename,'file')==0
				% check whether to downsample in different location
				if isempty(options.downsampleSaveFolder)
					convertInscopixIsxdToHdf5(inputFilePath, 'inputDatasetName', options.datasetName, 'maxChunkSize', options.maxChunkSize,'downsampleFactor',options.downsampleFactor,'outputDatasetName',options.outputDatasetName,'saveFolderTwo',downsampleSaveFolderTwoMod,'downsampleFactorTwo',options.downsampleFactorTwo);
				else
					convertInscopixIsxdToHdf5(inputFilePath, 'inputDatasetName', options.datasetName, 'maxChunkSize', options.maxChunkSize,'saveFolder',downsampleSaveFolderMod,'downsampleFactor',options.downsampleFactor,'outputDatasetName',options.outputDatasetName,'saveFolderTwo',downsampleSaveFolderTwoMod,'downsampleFactorTwo',options.downsampleFactorTwo);

					% copy information files to new downsample folder location
					% destFilenameTxt = [options.downsampleSaveFolder filesep name '.txt'];
					% destFilenameXml = [options.downsampleSaveFolder filesep name '.xml'];
					% fprintf('destFilenameTxt = %s\destFilenameXml = %s\n',destFilenameTxt,destFilenameXml)
					% if exist(srcFilenameTxt,'file')
					% 	copyfile(srcFilenameTxt,destFilenameTxt)
					% elseif exist(srcFilenameXml,'file')
					% 	copyfile(srcFilenameXml,destFilenameXml)
					% end
					% if ~isempty(options.downsampleSaveFolderTwo)
					% 	destFilenameTxt = [options.downsampleSaveFolderTwo filesep name '.txt']
					% 	destFilenameXml = [options.downsampleSaveFolderTwo filesep name '.xml']
					% 	if exist(srcFilenameTxt,'file')
					% 		copyfile(srcFilenameTxt,destFilenameTxt)
					% 	elseif exist(srcFilenameXml,'file')
					% 		copyfile(srcFilenameXml,destFilenameXml)
					% 	end
					% end
				end
			elseif ~isempty(options.downsampleSaveFolder)&exist(downsampleFilenameAlt,'file')==0
				convertInscopixIsxdToHdf5(inputFilePath, 'inputDatasetName', options.datasetName, 'maxChunkSize', options.maxChunkSize,'saveFolder',downsampleSaveFolderMod,'downsampleFactor',options.downsampleFactor,'outputDatasetName',options.outputDatasetName,'saveFolderTwo',downsampleSaveFolderTwoMod,'downsampleFactorTwo',options.downsampleFactorTwo);
				% destFilenameTxt = [options.downsampleSaveFolder filesep name '.txt']
				% destFilenameXml = [options.downsampleSaveFolder filesep name '.xml']
				% if exist(srcFilenameTxt,'file')
				% 	copyfile(srcFilenameTxt,destFilenameTxt)
				% elseif exist(srcFilenameXml,'file')
				% 	copyfile(srcFilenameXml,destFilenameXml)
				% end

				% copy information files to new downsample folder location
				% if ~isempty(options.downsampleSaveFolderTwo)
				% 	destFilenameTxt = [options.downsampleSaveFolderTwo filesep name '.txt']
				% 	destFilenameXml = [options.downsampleSaveFolderTwo filesep name '.xml']
				% 	if exist(srcFilenameTxt,'file')
				% 		copyfile(srcFilenameTxt,destFilenameTxt)
				% 	elseif exist(srcFilenameXml,'file')
				% 		copyfile(srcFilenameXml,destFilenameXml)
				% 	end
				% end
			else
				display(['skipping: ' inputFilePath])
			end
		catch err
			display(repmat('@',1,7))
			disp(getReport(err,'extended','hyperlinks','on'));
			display(repmat('@',1,7))
		end
	end
end

function [newFolderListCell] = subfxnSelectFolders()
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	nExistingFolders = 0;
	usrIdxChoiceStr = {'manually enter folders to list','GUI select folders','Use existing folder list'};
	scnsize = get(0,'ScreenSize');
	[sel, ok] = listdlg('ListString',usrIdxChoiceStr,'ListSize',[scnsize(3)*0.3 scnsize(4)*0.3],'Name','How to add folders with movies to downsample?');
	inputMethod = usrIdxChoiceStr{sel};

	switch inputMethod
		case 'Use existing folder list'
			newFolderListCell = {};
		case 'manually enter folders to list'
			AddOpts.Resize='on';
			AddOpts.WindowStyle='normal';
			AddOpts.Interpreter='tex';
			% inputdlg
			newFolderList = inputdlgcol('One new line per folder path. Enter folder path WITHOUT any single/double quotation marks around the path.','Adding folders to calciumImagingAnalysis object for downsampling.',[21 150],{''},AddOpts);
			if isempty(newFolderList)
				warning('No folders given. Please re-run modelDownsampleRawMovies.')
				return
			end
			newFolderList = newFolderList{1,1};
			% size(newFolderList)
			% class(newFolderList)

			% if obj.guiEnabled==1
			% 	scnsize = get(0,'ScreenSize');
			% 	[fileIdxArray, ok] = listdlg('ListString',obj.fileIDNameArray,'ListSize',[scnsize(3)*0.2 scnsize(4)*0.25],'Name','which folders to analyze?');
			% else
			% 	fileIdxArray = 1:length(obj.fileIDNameArray);
			% end
			nNewFolders = size(newFolderList,1);
			fileIdxArray = (nExistingFolders+1):(nExistingFolders+nNewFolders);
			nFolders = length(fileIdxArray);
			newFolderListCell = {};
			for thisFileNumIdx = 1:nFolders
				% strtrim(newFolderList(thisFileNumIdx,:))
				% class(strtrim(newFolderList(thisFileNumIdx,:)))
				newFolderListCell{thisFileNumIdx} = strtrim(newFolderList(thisFileNumIdx,:));
			end
		case 'GUI select folders'
			thisFileNumIdx = 1;
			newFolderListCell = {};
			pathToAdd = '';
			while ~isempty(pathToAdd)|length(newFolderListCell)<1
				try
					if ischar(pathToAdd)
						disp('Select a folder to add for downsampling. Press cancel to stop adding folders.')
						pathToAdd = uigetdir(pathToAdd,'Select a folder to add for downsampling. Press cancel to stop adding folders.');
						% If user cancels, do not add folder.
						if pathToAdd~=0
							fprintf('Adding folder #%d: %s.\n',thisFileNumIdx,pathToAdd);
							newFolderListCell{thisFileNumIdx} = pathToAdd;
							thisFileNumIdx = thisFileNumIdx+1;
						end
					else
						% Force exit
						pathToAdd = [];
					end
				catch err
					disp(repmat('@',1,7))
					disp(getReport(err,'extended','hyperlinks','on'));
					disp(repmat('@',1,7))
				end
			end
			nNewFolders = length(newFolderListCell);
			fileIdxArray = (nExistingFolders+1):(nExistingFolders+nNewFolders);
		otherwise
			% body
	end
end


function indxV = getAssociatedDose(uid)
%"getAssociatedDose"
%   Returns a dose index for corresponding dose uid passed.
%
%	DK 12/07/2006
% copyright (c) 2001-2008, Washington University in St. Louis.
% Permission is granted to use or modify only for non-commercial,
% non-treatment-decision applications, and further only if this header is
% not removed from any file. No warranty is expressed or implied for any
% use whatever: use at your own risk.  Users can request use of CERR for
% institutional review board-approved protocols.  Commercial users can
% request a license.  Contact Joe Deasy for more information
% (radonc.wustl.edu@jdeasy, reversed).
%
%Usage:
%   indxV = getAssociatedDose(uid)

global planC
indexS = planC{end};

% preallocate memory
if length(planC{indexS.dose})< 1
    indxV = [];
    return
end

[doseUID{1:length(planC{indexS.dose})}] = deal(planC{indexS.dose}.doseUID);

if length(uid)==0
    indxV = [];
else
    [jnk,indxV] = ismember(uid,doseUID);
    if indxV == 0;
        indxV = [];
    end
end


function varargout = main(varargin)
% Main: callback functions for main window
%
%    ABOUT
%
%      -Created:     January 2004
%      -Last update: February 13th, 2004
%      -Revision:    0.0.1
%      -Author:      R. S. Schestowitz, University of Manchester
% ==============================================================

% Edit the above text to modify the response to help_button register

% Last Modified by GUIDE v2.5 24-Mar-2004 17:39:06

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

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

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

% Choose default command line output for register
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

if strcmp(get(hObject,'Visible'),'off')
    initialize_gui(hObject, handles);
end

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

% Get default command line output from handles structure
varargout{1} = handles.output;

function initialize_gui(fig_handle, handles)

setappdata(0, 'test_data', '1');

function open_child_Callback(hObject, eventdata, handles)

child;

function show_params_Callback(hObject, eventdata, handles)

msgbox(getappdata(0, 'test_data'), 'Parameter Shown');


function cvx_expert_check( fname, varargin )

if ~isempty( varargin ) && ~any(cellfun('isclass',varargin,'cvx')), return; end
global cvx___
if cvx___.expert, return; end

url = [ 'file:///', cvx___.where, cvx___.fs, 'doc', cvx___.fs, 'advanced.html#the-successive-approximation-method' ];
fprintf( 1, [ 'CVX Warning:\n', ...
'   Models involving "%s" or other functions in the log, exp, and entropy\n', ...
'   family are solved using an experimental successive approximation method.\n', ...
'   This method is slower and less reliable than the method CVX employs for\n', ...
'   other models. Please see the section of the user''s guide entitled\n', ...
'       The successive approximation method\n', ...
'   for more details about the approach, and for instructions on how to\n', ...
'   suppress this warning message in the future.\n' ], fname, url );

cvx___.expert = true;

% Note that we do *not* call cvx_save_prefs here. We only save the
% preferences if an explicit setting of "cvx_expert true" is made.

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function [filename] = segFilename(present,uid,iid)
% function [filename] = segFileName(present,uid,iid)
%
% Return seg filename composed of components.
%
% INPUT
%	present		Presentation, one of {'gray','color'}.
%	uid		User ID.
%	iid		Image ID.
%
% OUTPUT
%	filename	Segmentation filename.
%
% David Martin <dmartin@eecs.berkeley.edu>
% January 2003

filename = fullfile(bsdsRoot,'human',present,sprintf('%d',uid),sprintf('%d.seg',iid));


function obj = setIndices(obj, index)
     % Sets indinces of the Nlp Variables
    %
    % Parameters:
    %  index: the indices of the variables @type integer

    assert(length(index) == obj.Dimension, ...
        'The length of the variable indices must be equal to the dimension of the NlpVariable object.');

    obj.Indices = index;
end


function [movieTmp] = viewAddTextToMovie(movieTmp,inputText,fontSize,varargin)
	% Adds text to movie matrix.
	% Biafra Ahanonu
	% inputs
		% inputSignal - input signal (or matrix)
	% outputs
		%

	[movieTmp] = ciapkg.video.viewAddTextToMovie(movieTmp,inputText,fontSize,'passArgs', varargin);
end


function vr = videoReader(url, varargin)
% videoReader class constructor
%   Creates a object that reads video streams.  We use a plugin
%   architecture in the backend to do the actual reading.  For example,
%   on Windows, DirectShow will typically be used and on Linux, the
%   ffmpeg library is often used.
%
%   vr = videoReader(url)
%     Opens the given video file for reading using the default plugin.
%     On Windows, 'DirectShow' is used by default and on Linux,
%     'ffmpegPopen2' is used by default.  For most plugins, the url will
%     really be a filename.
%
%   vr = videoReader(url, ..., 'plugin',pluginName, ...)
%   vr = videoReader(url,pluginName, ...)
%     Opens the file using the manually specified plugin implementation.
%     Available plugins include:
%
%     'DirectShow': preferred method on Windows
%       - Only available on Windows
%       - See INSTALL.dshow.txt for installation instructions
%       - Can load virtually any video file that can be played in
%         Microsoft's Windows Media Player.  Note that the correct codec
%         must be installed to read a file.  For example, to read
%         tests/numbers.3ivx.avi, the user must have installed an MPEG4
%         codec such as 3ivx (www.3ivx.com), DivX (www.divx.com), or XviD
%         (www.xvid.org).
%       - The URL parameter should be a filename.
%         - As a convenience, all forward slashes ('/') are automatically
%           converted to backslashes ('\')
%
%     'ffmpegPopen2': safe method on Linux
%       - Only supported on GNU/Linux (might work on BSD systems too like Mac
%         OS X, but this is untested)
%       - See INSTALL.ffmpeg.txt for installation instructions
%       - Creates a separate server process to communicate with the
%         ffmpeg libraries.
%         - Works when the system's version of GCC is very different from
%           the one that MathWorks used to compile Matlab.
%         - Isolates ffmpeg errors so they typically cannot crash
%           Matlab.
%         - May allow for more flexible distribution terms for your code
%           when it uses videoIO (ffmpeg may be compiled with either
%           the LGPL or GPL license).
%
%     'ffmpegDirect': low-overhead method on Linux
%       - same as ffmpegPopen2, but the ffmpeg libraries are loaded
%         directly by the MEX file.
%         - May not work if MathWorks' and your version of GCC are
%           incompatible.
%         - Slightly faster than ffmpegPopen2 since there is no
%           interprocess communication overhead.
%
%   vr = videoReader(url, ..., param,arg,...)
%     Allows the user to pass extra configuration arguments to plugin.
%     Currently no plugin arguments are supported right now.  In the
%     future, we may allow the user to do things like have DirectShow
%     automatically convert to grayscale, or give options to trade off
%     speed with seeking precision.
%
% Once you have created a videoReader object, you must next call NEXT,
% SEEK, or STEP at least once so that it will read some frame from disk.
% *After* calling one of these methods, you may call GETFRAME as many
% times as you would like and it will decode and return the current frame
% (without advancing to a different frame).  GETINFO may be called at any
% time (even before NEXT, SEEK, or STEP).  It returns basic information
% about the video stream.  Once you are done using the videoReader, make
% sure you call CLOSE so that any system resources allocated by the plugin
% may be released.  Here's a simple example of how you might use
% videoReader:
%
%   % take us to the videoReader directory since we know there's a video
%   % file there.
%   chdir(fileparts(which('videoReaderWrapper.cpp')));
%
%   % Construct a videoReader object
%   vr = videoReader('tests/numbers.uncompressed.avi');
%
%   % Do some processing on the video and display the results
%   avgIntensity = [];
%   i = 1;
%   figure;
%   while (next(vr))
%     img = getframe(vr);
%     avgIntensity(i) = mean(img(:));
%     subplot(121); imshow(img);        title('current frame');
%     subplot(122); plot(avgIntensity); title('avg. intensity vs. frame');
%     drawnow;      pause(0.1);         i = i+1;
%   end
%   vr = close(vr);
%
% SEE ALSO:
%   buildVideoMex
%   videoReader/close
%   videoReader/getframe
%   videoReader/getinfo
%   videoReader/getnext
%   videoReader/next
%   videoReader/seek
%   videoReader/step
%   videoWriter
%
%Copyright (c) 2006 Gerald Dalley
%See "MIT.txt" in the installation directory for licensing details (especially
%when using this library on GNU/Linux).

if (mod(length(varargin),2) == 0)
  plugin     = defaultVideoIOPlugin;
  pluginArgs = varargin;
else
  plugin     = varargin{1};
  pluginArgs = {varargin{2:end}};
end
[plugin,pluginArgs] = parsePlugin(plugin, pluginArgs);

vr = struct('plugin',mexName(plugin), 'handle',int32(-1));
vr = class(vr, 'videoReader');
[pathstr, name, ext, versn] = fileparts(url);
strArgs = cell(size(pluginArgs));
for i=1:numel(pluginArgs), strArgs{i} = num2str(pluginArgs{i}); end
vr.handle = feval(vr.plugin, 'open', vr.handle, ...
  fullfile(pathstr,[name ext versn]), strArgs{:});

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function n = mexName(plugin)
n = ['videoReader_' plugin];

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [plugin,pluginArgs] = parsePlugin(plugin, pluginArgs)
if (length(pluginArgs) > 0)
  [pluginSpecified,idx] = ismember('plugin', {pluginArgs{1:2:end}});
  if pluginSpecified
    plugin = pluginArgs{idx*2};
    pluginArgs = { pluginArgs{1:idx*2-2}, pluginArgs{idx*2+1:end} };
  end
end


function varargout = spm_BIDS(varargin)
% Parse and query a directory structure formated according to the BIDS standard
% FORMAT BIDS = spm_BIDS(root)
% root   - directory formated according to BIDS [Default: pwd]
% BIDS   - structure containing the BIDS file layout
%
% FORMAT result = spm_BIDS(BIDS,query,...)
% BIDS   - BIDS directory name or BIDS structure (from spm_BIDS)
% query  - type of query: {'data', 'metadata', 'sessions', 'subjects',
%          'runs', 'tasks', 'runs', 'types', 'modalities'}
% result - outcome of query
%__________________________________________________________________________
%
% BIDS (Brain Imaging Data Structure): https://bids.neuroimaging.io/
%   The brain imaging data structure, a format for organizing and
%   describing outputs of neuroimaging experiments.
%   K. J. Gorgolewski et al, Scientific Data, 2016.
%__________________________________________________________________________
% Copyright (C) 2016-2018 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_BIDS.m 7642 2019-07-19 15:04:40Z guillaume $

%-Validate input arguments
%==========================================================================
if ~nargin
    root = pwd;
elseif nargin == 1
    if ischar(varargin{1})
        root = spm_select('CPath',varargin{1});
    else
        varargout = varargin(1);
        return;
    end
else
    BIDS = spm_BIDS(varargin{1});
    varargout{1} = BIDS_query(BIDS,varargin{2:end});
    return;
end

%-BIDS structure
%==========================================================================

BIDS = struct(...
    'dir',root, ...               % BIDS directory
    'description',struct([]), ... % content of dataset_description.json
    'sessions',{{}},...           % cellstr of sessions
    'scans',struct([]),...        % content of sub-<participant_label>_scans.tsv (should go within subjects)
    'sess',struct([]),...         % content of sub-participants_label>_sessions.tsv (should go within subjects)
    'participants',struct([]),... % content of participants.tsv
    'subjects',struct([]));       % structure array of subjects

%-Validation of BIDS root directory
%==========================================================================
if isempty(BIDS.dir)
    error('A BIDS directory has to be specified.');
elseif ~exist(BIDS.dir,'dir')
    error('BIDS directory does not exist.');
elseif ~exist(fullfile(BIDS.dir,'dataset_description.json'),'file')
    error('BIDS directory not valid: missing dataset_description.json.');
end

%-Dataset description
%==========================================================================
try
    BIDS.description = spm_jsonread(fullfile(BIDS.dir,'dataset_description.json'));
catch
    error('BIDS dataset description could not be read.');
end
if ~isfield(BIDS.description,'BIDSVersion') || ~isfield(BIDS.description,'Name')
    error('BIDS dataset description not valid.');
end
% See also optional README and CHANGES files

%-Optional directories
%==========================================================================
% [code/]
% [derivatives/]
% [stimuli/]
% [sourcedata/]
% [phenotype]

%-Scans key file
%==========================================================================

% sub-<participant_label>/[ses-<session_label>/]
%     sub-<participant_label>_scans.tsv

%-Participant key file
%==========================================================================
p = spm_select('FPList',BIDS.dir,'^participants\.tsv$');
if ~isempty(p)
    BIDS.participants = spm_load(p);
end
p = spm_select('FPList',BIDS.dir,'^participants\.json$');
if ~isempty(p)
    BIDS.participants.meta = spm_jsonread(p);
end

%-Sessions file
%==========================================================================

% sub-<participant_label>/[ses-<session_label>/]
%      sub-<participant_label>[_ses-<session_label>]_sessions.tsv

%-Tasks: JSON files are accessed through metadata
%==========================================================================
%t = spm_select('FPList',BIDS.dir,...
%    '^task-.*_(beh|bold|events|channels|physio|stim|meg)\.(json|tsv)$');

%-Subjects
%==========================================================================
sub = cellstr(spm_select('List',BIDS.dir,'dir','^sub-.*$'));
if isequal(sub,{''})
    error('No subjects found in BIDS directory.');
end

for su=1:numel(sub)
    sess = cellstr(spm_select('List',fullfile(BIDS.dir,sub{su}),'dir','^ses-.*$'));
    for se=1:numel(sess)
        if isempty(BIDS.subjects)
            BIDS.subjects = parse_subject(BIDS.dir, sub{su}, sess{se});
        else
            BIDS.subjects(end+1) = parse_subject(BIDS.dir, sub{su}, sess{se});
        end
    end
end

varargout = { BIDS };

%==========================================================================
%-Parse a subject's directory
%==========================================================================
function subject = parse_subject(p, subjname, sesname)

subject.name    = subjname;   % subject name ('sub-<participant_label>')
subject.path    = fullfile(p,subjname,sesname); % full path to subject directory
subject.session = sesname; % session name ('' or 'ses-<label>')
subject.anat    = struct([]); % anatomy imaging data
subject.func    = struct([]); % task imaging data
subject.fmap    = struct([]); % fieldmap data
subject.beh     = struct([]); % behavioral experiment data
subject.dwi     = struct([]); % diffusion imaging data
subject.eeg     = struct([]); % EEG data
subject.meg     = struct([]); % MEG data
subject.pet     = struct([]); % PET imaging data

%--------------------------------------------------------------------------
%-Anatomy imaging data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'anat');
if exist(pth,'dir')
    f = spm_select('List',pth,...
        sprintf('^%s.*_([a-zA-Z0-9]+){1}\\.nii(\\.gz)?$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        %-Anatomy imaging data file
        %------------------------------------------------------------------
        p = parse_filename(f{i}, {'sub','ses','acq','ce','rec','fa','echo','inv','run'});
        subject.anat = [subject.anat p];

    end
end

%--------------------------------------------------------------------------
%-Task imaging data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'func');
if exist(pth,'dir')

    %-Task imaging data file
    %----------------------------------------------------------------------
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_bold\\.nii(\\.gz)?$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','rec','fa','echo','inv','run','recording', 'meta'});
        subject.func = [subject.func p];
        subject.func(end).meta = struct([]); % ?

    end

    %-Task events file
    %----------------------------------------------------------------------
    % (!) TODO: events file can also be stored at higher levels (inheritance principle)
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_events\\.tsv$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','rec','fa','echo','inv','run','recording', 'meta'});
        subject.func = [subject.func p];
        subject.func(end).meta = spm_load(fullfile(pth,f{i})); % ?

    end

    %-Physiological and other continuous recordings file
    %----------------------------------------------------------------------
    % (!) TODO: stim file can also be stored at higher levels (inheritance principle)
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_(physio|stim)\\.tsv\\.gz$',subject.name));
    % see also [_recording-<label>]
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','rec','fa','echo','inv','run','recording', 'meta'});
        subject.func = [subject.func p];
        subject.func(end).meta = struct([]); % ?

    end
end

%--------------------------------------------------------------------------
%-Fieldmap data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'fmap');
if exist(pth,'dir')
    f = spm_select('List',pth,...
        sprintf('^%s.*\\.nii(\\.gz)?$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    j = 1;

    %-Phase difference image and at least one magnitude image
    %----------------------------------------------------------------------
    labels = regexp(f,[...
        '^sub-[a-zA-Z0-9]+' ...              % sub-<participant_label>
        '(?<ses>_ses-[a-zA-Z0-9]+)?' ...     % ses-<label>
        '(?<acq>_acq-[a-zA-Z0-9]+)?' ...     % acq-<label>
        '(?<run>_run-[a-zA-Z0-9]+)?' ...     % run-<index>
        '_phasediff\.nii(\.gz)?$'],'names'); % NIfTI file extension
    if any(~cellfun(@isempty,labels))
        idx = find(~cellfun(@isempty,labels));
        for i=1:numel(idx)
            fb = spm_file(spm_file(f{idx(i)},'basename'),'basename');
            metafile = fullfile(pth,spm_file(fb,'ext','json'));
            subject.fmap(j).type = 'phasediff';
            subject.fmap(j).filename = f{idx(i)};
            subject.fmap(j).magnitude = {...
                strrep(f{idx(i)},'_phasediff.nii','_magnitude1.nii'),...
                strrep(f{idx(i)},'_phasediff.nii','_magnitude2.nii')}; % optional
            subject.fmap(j).ses = regexprep(labels{idx(i)}.ses,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).acq = regexprep(labels{idx(i)}.acq,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).run = regexprep(labels{idx(i)}.run,'^_[a-zA-Z0-9]+-','');
            if spm_existfile(metafile)
                subject.fmap(j).meta = spm_jsonread(metafile);
            else
                % (!) TODO: file can also be stored at higher levels (inheritance principle)
                subject.fmap(j).meta = struct([]); % ?
            end
            j = j + 1;
        end
    end

    %-Two phase images and two magnitude images
    %----------------------------------------------------------------------
    labels = regexp(f,[...
        '^sub-[a-zA-Z0-9]+' ...           % sub-<participant_label>
        '(?<ses>_ses-[a-zA-Z0-9]+)?' ...  % ses-<label>
        '(?<acq>_acq-[a-zA-Z0-9]+)?' ...  % acq-<label>
        '(?<run>_run-[a-zA-Z0-9]+)?' ...  % run-<index>
        '_phase1\.nii(\.gz)?$'],'names'); % NIfTI file extension
    if any(~cellfun(@isempty,labels))
        idx = find(~cellfun(@isempty,labels));
        for i=1:numel(idx)
            fb = spm_file(spm_file(f{idx(i)},'basename'),'basename');
            metafile = fullfile(pth,spm_file(fb,'ext','json'));
            subject.fmap(j).type = 'phase12';
            subject.fmap(j).filename = {...
                f{idx(i)},...
                strrep(f{idx(i)},'_phase1.nii','_phase2.nii')};
            subject.fmap(j).magnitude = {...
                strrep(f{idx(i)},'_phase1.nii','_magnitude1.nii'),...
                strrep(f{idx(i)},'_phase1.nii','_magnitude2.nii')};
            subject.fmap(j).ses = regexprep(labels{idx(i)}.ses,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).acq = regexprep(labels{idx(i)}.acq,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).run = regexprep(labels{idx(i)}.run,'^_[a-zA-Z0-9]+-','');
            if spm_existfile(metafile)
                subject.fmap(j).meta = {...
                    spm_jsonread(metafile),...
                    spm_jsonread(strrep(metafile,'_phase1.json','_phase2.json'))};
            else
                % (!) TODO: file can also be stored at higher levels (inheritance principle)
                subject.fmap(j).meta = struct([]); % ?
            end
            j = j + 1;
        end
    end

    %-A single, real fieldmap image
    %----------------------------------------------------------------------
    labels = regexp(f,[...
        '^sub-[a-zA-Z0-9]+' ...             % sub-<participant_label>
        '(?<ses>_ses-[a-zA-Z0-9]+)?' ...    % ses-<label>
        '(?<acq>_acq-[a-zA-Z0-9]+)?' ...    % acq-<label>
        '(?<run>_run-[a-zA-Z0-9]+)?' ...    % run-<index>
        '_fieldmap\.nii(\.gz)?$'],'names'); % NIfTI file extension
    if any(~cellfun(@isempty,labels))
        idx = find(~cellfun(@isempty,labels));
        for i=1:numel(idx)
            fb = spm_file(spm_file(f{idx(i)},'basename'),'basename');
            metafile = fullfile(pth,spm_file(fb,'ext','json'));
            subject.fmap(j).type = 'fieldmap';
            subject.fmap(j).filename = f{idx(i)};
            subject.fmap(j).magnitude = strrep(f{idx(i)},'_fieldmap.nii','_magnitude.nii');
            subject.fmap(j).ses = regexprep(labels{idx(i)}.ses,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).acq = regexprep(labels{idx(i)}.acq,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).run = regexprep(labels{idx(i)}.run,'^_[a-zA-Z0-9]+-','');
            if spm_existfile(metafile)
                subject.fmap(j).meta = spm_jsonread(metafile);
            else
                % (!) TODO: file can also be stored at higher levels (inheritance principle)
                subject.fmap(j).meta = struct([]); % ?
            end
            j = j + 1;
        end
    end

    %-Multiple phase encoded directions (topup)
    %----------------------------------------------------------------------
    labels = regexp(f,[...
        '^sub-[a-zA-Z0-9]+' ...          % sub-<participant_label>
        '(?<ses>_ses-[a-zA-Z0-9]+)?' ... % ses-<label>
        '(?<acq>_acq-[a-zA-Z0-9]+)?' ... % acq-<label>
        '_dir-(?<dir>[a-zA-Z0-9]+)?' ... % dir-<index>
        '(?<run>_run-[a-zA-Z0-9]+)?' ... % run-<index>
        '_epi\.nii(\.gz)?$'],'names');   % NIfTI file extension
    if any(~cellfun(@isempty,labels))
        idx = find(~cellfun(@isempty,labels));
        for i=1:numel(idx)
            fb = spm_file(spm_file(f{idx(i)},'basename'),'basename');
            metafile = fullfile(pth,spm_file(fb,'ext','json'));
            subject.fmap(j).type = 'epi';
            subject.fmap(j).filename = f{idx(i)};
            subject.fmap(j).ses = regexprep(labels{idx(i)}.ses,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).acq = regexprep(labels{idx(i)}.acq,'^_[a-zA-Z0-9]+-','');
            subject.fmap(j).dir = labels{idx(i)}.dir;
            subject.fmap(j).run = regexprep(labels{idx(i)}.run,'^_[a-zA-Z0-9]+-','');
            if spm_existfile(metafile)
                subject.fmap(j).meta = spm_jsonread(metafile);
            else
                % (!) TODO: file can also be stored at higher levels (inheritance principle)
                subject.fmap(j).meta = struct([]); % ?
            end
            j = j + 1;
        end
    end
end

%--------------------------------------------------------------------------
%-EEG data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'eeg');
if exist(pth,'dir')

    %-EEG data file
    %----------------------------------------------------------------------
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_eeg\\..*[^json]$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','meta'});
        subject.eeg = [subject.eeg p];
        subject.eeg(end).meta = struct([]); % ?

    end

    %-EEG events file
    %----------------------------------------------------------------------
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_events\\.tsv$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','meta'});
        subject.eeg = [subject.eeg p];
        subject.eeg(end).meta = spm_load(fullfile(pth,f{i})); % ?

    end

    %-Channel description table
    %----------------------------------------------------------------------
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_channels\\.tsv$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','meta'});
        subject.eeg = [subject.eeg p];
        subject.eeg(end).meta = spm_load(fullfile(pth,f{i})); % ?

    end

    %-Session-specific file
    %----------------------------------------------------------------------
    f = spm_select('List',pth,...
        sprintf('^%s(_ses-[a-zA-Z0-9]+)?.*_(electrodes\\.tsv|photo\\.jpg|coordsystem\\.json|headshape\\..*)$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','acq','meta'});
        subject.eeg = [subject.eeg p];
        subject.eeg(end).meta = struct([]); % ?

    end

end

%--------------------------------------------------------------------------
%-MEG data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'meg');
if exist(pth,'dir')

    %-MEG data file
    %----------------------------------------------------------------------
    [f,d] = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_meg\\..*[^json]$',subject.name));
    if isempty(f), f = d; end
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','proc', 'meta'});
        subject.meg = [subject.meg p];
        subject.meg(end).meta = struct([]); % ?

    end

    %-MEG events file
    %----------------------------------------------------------------------
    % (!) TODO: events file can also be stored at higher levels (inheritance principle)
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_events\\.tsv$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','proc', 'meta'});
        subject.meg = [subject.meg p];
        subject.meg(end).meta = spm_load(fullfile(pth,f{i})); % ?

    end

    %-Channels description table
    %----------------------------------------------------------------------
    % (!) TODO: channels file can also be stored at higher levels (inheritance principle)
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_channels\\.tsv$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','proc', 'meta'});
        subject.meg = [subject.meg p];
        subject.meg(end).meta = spm_load(fullfile(pth,f{i})); % ?

    end

    %-Session-specific file
    %----------------------------------------------------------------------
    f = spm_select('List',pth,...
        sprintf('^%s(_ses-[a-zA-Z0-9]+)?.*_(photo\\.jpg|coordsystem\\.json|headshape\\..*)$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        p = parse_filename(f{i}, {'sub','ses','task','acq','run','proc', 'meta'});
        subject.meg = [subject.meg p];
        subject.meg(end).meta = struct([]); % ?

    end

end

%--------------------------------------------------------------------------
%-Behavioral experiments data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'beh');
if exist(pth,'dir')
    f = spm_select('FPList',pth,...
        sprintf('^%s.*_(events\\.tsv|beh\\.json|physio\\.tsv\\.gz|stim\\.tsv\\.gz)$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        %-Event timing, metadata, physiological and other continuous
        % recordings
        %------------------------------------------------------------------
        p = parse_filename(f{i}, {'sub','ses','task'});
        subject.beh = [subject.beh p];

    end
end

%--------------------------------------------------------------------------
%-Diffusion imaging data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'dwi');
if exist(pth,'dir')
    f = spm_select('FPList',pth,...
        sprintf('^%s.*_([a-zA-Z0-9]+){1}\\.nii(\\.gz)?$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        %-Diffusion imaging file
        %------------------------------------------------------------------
        p = parse_filename(f{i}, {'sub','ses','acq','run', 'bval','bvec'});
        subject.dwi = [subject.dwi p];

        %-bval file
        %------------------------------------------------------------------
        % bval file can also be stored at higher levels (inheritance principle)
        bvalfile = get_metadata(f{i},'^.*%s\\.bval$');
        if isfield(bvalfile,'filename')
            subject.dwi(end).bval = spm_load(bvalfile.filename); % ?
        end

        %-bvec file
        %------------------------------------------------------------------
        % bvec file can also be stored at higher levels (inheritance principle)
        bvecfile = get_metadata(f{i},'^.*%s\\.bvec$');
        if isfield(bvalfile,'filename')
            subject.dwi(end).bvec = spm_load(bvecfile.filename); % ?
        end

    end
end

%--------------------------------------------------------------------------
%-Positron Emission Tomography imaging data
%--------------------------------------------------------------------------
pth = fullfile(subject.path,'pet');
if exist(pth,'dir')
    f = spm_select('List',pth,...
        sprintf('^%s.*_task-.*_pet\\.nii(\\.gz)?$',subject.name));
    if isempty(f), f = {}; else f = cellstr(f); end
    for i=1:numel(f)

        %-PET imaging file
        %------------------------------------------------------------------
        p = parse_filename(f{i}, {'sub','ses','task','acq','rec','run'});
        subject.pet = [subject.pet p];

    end
end

%==========================================================================
%-Perform a BIDS query
%==========================================================================
function result = BIDS_query(BIDS,query,varargin)
opts = parse_query(varargin);
switch query
%   case 'subjects'
%       result = regexprep(unique({BIDS.subjects.name}),'^[a-zA-Z0-9]+-','');
    case 'sessions'
        result = unique({BIDS.subjects.session});
        result = regexprep(result,'^[a-zA-Z0-9]+-','');
        result = unique(result);
        result(cellfun('isempty',result)) = [];
    case 'modalities'
        hasmod = arrayfun(@(y) structfun(@(x) isstruct(x) & ~isempty(x),y),...
            BIDS.subjects,'UniformOutput',false);
        hasmod = any([hasmod{:}],2);
        mods   = fieldnames(BIDS.subjects)';
        result = mods(hasmod);
    case {'subjects', 'tasks', 'runs', 'types', 'data', 'metadata'}
        %-Initialise output variable
        result = {};
        %-Filter according to subjects
        if any(ismember(opts(:,1),'sub'))
            subs = opts{ismember(opts(:,1),'sub'),2};
            opts(ismember(opts(:,1),'sub'),:) = [];
        else
            subs = unique({BIDS.subjects.name});
            subs = regexprep(subs,'^[a-zA-Z0-9]+-','');
        end
        %-Filter subjects according to participants.tsv
        if any(ismember(opts(:,1),'participants'))
            p = find(ismember(opts(:,1),'participants'));
            for i=1:numel(p)
                q = opts{p(i),2};
                fn = fieldnames(q);
                for j=1:numel(fn)
                    if iscell(BIDS.participants.(fn{j}))
                        idx = ismember(BIDS.participants.(fn{j}),q.(fn{j}));
                    else
                        if isa(q.(fn{j}),'function_handle')
                            idx = feval(q.(fn{j}),BIDS.participants.(fn{j}));
                        else
                            idx = BIDS.participants.(fn{j}) == q.(fn{j});
                        end
                    end
                    s = regexprep(BIDS.participants.participant_id(idx),'^[a-zA-Z0-9]+-','');
                    subs = intersect(subs, s);
                end
            end
            opts(p,:) = [];
        end
        %-Filter according to modality
        if any(ismember(opts(:,1),'modality'))
            mods = opts{ismember(opts(:,1),'modality'),2};
            opts(ismember(opts(:,1),'modality'),:) = [];
        else
            mods = BIDS_query(BIDS,'modalities');
        end
        %-Get optional target option for metadata query
        if strcmp(query,'metadata') && any(ismember(opts(:,1),'target'))
            target = opts{ismember(opts(:,1),'target'),2};
            opts(ismember(opts(:,1),'target'),:) = [];
            if iscellstr(target)
                target = substruct('.',target{1});
            end
        else
            target = [];
        end
        %-Perform query
        for i=1:numel(BIDS.subjects)
            if ~ismember(BIDS.subjects(i).name(5:end),subs), continue; end
            for j=1:numel(mods)
                d = BIDS.subjects(i).(mods{j});
                for k=1:numel(d)
                    sts = true;
                    for l=1:size(opts,1)
                        if ~isfield(d(k),opts{l,1}) || ~ismember(d(k).(opts{l,1}),opts{l,2})
                            sts = false;
                        end
                    end
                    switch query
                        case 'subjects'
                            if sts
                                result{end+1} = BIDS.subjects(i).name;
                            end
                        case 'data'
                            if sts && isfield(d(k),'filename')
                                result{end+1} = fullfile(BIDS.subjects(i).path,mods{j},d(k).filename);
                            end
                        case 'metadata'
                            if sts && isfield(d(k),'filename')
                                f = fullfile(BIDS.subjects(i).path,mods{j},d(k).filename);
                                result{end+1} = get_metadata(f);
                                if ~isempty(target)
                                    try
                                        result{end} = subsref(result{end},target);
                                    catch
                                        warning('Non-existent field for metadata.');
                                        result{end} = [];
                                    end
                                end
                            end
%                             if sts && isfield(d(k),'meta')
%                                 result{end+1} = d(k).meta;
%                             end
                        case 'runs'
                            if sts && isfield(d(k),'run')
                                result{end+1} = d(k).run;
                            end
                        case 'tasks'
                            if sts && isfield(d(k),'task')
                                result{end+1} = d(k).task;
                            end
                        case 'types'
                            if sts && isfield(d(k),'type')
                                result{end+1} = d(k).type;
                            end
                    end
                end
            end
        end
        %-Postprocessing output variable
        switch query
            case 'subjects'
                result = unique(result);
                result = regexprep(result,'^[a-zA-Z0-9]+-','');
            case 'data'
                result = result';
            case 'metadata'
                if numel(result) == 1
                    result = result{1};
                end
            case {'tasks','runs','types'}
                result = unique(result);
                result(cellfun('isempty',result)) = [];
        end
    otherwise
        error('Unable to perform BIDS query.');
end

%==========================================================================
%-Parse BIDS query
%==========================================================================
function query = parse_query(query)
if numel(query) == 1 && isstruct(query{1})
    query = [fieldnames(query{1}), struct2cell(query{1})];
else
    if mod(numel(query),2)
        error('Invalid input syntax.');
    end
    query = reshape(query,2,[])';
end
for i=1:size(query,1)
    if ischar(query{i,2})
        query{i,2} = cellstr(query{i,2});
    end
    for j=1:numel(query{i,2})
        if iscellstr(query{i,2})
            query{i,2}{j} = regexprep(query{i,2}{j},sprintf('^%s-',query{i,1}),'');
        end
    end
end

%==========================================================================
%-Parse filename
%==========================================================================
function p = parse_filename(filename,fields)
filename = spm_file(filename,'filename');
[parts, dummy] = regexp(filename,'(?:_)+','split','match');
p.filename = filename;
[p.type, p.ext] = strtok(parts{end},'.');
for i=1:numel(parts)-1
    [d, dummy] = regexp(parts{i},'(?:\-)+','split','match');
    p.(d{1}) = d{2};
end
if nargin == 2
    for i=1:numel(fields)
        if ~isfield(p,fields{i})
            p.(fields{i}) = '';
        end
    end
    try
        p = orderfields(p,['filename','ext','type',fields]);
    catch
        warning('Ignoring file "%s" not matching template.',filename);
        p = struct([]);
    end
end

%==========================================================================
%-Get metadata
%==========================================================================
function meta = get_metadata(filename, pattern)
if nargin == 1, pattern = '^.*%s\\.json$'; end
pth = fileparts(filename);
p = parse_filename(filename);

meta = struct();

if isfield(p,'ses') && ~isempty(p.ses)
    N = 4; % there is a session level in the hierarchy
else
    N = 3;
end

for n=1:N
    metafile = spm_select('FPList',pth, sprintf(pattern,p.type));
    if isempty(metafile), metafile = {}; else metafile = cellstr(metafile); end
    for i=1:numel(metafile)
        p2 = parse_filename(metafile{i});
        fn = setdiff(fieldnames(p2),{'filename','ext','type'});
        ismeta = true;
        for j=1:numel(fn)
            if ~isfield(p,fn{j}) || ~strcmp(p.(fn{j}),p2.(fn{j}))
                ismeta = false;
                break;
            end
        end
        if ismeta
            if strcmp(p2.ext,'.json')
                meta = update_metadata(meta,spm_jsonread(metafile{i}));
            else
                meta.filename = metafile{i};
            end
        end
    end
    pth = fullfile(pth,'..');
end

%==========================================================================
%-Inheritance principle
%==========================================================================
function s1 = update_metadata(s1,s2)
fn = fieldnames(s2);
for i=1:numel(fn)
    if ~isfield(s1,fn{i})
        s1.(fn{i}) = s2.(fn{i});
    end
end


function x = uplus( x )
    %UPLUS Unary plus.
    %   X = UPLUS(X) does nothing.
    %
    %   See also UMINUS.

    %   TTeMPS Toolbox.
    %   Michael Steinlechner, 2013-2016
    %   Questions and contact: michael.steinlechner@epfl.ch
    %   BSD 2-clause license, see LICENSE.txt

end


function planC = getplanCDownSample(planC, optS, index)

indexS = planC{end};

for i = 1: size(planC{indexS.scan}.scanArray,3)
    planC{indexS.scan}.scanInfo(1,i).grid1Units = index*planC{indexS.scan}.scanInfo(1,i).grid1Units;
    planC{indexS.scan}.scanInfo(1,i).grid2Units = index*planC{indexS.scan}.scanInfo(1,i).grid2Units;
    planC{indexS.scan}.scanInfo(1,i).sizeOfDimension2 = size(planC{indexS.scan}.scanArray,2)/index;
    planC{indexS.scan}.scanInfo(1,i).sizeOfDimension1 = size(planC{indexS.scan}.scanArray,1)/index;
end

planC{indexS.scan}.scanArray = getDownsample3(planC{indexS.scan}.scanArray,index,1);

planC{indexS.scan}.scanArraySuperior = [];
planC{indexS.scan}.scanArrayInferior = [];
planC{indexS.scan}.uniformScanInfo = [];

for i=1:length(planC{indexS.structures})
    planC{indexS.structures}(i).rasterSegments =[];
end

planC = getRasterSegs(planC, optS);

planC = setUniformizedData(planC, optS);

try
	for i=1:length(planC{indexS.dose})
        planC = clearCache(planC, i);
	end
end


function obj_moveBack(ho)
%OBJ_MOVEBACK - Move graphic object to the background
%
%Synposis:
% obj_moveBack(<HO>)
%
%Input:
% HO: handle of graphic object, default: last plotted object

if ~exist('ho', 'var'),
  ho= gco;
elseif length(ho)>1,
  for h= ho(:)',
    obj_moveBack(h);
  end
  return;
end

hp= get(ho, 'parent');
hc= get(hp, 'children');
if ~exist('ho', 'var') || isempty(ho),
  hi= length(hc);
else
  hi= find(hc==ho);
  if isempty(hi),
    error('handle not found in current axes');
  end
end

set(hp, 'children', [hc([1:hi-1, hi+1:end, hi])]);