function cm = render_on_surface(obj, surface_handles, varargin)
% Map voxels in obj to isosurface (patch) handles in han and change surface colors according to values in obj
%
% :Usage:
% ::
%
%     cm = render_on_surface(obj, han, [optional inputs])
%
% - Object can be thresholded or unthresholded statistic_image, or other image_vector object
% - Uses only the first image in obj
%
% ..
%     Author and copyright information:
%
%     Copyright (C) 2020 Tor Wager
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     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, see <http://www.gnu.org/licenses/>.
% ..
%
% :Inputs:
%
%   **obj:**
%       - image_vector object or subclasses
%       - e.g., fmri_data, thresholded or unthresholded statistic_image obj
%       - Uses only the first image in obj
%
%   **han:**
%       - A vector of one or more handles to patch objects (from surfaces)
%       - e.g., from patch(isosurface()), patch(isocaps()),
%       fmri_data.surface(), fmri_data.isosurface()
%       - Try: surface_handles = addbrain('left_cutaway');
%       - Try: surface_handles = addbrain('coronal_slabs_4');
%
% :Optional Inputs:
%   **'clim':**
%        Followed by [lowerlimit upperlimit] values to define color limits
%
%   **'colormap':**
%        Followed by the name of a Matlab colormap string
%        e.g., 'hot' (default), 'summer', 'winter'
%        - Creates a split colormap with gray values where object values
%        are zero, and colors where object values are nonzero
%
%   **'indexmap':**
%       Interpret data as indices into colormap. You must specify
%       'colormap' argument as well, otherwise this does nothing and a
%       warning is thrown.
%
% :Outputs:
%
%   **renders colors on surfaces:**
%   Note that it is not always possible to reset the surfaces to their
%   original colors completely; you may have to redraw them if you change
%   the threshold/voxels that should be colored.
%
% :Examples:
% ::
%
% % Load a standard dataset (emotion regulation test data)
% % Perform a t-test, and render the results on a series of coronal slabs
%
% imgs = load_image_set('emotionreg', 'noverbose');
% t = ttest(imgs, .01, 'unc');
% figure; han = addbrain('coronal_slabs_4');
%
% % When the t map has positive and negative values, creates a special
% % bicolor split colormap that has warm colors for positive vals, and cool colors
% % for negative vals:
% render_on_surface(t, han);
%
% % You can set the color limits (here, in t-values)
% render_on_surface(t, han, 'clim', [-4 4]);
%
% % You can set the colormap to any Matlab colormap:
% render_on_surface(t, han, 'colormap', 'summer');
%
% % If your image is positive-valued only (or negative-valued only), a
% % bicolor split map will not be created:
%
% t = threshold(t, [2 Inf], 'raw-between');
% render_on_surface(t, han, 'colormap', 'winter', 'clim', [2 6]);
%
% Note:
% To erase rendered blobs, use:
% sh = addbrain('eraseblobs', sh);

% Programmers' Notes:
% Tor Wager, Jan 2020
% desirable features:
% - Speed
% - control of colormap, can update
% - consistent value->color mapping across surfaces
% - need to know meaning of values, as they often represent statistic values
% - can re-run without changing colors
% - need to plot on isosurface or isocaps objects that use colormap to
% render gray-scale images.
% This last part makes it a bit more complicated.
% Rendering only on solid-color patch objects from isosurfaces is easier:
%     c = isocolors(mesh_struct.X, mesh_struct.Y, mesh_struct.Z, mesh_struct.voldata, han(i));
%     set(han(i), 'FaceColor', 'interp')
%     c_orig = get(han(i), 'FaceVertexCData');
%     han(i).FaceVertexCData = c;
%
% But to render on either solid-color or gray scale colormapped patches, we want to use
% a split colormap.
%
% Different surface handles (to patches) will have FaceColor is an rgb
% triplet (for solid-color patches) or 'interp' for colormapped patches
% (like isocaps). This will be set to interp for all patches, but we have
% to preserve the existing colormapped grayscale values for interp patches
% by using the split colormap.
%
% Sept 2020: add solid-color 'color' option
% revise surface() method to use this

if any(~ishandle(surface_handles))
    error('Some surface_handles are not valid handles');
end

% -------------------------------------------------------------------------
% OPTIONAL INPUTS
% -------------------------------------------------------------------------

% cm = [];
nvals = 256;                % number of values for each segment of colormap
colormapname = 'hot';       % default colormap name (all Matlab defined colormaps OK)
custom_colormap = false;
pos_colormap = [];
neg_colormap = [];
clim = [];
axis_handle = get(surface_handles, 'Parent');          % axis handle to apply colormap to; can be altered with varargin
dolegend = true;
doindexmap = false;

allowable_keyword_value_pairs = {'clim' 'color' 'colormap' 'colormapname' 'axis_handle' 'pos_colormap' 'neg_colormap'};

% optional inputs with default values - each keyword entered will create a variable of the same name

for i = 1:length(varargin)
    if ischar(varargin{i})
        switch varargin{i}

            case 'color'
                % Special instructions for solid colors
                mycolor = varargin{i+1};
                validateattributes(mycolor, {'numeric'}, {'>=', 0, '<=', 1, 'size', [1 3]})

                nvals = 256;
                pos_colormap = repmat(mycolor, nvals, 1);

            case 'colormap'

                colormapname = varargin{i+1}; varargin{i+1} = [];
                custom_colormap = true;

            case 'cmaprange'
                % option to match region.montage method

                clim =  varargin{i+1}; varargin{i+1} = [];

            case 'nolegend'

                dolegend = false;

            case 'indexmap'
                if ~contains('colormap',varargin(cellfun(@ischar,varargin))),...
                        warning('render_on_surface() given ''indexmap'' argument, but no ''colormap'' argument. ''indexmap'' doesn''t do anything without a ''colormap'' argument.');
                end

                doindexmap = true;

            case allowable_keyword_value_pairs

                eval([varargin{i} ' = varargin{i+1}; varargin{i+1} = [];']);

                % eliminate this here because we may have passed many irrelevant values in in surface() call
                %otherwise, warning(['Unknown input string option:' varargin{i}]);
        end
    end
end

% There are 2 ways to enter custom colormaps.
% 1. By name
% 'colormap', [name of Matlab colormap]
%
% 2. Enter colormaps generated externally
% 'pos_colormap', followed by [n x 3] color vectors and/or
% 'neg_colormap', followed by [n x 3] color vectors
%
% If you enter 'pos_colormap' / 'neg_colormap', these will be stacked and
% added to a gray colormap

% Get range of values in object
% -----------------------------------------------------------------------
% These will be used to map color limits and create legend
% we will map values into the range of [1 nvals] where nvals = colormap length
% Deal with possibility of multiple images in object
% Exclude zeros

[datvec, clim] = get_data_range(obj, clim);

% -------------------------------------------------------------------------
% Define colormap
% -------------------------------------------------------------------------

if ~isempty(pos_colormap) ||  ~isempty(neg_colormap)

    if any(strcmp(varargin, 'colormap'))
        error('Entering ''colormap'' argument and name cannot be done when entering custom colormaps in ''pos_colormap''/''neg_colormap''');
    end

    if ~isempty(pos_colormap) && ~isempty(neg_colormap) && size(pos_colormap, 1) ~= size(neg_colormap, 1)
        error('Lengths of positive-color and negative-color colormaps must match.')
    end

    if ~isempty(pos_colormap)
        validateattributes(pos_colormap, {'numeric'}, {'>=', 0, '<=', 1, 'size', [NaN 3]})
        nvals = size(pos_colormap, 1);
    end

    if ~isempty(neg_colormap)
        validateattributes(neg_colormap, {'numeric'}, {'>=', 0, '<=', 1, 'size', [NaN 3]})
        nvals = size(neg_colormap, 1);
    end

    if isempty(pos_colormap)
        pos_colormap = hot(nvals);
    end

    if isempty(neg_colormap)
        neg_colormap = cool(nvals);
    end

    custom_colormap = true;

    % Build the colormap
    % -----------------------------------------------------------------------
    % Skip colormap generator, already
    % found the range of colors for pos/neg values to split around 0 here.
    % this sets the colormap for axis_handle
    [cm, kpos, kneg] = hotcool_split_colormap(nvals, clim, axis_handle, pos_colormap(1, :), pos_colormap(end, :), neg_colormap(1, :), neg_colormap(end, :));

    % colormapname = [neg_colormap; pos_colormap];   % colormapname is either name or [nvals x 3] matrix
    % nvals = size(colormapname, 1);                 % needs to match for color and gray maps to work right

else

    % Build the colormap
    % -----------------------------------------------------------------------
    % See notes below

    if doindexmap

        nvals = min([256, length(unique(obj.dat))]);

    end

    if custom_colormap

        [cm, kpos, kneg] = split_colormap(nvals, colormapname, axis_handle); % colormapname is either name or [nvals x 3] matrix

    elseif diff(sign(clim))

        % Default colormap for objects with - and + values
        [cm, kpos, kneg] = hotcool_split_colormap(nvals, clim, axis_handle);

    else
        [cm, kpos, kneg] = split_colormap(nvals, colormapname, axis_handle);
    end

end % custom posneg or other colormap

% -------------------------------------------------------------------------
% Change colors
% -------------------------------------------------------------------------

% Map object data values to indices in split colormap
% -----------------------------------------------------------------------
% Define mapping function from image values -> colormap indices (1-512).
% We will apply this later to vertex color data (only colored)
% Defining this here preserves the same mapping across different surfaces
% clim(1) mapped to lowest color, clim(2) to highest color
% map_function = @(c) 1 + nvals + (c - clim(1)) ./ range(clim) .* nvals;

map_function = @(c, x1, x2, y1, y2)  y1 + (c - x1) * (y2 - y1) ./ (x2 - x1);

% problem with above is that border gets mapped to low color when interpolating, which is
% blue for split colormap, creating blue outline around orange blobs. SO
% we need to map separately for pos and neg colored blobs
% ***

% problem with below is that vertices near empty (zero) voxels get interpolated down to zero
%map_function = @(c) 1 + nvals + (c - 0) ./ range(clim) .* nvals;

% Reconstruct volume data
% -----------------------------------------------------------------------
% Needed to map to vertices later
[~, ~, mesh_struct] = reconstruct_image(obj);                % get volume data for slices

% Deal with edge interpolation effects
% problem is that vertices near empty (zero) voxels get interpolated down to zero
% solution: map all non-zero voxels to lowest limit value
% Need to preserve signs as well.
% Then replace empty voxels with gray
% lower lim to interp to 10% of the way towards zero,
% preserving sign
% lowerclimit = linspace(clim(1), 0, 10);
% lowerclimit = lowerclimit(2);
% mesh_struct.voldata(mesh_struct.voldata == 0) = lowerclimit;
% lowerclimit = 0; ... wh = c == 0 | isnan(c);

% Kludgy fix for interpolation issues around border of blobs
% Changing c or c_colored won't do it because of how gray and colored
% colormaps are stacked. With one color, we interpolate into the gray zone,
% into about the top 256-150 values.
% for i = 256:-1:150
%     cm(i, :) = [.5 .5 .5];
% end
% colormap(cm)

for i = 1:length(surface_handles)
    % For each surface handle entered
    % -----------------------------------------------------------------------

    % Get vertex colors for image (obj) to map
    % -----------------------------------------------------------------------
    % Use isocolors to get colormapped values
    % Convert to index color in split colormap by adding nvals to put in
    % colored range
    % isocolors returns nans sometimes even when no NaNs in data
    c = isocolors(mesh_struct.X, mesh_struct.Y, mesh_struct.Z, mesh_struct.voldata, surface_handles(i));
    if doindexmap, c = round(c); end

    % doesn't work to fix interpolation error.
    %     border_percent = prctile(abs(c(c ~= 0)), 80);
    %     whlow = abs(c) < border_percent | isnan(c);

    c_colored = c;

    whpos = c > 0;
    %     kpos = 61;   % which block of 256 colors; depends on colormap
    cpos = map_function(c(whpos), 0, clim(2), (kpos-1)*nvals+1, kpos*nvals); % map into indices in hot cm range of colormap
    c_colored(whpos) = cpos;

    whneg = c < 0;
    %     kneg = 55;   % which block of 256 colors
    cneg = map_function(c(whneg), clim(1), 0, (kneg-1)*nvals+1, kneg*nvals); % map into indices in cool cm range of colormap
    c_colored(whneg) = cneg;

    wh = c == 0 | isnan(c);                    % save these to replace with gray-scale later

    %c_colored = map_function(c);    % Map to colormap indices (nvals = starting range, nvals elements)

    % FIX: rescale range so we don't map into gray range at edges due to
    % interpolation
    %     xx = c_colored(~wh); % in-blob vertices
    %     xx = 1 + nvals + max(xx) .* (xx - min(xx)) ./ (range(xx) + nvals);
    %     c_colored(~wh) = xx;
    %cm(256, :) = [1 0 1];

    % Get the original grayscale values
    % -----------------------------------------------------------------------
    % Exclude colored values

    c_gray = get(surface_handles(i), 'FaceVertexCData');

    if isempty(c_gray)

        % solid
        % c_gray = repmat(round(nvals ./ 2), size(get(surface_handles(i), 'Vertices'), 1), 1);
        c_gray = repmat(0 .* nvals + .5 .* round(nvals), size(get(surface_handles(i), 'Vertices'), 1), 1);

    else
        %orig: c_gray(~wh) = (c_gray(~wh) - min(c_gray(~wh))) ./ range(c_gray(~wh)) .* nvals;

        if range(c_gray(wh)) == 0
            % Solid surface color
            % do nothing - skip rescaling
            % -----------------------------------------------------------------------
            %c_colored(wh) = c_gray(wh);

        else
            % rescale to gray part of colormap
            c_gray(wh) = (c_gray(wh) - min(c_gray(wh))) ./ range(c_gray(wh)) .* nvals;
            % ***

            % Now merge graycsale and object-mapped colors
            % -----------------------------------------------------------------------
            % Everything that was originally zero in isocolors c
            % gets replaced with its original grayscale values in c_gray

            %c_colored(wh) = c_gray(wh);

        end
    end

    c_colored(wh) = c_gray(wh);

    % Set object handle properties
    % -----------------------------------------------------------------------
    % Change from 'scaled' to 'direct' mode

    % Save FaceVertexCData f or later
    prevdata = get(surface_handles(i), 'FaceVertexCData');
    set(surface_handles(i), 'UserData', prevdata);

    set(surface_handles(i), 'FaceVertexCData', c_colored);  % dot indexing sometimes works, sometimes doesn't...depends on handle type
    if ~doindexmap
        set(surface_handles(i), 'FaceColor', 'interp');
    else
        set(surface_handles(i), 'FaceColor', 'flat');
    end
    set(surface_handles(i), 'CDataMapping', 'direct')
    set(surface_handles(i), 'EdgeColor', 'none');

end

% Colorbars - legend
% -----------------------------------------------------------------------

if ~dolegend, return, end

if any(datvec > 0)

    bar1axis = axes('Position', [.55 .55 .38 .4]);
    colormap(bar1axis, cm(1+(kpos-1)*nvals:kpos*nvals, :))
    colorbar_han = colorbar(bar1axis);
    set(bar1axis, 'Visible', 'off');

    minpos = min(datvec(datvec > 0));
    set(colorbar_han, 'YTick', [0 1], 'YTickLabel', round([minpos clim(2)] * 100)/100, 'FontSize', 18);

end

if any(datvec < 0)

    bar2axis = axes('Position', [.55 .1 .38 .4]);
    colormap(bar2axis, cm(1+(kneg-1)*nvals:kneg*nvals, :))
    colorbar_han = colorbar(bar2axis);
    set(bar2axis, 'Visible', 'off');

    maxneg = max(datvec(datvec < 0));
    set(colorbar_han, 'YTick', [0 1], 'YTickLabel', round([clim(1) maxneg] * 100)/100, 'FontSize', 18);

end

end % main function

% -----------------------------------------------------------------------
% -----------------------------------------------------------------------
% Subfunctions
% -----------------------------------------------------------------------
% -----------------------------------------------------------------------

function [datvec, clim] = get_data_range(obj, clim)

if isa(obj, 'statistic_image')

    sig = logical(obj.sig);
    dat = obj.dat(sig);

else

    wh = obj.dat ~= 0 & ~isnan(obj.dat);
    dat = obj.dat(wh);

end

datvec = dat(:);

if any(isinf(datvec))
    warning('Some image values are Inf. Expect erratic behavior/errors.');
    whinf = isinf(datvec);
    datvec(whinf) = sign(datvec(whinf)) .* max(abs(datvec(~whinf)));
end

if isempty(clim)

    %clim = [min(datvec) max(datvec)];  % clim: data values for min and max, should become min/max colors

    if any(datvec < 0) && any(datvec > 0) % split colormap

        clim = double([prctile(datvec(datvec < 0), 10) prctile(datvec(datvec > 0), 90)]); % Match defaults for montage in render_blobs

    else
        % may need adjustment
        clim = [min(datvec) max(datvec)];

    end

    % if no variance, we have constant data values - special case.
    % reducing lower clim(1) will effectively map all data to max color value
    if abs(diff(clim)) < 100 * eps
        clim(1) = clim(2) - 1;
    end

end

end % function

% -----------------------------------------------------------------------
% -----------------------------------------------------------------------
% Colormap subfunctions
% -----------------------------------------------------------------------
% -----------------------------------------------------------------------

% - This will include nvals (generally 256) gray-scale and nvals colored
% values. If nvals = 256, the first 256 colors will be grayscale, and the
% next 256 color values.
% - If the object has positive and negative values, assume we want a split
% colormap showing negative values in cool colors, positive values in hot
% colors. The split colormap will also have different ranges for negative and
% positive values
% - If object is positive-valued or negative-valued only, assume we want a unipolar colormap

function [cm, kpos, kneg] = hotcool_split_colormap(nvals, clim, axis_handle, varargin)
%
% cm = hotcool_split_colormap(nvals, clim, [lowhot hihot lowcool hicool]), each is [r g b] triplet
%
% Get hotcool split colormap (around 0)
% note: colormaps with these colors match spm_orthviews_hotcool_colormap used in orthviews()
% [.4 .4 .3], [1 1 0] (hot/pos) and [0 0 1], [.4 .3 .4] (cool/neg)

% Split blue/yellow
% lowhot = [.4 .4 .3];
% hihot = [1 1 0];
% lowcool = [0 0 1];
% hicool = [.4 .3 .4];

% Default addblobs - orange/pink
hihot = [1 1 0]; % max pos, most extreme values
lowhot = [1 .4 .5]; % [.8 .3 0]; % min pos
hicool = [0 .8 .8]; % [.3 .6 .9]; % max neg
lowcool = [0 0 1]; % min neg, most extreme values

graybuffer = 20;  % for border - fade to gray in lowest k values

if ~isempty(varargin)
    if length(varargin) < 4
        error('Enter no optional arguments or 4 [r g b] color triplets');
    end
    lowhot = varargin{1};
    hihot = varargin{2};
    lowcool = varargin{3};
    hicool = varargin{4};
end

cmgray = gray(nvals);

thr = 0;    % Threshold to split colormap into two colors

% vec = linspace(clim(1), clim(2), nvals);
% pos = find(vec > thr);
% np = length(pos);

[hotcm, coolcm] = deal([]);

if graybuffer

    hotcm = [colormap_tor([.5 .5 .5], lowhot, 'n', graybuffer); ...
        colormap_tor(lowhot, hihot, 'n', nvals-graybuffer)];

else

    hotcm = colormap_tor(lowhot, hihot, 'n', nvals); %np);

end

%hotcm(1, :) = [.5 .5 .5]; % first element is gray
% end

% neg = find(vec < -thr);
% nn = length(neg);

% if nn > 0
%coolcm(end, :) = [.5 .5 .5]; % last element is gray
% end

if graybuffer

    coolcm = [colormap_tor(lowcool, hicool, 'n', nvals-graybuffer); ...
        colormap_tor(hicool, [.5 .5 .5], 'n', graybuffer)];

else

    coolcm = colormap_tor(lowcool, hicool, 'n', nvals); % nn);

end

% for interpolation issues
% 1:256 is gray-scale anat; 257:512 is buffer; 513:768 is negcm; 769:1024
% is buffer; 1025:1280 is poscm
buffercm = repmat([.5 .5 .5], nvals, 1);

% which block of nvals (usually 256) has colored values
kpos = 61;
kneg = 55;

%cm = [coolcm; buffercm; buffercm; cmgray; hotcm];
cm = [cmgray; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    buffercm; buffercm; buffercm; buffercm; coolcm; ...
    buffercm; buffercm; buffercm; buffercm; buffercm; ...
    hotcm];

% Kludgy fix for interpolation issues
% cm(128+3*256:4*256, :) = .5;
% cm(1+3*256:50+3*256, :) = .5;

% cm(128+0*256:1*256, :) = .5;
% cm(1+0*256:50+0*256, :) = .5;

% cm = [cmgray; cm];

% Change colormap(s)
if isempty(axis_handle)
    colormap(cm)

elseif iscell(axis_handle)

    for i = 1:length(axis_handle)
        colormap(axis_handle{i}, cm);
    end

else
    colormap(axis_handle, cm);
end

end

function [cm, kpos, kneg] = split_colormap(nvals, cmcolor, axis_handle)
% Get split colormap with gray then colored values
% e.g., cm = split_colormap(256, [2 8], 'hot')
%
% colormapname is either name or [nvals x 3] matrix

%validateattributes(cmcolor, {'numeric' 'char'})

% which block of nvals (usually 256) has colored values
kpos = 12;
kneg = 12;

cmgray = gray(nvals);

if ischar(cmcolor)
    % Named colormap function
    cmcolor = eval(sprintf('%s(%d)', cmcolor, nvals));
end

validateattributes(cmcolor, {'numeric'}, {'>=', 0, '<=', 1, 'size', [NaN 3]})

buffercm = repmat([.5 .5 .5], nvals, 1);

cm = [cmgray; repmat(buffercm, 10, 1); cmcolor];

% Change colormap(s)
if isempty(axis_handle)
    colormap(cm)

elseif iscell(axis_handle)

    for i = 1:length(axis_handle)
        colormap(axis_handle{i}, cm);
    end

else
    colormap(axis_handle, cm);
end

end


function [favar]=loadsignres_favar(pref,favar,endo)

%%%%% Routine for information variables X
% preliminary tasks
% identify the number of endo
favar.numendo=size(endo,1);

% initiate the cells signrestable, signresperiods and signreslabels %%%% I think this step is not necessary
% favar.signrestable=cell(favar.nsignres,favar.numendo); %number of information variables to be restricted, one for the moment
% favar.signresperiods=cell(favar.nsignres,favar.numendo); %number of information variables to be restricted, one for the moment
% favar.signreslabels=favar.informationvariablestrings{1,1}; %number of information variables to be restricted, one for the moment
% load the data from Excel
% sign restrictions values
[~,~,strngs1]=xlsread(pref.excelFile,'favar.sign res values');
[~,~,strngs2]=xlsread(pref.excelFile,'favar.sign res periods');
% replace NaN entries by blanks
strngs1(cellfun(@(x) any(isnan(x)),strngs1))={[]};
strngs2(cellfun(@(x) any(isnan(x)),strngs2))={[]};
% convert all numeric entries into strings
strngs1(cellfun(@isnumeric,strngs1))=cellfun(@num2str,strngs1(cellfun(@isnumeric,strngs1)),'UniformOutput',0);
strngs2(cellfun(@isnumeric,strngs2))=cellfun(@num2str,strngs2(cellfun(@isnumeric,strngs2)),'UniformOutput',0);
% identify the non-empty entries (pairs of rows and columns)
[nerows1,neclmns1]=find(~cellfun('isempty',strngs1));
[nerows2,neclmns2]=find(~cellfun('isempty',strngs2));
% count the number of such entries
neentries1=size(nerows1,1);
neentries2=size(nerows2,1);
% all these entries contrain strings: fix them to correct potential user formatting errors
% loop over entries (value table)
for ii=1:neentries1
strngs1{nerows1(ii,1),neclmns1(ii,1)}=bear.utils.fixstring(strngs1{nerows1(ii,1),neclmns1(ii,1)});
end
% loop over entries (period table)
for ii=1:neentries2
strngs2{nerows2(ii,1),neclmns2(ii,1)}=bear.utils.fixstring(strngs2{nerows2(ii,1),neclmns2(ii,1)});
end

% information variables in X that are restricted
neclmns1index=neclmns1==1;
nerows1index=nerows1(neclmns1index,1);

favar.signresX=strngs1(nerows1index,1);
%number of restricted information variables
favar.nsignresX=size(favar.signresX,1);

%create indices for restricted information variables (advantage here: ordering in the sign res table is irrelevant)
    for jj=1:favar.nsignresX
        for ii=1:favar.nfactorvar
        favar.signresX_indexlogical{jj,1}(ii,1)=strcmp(favar.informationvariablestrings(1,ii),favar.signresX{jj,1}); % do we need this output in favar.?
        end
    end
    for jj=1:favar.nsignresX
        favar.signresX_index(jj,1)=find(favar.signresX_indexlogical{jj,1}==1);
    end

% sign restriction values

% loop over endogenous variables
for ii=1:favar.numendo
    [~,c]=find(strcmp(strngs1,endo{ii,1}));
for ll=1:favar.nsignresX
[r,~]=find(strcmp(favar.signresX,favar.informationvariablestrings{ll,1}));% first entry number of information variables to be restricted, one for the moment
   % if it is not possible to find two entries, return an error
%    if size(r,1)<2
%    message=['favar Sign restriction error: information variable ' favar.informationvariablestrings{ll,1} ' cannot be found in both rows and columns of the table. Please verify that the ''sign res values'' sheet of the Excel data file is properly filled.'];
%    msgbox(message);
%    error('programme termination: sign restriction error');
%    end
% otherwise, the greatest number in r corresponds to the row of the column labels: record it
rows(favar.nsignresX,1)=max(r);
% the greatest number in c corresponds to the column of the row labels: record it
clmns(ii,1)=max(c);
end
end
% now recover the values for the cell signrestable
% loop over endogenous (rows)
for ii=1:favar.nsignresX % loop over restricted information variables
   for jj=1:favar.numendo % loop over endogenous (columns)
   favar.signrestable{ii,jj}=strngs1{rows(ii,1),clmns(jj,1)};
   end
end
%favar sign res table can be integrated to the normal one. take the
%signrestable definition (endpoints) from the normal one, and just start
%the favar table after that

% check whether the restriction table is valid: return an error if the the column requirement for zero restrictions is not satisfied
% loop over columns
for ii=1:favar.numendo
% count the number of zero restrictions in this column
numzerores=sum(strcmp(favar.signrestable(:,ii),'0'));
   % if there are too many zero restrictions for the column, return an error
   if numzerores>favar.numendo-ii
   temp=['Zero restriction issue: you have requested ' num2str(numzerores) ' zero restrictions in column ' num2str(ii) ' of the restriction matrix, but at most ' num2str(favar.numendo-ii) ' such restrictions can be implemented.'];
   msgbox(temp);
   error('Zero restriction error');
   end
end

% sign restriction periods

% recover the rows and columns of each endogenous variable
% loop over endogenous variables
for ii=1:favar.numendo
% for each variable, there should be two entries in the table corresponding to its name
% one is the column lable, the other is the row label
[~,c]=find(strcmp(strngs1,endo{ii,1}));
[r,~]=find(strcmp(strngs1,favar.informationvariablestrings{ll,1}));% first entry number of information variables to be restricted, one for the moment
   % if it is not possible to find two entries, return an error
%    if size(r,1)<2
%    message=['Sign restriction error: endogenous variable ' favar.informationvariablestrings{ii,1} ' cannot be found in both rows and columns of the table. Please verify that the ''sign res periods'' sheet of the Excel data file is properly filled.'];
%    msgbox(message);
%    error('programme termination: sign restriction error');
%    end
% the greatest number in r corresponds to the row of the column labels: record it
rows(favar.nsignresX,1)=max(r);
% the greatest number in c corresponds to the column of the row labels: record it
clmns(ii,1)=max(c);
end
% now recover the values for the cell signresperiods
% loop over endogenous (rows)
for ii=1:1 %number of information variables to be restricted, one for the moment
   % loop over endogenous (columns)
   for jj=1:favar.numendo
   % record the value
   favar.signresperiods{ii,jj}=str2num(strngs2{rows(ii,1),clmns(jj,1)});
   end
end

% recover the labels, if any
% initiate
favar.signreslabels=cell(favar.numendo,1);
% loop over endogenous (columns)
for ii=1:favar.numendo
% the label for shock ii is found in strngs2, row 3 and column 'clmns(ii,1)'
temp=strngs2{1,clmns(ii,1)};
   % if empty, give the generic name 'shock ii'
   if isempty(temp)
   favar.signreslabels{ii,1}=['shock ' num2str(ii)];
   % else, record the name
   else
   favar.signreslabels{ii,1}=temp;
   end
end

% finally, record on Excel
if pref.results==1
    bear.xlswritegeneral(fullfile(pref.results_path, [pref.results_sub '.xlsx']),strngs1,'favar.sign res values','B2');
    bear.xlswritegeneral(fullfile(pref.results_path, [pref.results_sub '.xlsx']),strngs2,'favar.sign res periods','B2');
end


function [  ] = DngToJpg( dngFile,qual,gamma )
%DNGTOJPG Summary of this function goes here
%   Detailed explanation goes here

    [raw, info] = Load_Data_and_Metadata_from_DNG(dngFile);
    if exist('gamma','var')
        srgb = run_pipeline(raw,info,'raw','srgb');
        srgb=srgb.^gamma;
        imwrite(srgb, [dngFile(1:end-4),'_gamma.JPG'], 'quality', qual);
    else
        srgb = run_pipeline(raw,info,'raw','tone');
        imwrite(srgb, [dngFile(1:end-4), '.JPG'], 'quality', qual);
    end
end


function [feasible,feaslistLMI] = isfeasible(F,tol)

if nargin == 1
    tol = 0;
end
feaslistLMI = check(F);
feasible = all(feaslistLMI >= -tol);


%islscalar - Test if parameter is a (pseudo) logical scalar.
%
%  USAGE
%
%    test = islscalar(x)
%
%    x              parameter to test
%
%  NOTE
%
%    To be considered logical, the scalar should be equal to 0 or 1, but it does
%    not need to actually be of class 'logical' (class(x) could be e.g. 'double').
%
%  SEE ALSO
%
%    See also islvector, islmatrix, isdmatrix, isdvector, isdscalar, isimatrix, isivector,
%    isstring_FMAT.
%

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

function test = islscalar(x)

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

% Test: double, scalar
test = islogical(x) & isscalar(x);

function test = islogical(x)

test = builtin('islogical',x) | all(x(:)==0|x(:)==1);


function filename=cosmo_make_temp_filename(prefix,suffix)
% give temporary filename that does not exist when this function is called
%
% filename=cosmo_make_temp_filename(prefix,suffix)
%
% Inputs:
%   prefix              optional, string with prefix for temporary file
%                       (default: '')
%   suffix              string [or cellstring] with suffix [suffixes] for
%                       temporary file [files]
%                       (default: '')
%
% Output:
%   filename            filename that does not exist when this function was
%                       called (in the current directory), starting with
%                       prefix, ending with suffix, and with a random infix
%                       string.
%                       If suffix is a cellstring, then filename is a
%                       cellstring with as many elements as suffix,
%                       each with the same prefix and random infix.
%
% Examples:
%   % generate random filename
%   fn=cosmo_make_temp_filename();
%
%   % generate random filename starting with 'foo'
%   fn=cosmo_make_temp_filename('foo');
%
%   % generate random filename starting with 'foo' and ending with '.bar'
%   fn=cosmo_make_temp_filename('foo','.bar');
%
%   % generate two random filename, both starting with 'foo', and ending
%   % with '.bar' and '.baz', respectively. The output fns is a cell with
%   % two strings
%   fns=cosmo_make_temp_filename('foo',{'.bar','.bar'});
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

    if nargin<2, suffix=''; end
    if nargin<1, prefix=''; end

    check_input(prefix,suffix);

    suffix_is_cell=iscell(suffix);

    if suffix_is_cell
        suffix_cell=suffix;
    else
        suffix_cell={suffix};
    end

    nsuffix=numel(suffix_cell);

    filename_cell=cell(nsuffix,1);
    while true
        infix=generate_random_infix();

        does_exist=false;

        for k=1:nsuffix
            fn=[prefix infix suffix_cell{k}];

            if exist(fn,'file')
                does_exist=true;
                break;
            end

            filename_cell{k}=fn;
        end

        if ~does_exist
            break;
        end
    end

    if suffix_is_cell
        filename=filename_cell;
    else
        filename=filename_cell{1};
    end

function infix=generate_random_infix(infix_length)
    if nargin<1
        infix_length=20;
    end

    char_val_min=double('a');
    char_val_max=double('z');
    char_val_range=char_val_max-char_val_min;

    rand_chars=char(rand(1,infix_length)*char_val_range+char_val_min);
    infix=['tmp_' rand_chars];

function check_input(prefix,suffix)
    if ~ischar(prefix)
        error('prefix must be a string');
    end

    if ~ischar(suffix) && ~iscellstr(suffix)
        error('suffix must be string or cell with strings');
    end


% GET_BOUNDARY_INDICES: given a set of indices (elements or dofs) in the
%  whole domain, collect those that belong to a given boundary.
%
%   bnd_indices = get_boundary_indices (iside, size_dir, indices)
%
% INPUT:
%
%   iside:    number of the side where to compute the indices
%   size_dir: the size in each direction (usually given by ndof_dir or nel_dir)
%   indices:  set of indices in the whole domain
%
% OUTPUT:
%
%   bnd_indices: set of indices that belong to the boundary, with the
%     local numbering of the boundary.
%
% Copyright (C) 2015 Rafael Vazquez
%
%    This program is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.

%    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, see <http://www.gnu.org/licenses/>.

function bnd_indices = get_boundary_indices (iside, size_dir, indices)

  ndim = numel (size_dir);
  ind2 = ceil (iside/2);
  ind = setdiff (1:ndim, ind2);

  if (mod(iside,2) == 1)
    boundary_ind = 1;
  else
    boundary_ind = size_dir (ind2);
  end

  indsub = cell (1, ndim);
  [indsub{:}] = ind2sub (size_dir, indices);
  aux = find (indsub{ind2} == boundary_ind);
  ppp = cellfun (@(x) x(aux), indsub(ind), 'UniformOutput', false);
  bnd_indices = sub2ind ([size_dir(ind), 1], ppp{:});

end


function class_map=GetClassMap(label,cmap)
img_size=size(label);
class_map=cmap(label(:)+1,:);
class_map=reshape(class_map,[img_size(1), img_size(2),3]);
class_map=uint8(class_map);


% This file is part of the Matlab Toolbox for Dimensionality Reduction v0.7.2b.
% The toolbox can be obtained from http://homepage.tudelft.nl/19j49
% You are free to use, change, or redistribute this code in any way you
% want for non-commercial purposes. However, it is appreciated if you
% maintain the name of the original author.
%
% (C) Laurens van der Maaten, 2010
% University California, San Diego / Delft University of Technology

function scatter12n(name,data,labels)

ld=length(data(1,:));
    if ld==2
        hf=figure;
        set(hf,'name',name,'NumberTitle','off');
        %if handles.islb
            scatter(data(:,1),data(:,2),5,labels);
%         else
%             scatter(data(:,1),data(:,2),5);
%         end
        title(name);
    else
        if ld==1
            hf=figure;
            set(hf,'name',name,'NumberTitle','off');
            %if handles.islb
                scatter(data(:,1),zeros(length(data(:,1)),1),5,labels);
%             else
%                 scatter(data(:,1),zeros(length(data(:,1)),1),5);
%             end
            title(name);
        else
            %if handles.islb
                scattern(name,data,labels);
%             else
%                 scattern('Result of dimensionality reduction',data);
%             end
        end

    end


function y = variablereplace(y,oldVar,newVar)

var = y.lmi_variables;

[~,pos] = ismember(oldVar,var);
if any(pos)
    index = find(pos);
    pos = pos(index);
    y.lmi_variables(pos) = newVar(index);

    [var,pos] = sort(y.lmi_variables);
    y.lmi_variables = var;
    y.basis = [y.basis(:,1) y.basis(:,1+pos)];
end


classdef TestTonemapDrago
    %TestTonemapDrago

    methods (Static)
        function test_1
            hdr = cv.imread(fullfile(mexopencv.root(),'test','memorial.hdr'), 'Flags',-1);
            hdr = cv.resize(hdr, 0.5, 0.5);

            obj = cv.TonemapDrago();
            obj.Gamma = 2.2;
            obj.Saturation = 1.0;
            obj.Bias = 0.85;

            ldr = obj.process(hdr);
            validateattributes(ldr, {'single'}, ...
                {'size',size(hdr), 'nonnegative'});
        end
    end

end


function varargout = process_ft_sourceanalysis_dics(varargin )
% PROCESS_FT_SOURCEANALYSIS Call FieldTrip function ft_sourceanalysis (DICS)

% @=============================================================================
% 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: Vahab YoussofZadeh, Francois Tadel, 2021

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'FieldTrip: ft_sourceanalysis (DICS)';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Sources';
    sProcess.Index       = 357;
    sProcess.Description = 'https://github.com/vyoussofzadeh/DICS-beamformer-for-Brainstorm';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data'};
    sProcess.OutputTypes = {'data'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;

    % Option: Sensors selection
    sProcess.options.sensortype.Comment = 'Sensor type:';
    sProcess.options.sensortype.Type    = 'combobox_label';
    sProcess.options.sensortype.Value   = {'MEG', {'MEG', 'MEG GRAD', 'MEG MAG', 'EEG', 'SEEG', 'ECOG'; ...
                                                   'MEG', 'MEG GRAD', 'MEG MAG', 'EEG', 'SEEG', 'ECOG'}};
    % Label: Time
    sProcess.options.label1.Comment = '
Time of interest:';
    sProcess.options.label1.Type    = 'label';
    % Active time window
    sProcess.options.poststim.Comment = 'Active (post-stim):';
    sProcess.options.poststim.Type    = 'poststim';
    sProcess.options.poststim.Value   = [];
    % Baseline time window
    sProcess.options.baseline.Comment = 'Baseline (pre-stim):';
    sProcess.options.baseline.Type    = 'baseline';
    sProcess.options.baseline.Value   = [];

    % Label: Frequency
    sProcess.options.label2.Comment = '
Frequency of interset:';
    sProcess.options.label2.Type    = 'label';
    % Enter the FOI in the data in Hz, eg, 22:
    sProcess.options.foi.Comment = 'FOI:';
    sProcess.options.foi.Type    = 'value';
    sProcess.options.foi.Value   = {18, 'Hz', 0};
    % Enter the Tapering frequency in the data in Hz, eg, 4:
    sProcess.options.tpr.Comment = 'Tapering freq:';
    sProcess.options.tpr.Type    = 'value';
    sProcess.options.tpr.Value   = {4, 'Hz', 0};

    % Label: Contrast
    sProcess.options.label4.Comment = '
Contrasting analysis:'; % Contrast between pre and post, across trials
    sProcess.options.label4.Type    = 'label';
    % Contrast
    sProcess.options.method.Comment = {'Subtraction (post-pre)', 'Permutation-stats (post-vs-pre)', 'Contrasting:'; ...
                                       'subtraction', 'permutation', ''};
    sProcess.options.method.Type    = 'radio_linelabel';
    sProcess.options.method.Value   = 'subtraction';
    % ERDS
    sProcess.options.erds.Comment = {'ERD', 'ERS', 'both', 'ERD/S effects:'; ...
                                     'erd', 'ers', 'both', ''};
    sProcess.options.erds.Type    = 'radio_linelabel';
    sProcess.options.erds.Value   = 'erd';
    % Effects
    sProcess.options.effect.Comment = {'abs', 'raw', 'Absolute/raw value of the contrast:'; ...
                                       'abs', 'raw', ''};
    sProcess.options.effect.Type    = 'radio_linelabel';
    sProcess.options.effect.Value   = 'abs';

    % Label: TFR
    sProcess.options.label3.Comment = '
Time-freq response (used for data inspection):';
    sProcess.options.label3.Type    = 'label';
    % Max frequency
    sProcess.options.maxfreq.Comment = 'Max frequency (for TFR calculation):';
    sProcess.options.maxfreq.Type    = 'value';
    sProcess.options.maxfreq.Value   = {40, 'Hz', 0};
    % Plot time-freq
    sProcess.options.showtfr.Comment = 'Plot time-frequency figure for visual';
    sProcess.options.showtfr.Type    = 'checkbox';
    sProcess.options.showtfr.Value   = 1;
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    OutputFiles = {};
    % Initialize FieldTrip
    [isInstalled, errMsg] = bst_plugin('Install', 'fieldtrip');
    if ~isInstalled
        bst_report('Error', sProcess, [], errMsg);
        return;
    end

    % ===== GET OPTIONS =====
    % Inverse options
    Method   = sProcess.options.method.Value;
    Modality = sProcess.options.sensortype.Value{1};
    ShowTfr  = sProcess.options.showtfr.Value;
    MaxFreq  = sProcess.options.maxfreq.Value{1};
    Baseline = sProcess.options.baseline.Value{1};
    PostStim = sProcess.options.poststim.Value{1};
    FOI      = sProcess.options.foi.Value{1};
    TprFreq  = sProcess.options.tpr.Value{1};
    % Progress bar
    bst_progress('start', 'ft_sourceanalysis', 'Loading input files...', 0, 2*length(sInputs));

    % ===== LOAD: CHANNEL FILE =====
    bst_progress('text', 'Loading input files...');

    % Load channel file
    ChannelMat = in_bst_channel(sInputs(1).ChannelFile);
    % Get selected sensors
    iChannels = channel_find(ChannelMat.Channel, Modality);
    if isempty(iChannels)
        bst_report('Error', sProcess, sInputs, ['Channels "' Modality '" not found in channel file.']);
        return;
    end

    % ===== LOAD: BAD CHANNELS =====
    % Load bad channels from all the input files
    isChannelGood = [];
    for iInput = 1:length(sInputs)
        DataFile = sInputs(1).FileName;
        DataMat = load(file_fullpath(DataFile), 'ChannelFlag');
        if isempty(isChannelGood)
            isChannelGood = (DataMat.ChannelFlag == 1);
        elseif (length(DataMat.ChannelFlag) ~= length(isChannelGood)) || (length(DataMat.ChannelFlag) ~= length(ChannelMat.Channel))
            bst_report('Error', sProcess, sInputs, 'All the input files must have the same number of channels.');
            return;
        else
            isChannelGood = isChannelGood & (DataMat.ChannelFlag == 1);
        end
    end
    % Remove bad channels
    iChannelsData = intersect(iChannels, find(isChannelGood'));
    % Error: All channels tagged as bad
    if isempty(iChannelsData)
        bst_report('Error', sProcess, sInputs, 'All the selected channels are tagged as bad.');
        return;
    elseif any(~isChannelGood)
        bst_report('Info', sProcess, sInputs, ['Found ' num2str(length(find(~isChannelGood))) ' bad channels: ', sprintf('%s ', ChannelMat.Channel(find(~isChannelGood)).Name)]);
    end

    % ===== LOAD: HEADMODEL =====
    % Get the study
    sStudyChan = bst_get('ChannelFile', sInputs(1).ChannelFile);
    % Error if there is no head model available
    if isempty(sStudyChan.iHeadModel)
        bst_report('Error', sProcess, [], ['No head model available in folder: ' bst_fileparts(sStudyChan.FileName)]);
        return;
    end
    % Load head model
    HeadModelFile = sStudyChan.HeadModel(sStudyChan.iHeadModel).FileName;
    HeadModelMat = in_bst_headmodel(HeadModelFile);
    % Convert head model to FieldTrip format
    [ftHeadmodel, ftLeadfield, iChannelsData] = out_fieldtrip_headmodel(HeadModelMat, ChannelMat, iChannelsData, 1);

    % ===== LOAD: DATA =====
    % Template FieldTrip structure for all trials
    ftData = out_fieldtrip_data(sInputs(1).FileName, ChannelMat, iChannelsData, 1);
    ftData.trial = cell(1,length(sInputs));
    ftData.time = cell(1,length(sInputs));
    % Load all the trials
    AllChannelFiles = unique({sInputs.ChannelFile});
    iChanInputs = find(ismember({sInputs.ChannelFile}, AllChannelFiles{1}));
    for iInput = 1:length(sInputs)
        DataFile = sInputs(iChanInputs(iInput)).FileName;
        DataMat = in_bst_data(DataFile);
        ftData.trial{iInput} = DataMat.F(iChannelsData,:);
        ftData.time{iInput} = DataMat.Time;
    end

    % ===== FIELDTRIP: ft_freqanalysis =====
    bst_progress('text', 'Calling FieldTrip function: ft_freqanalysis...');
    % Compute tfr-decomposition
    cfg = [];
    cfg.output     = 'pow';
    cfg.channel    = 'all';
    cfg.method     = 'mtmconvol';
    cfg.taper      = 'hanning';
    cfg.foi        = 1:2:MaxFreq;
    cfg.keeptrials = 'yes';
    cfg.t_ftimwin  = 3 ./ cfg.foi;
    cfg.tapsmofrq  = 0.8 * cfg.foi;
    cfg.toi        = Baseline(1):0.05:PostStim(2);
    tfr            = ft_freqanalysis(cfg, ftData);
    % Plot TFR
    cfg = [];
    cfg.fmax = MaxFreq;
    cfg.toi = [Baseline(1), PostStim(2)];
    cfg.baselinetime = Baseline;
    cfg.plotflag = ShowTfr;
    [time_of_interest,freq_of_interest] = do_tfr_plot(cfg, tfr);
    disp(['Global max: time:',num2str(time_of_interest),'sec']);
    disp(['Global max: freq:',num2str(freq_of_interest),'Hz']);

    % ===== FIELDTRIP: EPOCHING =====
    % Baseline
    cfg = [];
    cfg.toilim = Baseline;
    ep_data.bsl = ft_redefinetrial(cfg, ftData);
    % Post-stim
    cfg.toilim = PostStim;
    ep_data.pst = ft_redefinetrial(cfg, ftData);
    % Baseline + Post-stim
    cfg = [];
    ep_data.app = ft_appenddata(cfg, ep_data.bsl, ep_data.pst);

    % ===== TEMPORARY FOLDER =====
    % Output folder (for figures and FieldTrip structures)
    TmpDir = bst_get('BrainstormTmpDir', 0, 'dics');

    % ===== FIELDTRIP: SPECTRAL ANALYSIS =====
    cfg_main = [];
    cfg_main.fmax = MaxFreq;
    switch sProcess.options.sensortype.Value{1}
        case {'EEG', 'SEEG', 'ECOG'}
            cfg_main.sens = ftData.elec;
        case {'MEG', 'MEG GRAD', 'MEG MAG'}
            cfg_main.sens = ftData.grad;
    end
    cfg_main.outputdir = TmpDir;
    cfg_main.freq_of_interest  = freq_of_interest; % Hz

    cfg = [];
    cfg.foilim    = [2 cfg_main.fmax];
    cfg.tapsmofrq = 1;
    cfg.taper     = 'hanning';
    f_data.bsl = do_fft(cfg, ep_data.bsl); f_data.bsl.elec = cfg_main.sens;
    f_data.pst = do_fft(cfg, ep_data.pst); f_data.pst.elec = cfg_main.sens;

    % ===== FIELDTRIP: PSD SENSOR SPACE =====
    outputdir_dics = cfg_main.outputdir;
    if exist(outputdir_dics, 'file') == 0, mkdir(outputdir_dics), end

    f_sugg = round(cfg_main.freq_of_interest);
    disp(['Suggested by TFR: ', num2str(f_sugg),'(+-3Hz)']);
    disp(['Select foi,eg ,', num2str(f_sugg),':']);

    cfg = [];
    cfg.foilim = [FOI, FOI];
    if (FOI >= 4)
        cfg.taper = 'dpss';
        cfg.tapsmofrq = TprFreq;
    else
        cfg.taper = 'hanning';
        cfg.tapsmofrq = 1;
    end

    f_data.app = do_fft(cfg, ep_data.app); f_data.app.elec = cfg_main.sens;
    f_data.bsl = do_fft(cfg, ep_data.bsl); f_data.bsl.elec = cfg_main.sens;
    f_data.pst = do_fft(cfg, ep_data.pst); f_data.pst.elec = cfg_main.sens;

    % ===== SOURCE ANALYSIS =====
    switch Method
        case 'subtraction'
            cfg = [];
            cfg.method = 'dics';
            cfg.dics.lambda = '100%';
            cfg.sourcemodel  = ftLeadfield;
            cfg.frequency    = f_data.app.freq;
            cfg.headmodel = ftHeadmodel;
            cfg.dics.keepfilter = 'yes';
            cfg.dics.fixedori    = 'yes'; % project on axis of most variance using SVD
            sourceavg = ft_sourceanalysis(cfg, f_data.app);

            cfg = [];
            cfg.method = 'dics';
            cfg.dics.lambda = '0%';
            cfg.sourcemodel        = ftLeadfield;
            cfg.sourcemodel.filter = sourceavg.avg.filter;
            cfg.dics.fixedori    = 'yes'; % project on axis of most variance using SVD
            cfg.headmodel = ftHeadmodel;
            s_data.bsl      = ft_sourceanalysis(cfg, f_data.bsl);
            s_data.pst      = ft_sourceanalysis(cfg, f_data.pst);

            switch sProcess.options.erds.Value
                case 'erd'
                    cfg = [];
                    cfg.parameter = 'pow';
                    cfg.operation = 'log10(x1/x2)'; % sourceA divided by sourceB
                    source_diff_dics = ft_math(cfg, s_data.pst, s_data.bsl);
                    source_diff_dics.pow(isnan(source_diff_dics.pow))=0;
                    source_diff_dics.pow(source_diff_dics.pow>0)=0;
                case 'ers'
                    cfg = [];
                    cfg.parameter = 'pow';
                    cfg.operation = 'log10(x1/x2)'; % sourceA divided by sourceB
                    source_diff_dics = ft_math(cfg, s_data.pst, s_data.bsl);
                    source_diff_dics.pow(isnan(source_diff_dics.pow))=0;
                    source_diff_dics.pow(source_diff_dics.pow<0)=0;
                case 'both'
                    cfg = [];
                    cfg.parameter = 'pow';
                    cfg.operation = 'log10(x1/x2)'; % sourceA divided by sourceB
                    source_diff_dics = ft_math(cfg,s_data.pst,s_data.bsl);
                    source_diff_dics.pow(isnan(source_diff_dics.pow))=0;
            end

        case 'permutation'
            cfg = [];
            cfg.method = 'dics';
            cfg.dics.lambda = '100%';
            cfg.frequency    = f_data.app.freq;
            cfg.headmodel = ftHeadmodel;
            cfg.sourcemodel  = ftLeadfield;
            cfg.dics.keepfilter = 'yes';
            cfg.dics.fixedori    = 'yes'; % project on axis of most variance using SVD
            sourceavg = ft_sourceanalysis(cfg, f_data.app);

            cfg = [];
            cfg.method = 'dics';
            cfg.sourcemodel        = ftLeadfield;
            cfg.sourcemodel.filter = sourceavg.avg.filter;
            cfg.dics.fixedori    = 'yes'; % project on axis of most variance using SVD
            cfg.rawtrial = 'yes';
            cfg.headmodel = ftHeadmodel;
            s_data.bsl      = ft_sourceanalysis(cfg, f_data.bsl);
            s_data.pst      = ft_sourceanalysis(cfg, f_data.pst);

            stat = do_source_stat_montcarlo(s_data);

            tmp = stat.stat;
            tmp2 = zeros(size(stat.pos,1),1);
            tmp2(stat.inside) = tmp;

            stats1  = stat;
            stats1.stat =  tmp2;
            stats1.mask = stat.inside;
            stats2 = stats1;
            stats2.stat(stats2.stat>0)=0;
            stats2.stat(isnan(stats2.stat))=0;
    end

    % ===== SAVE RESULTS =====
    % === CREATE OUTPUT STRUCTURE ===
    bst_progress('text', 'Saving source file...');
    bst_progress('inc', 1);
    % Output study
    if (length(sInputs) == 1)
        iStudyOut = sInputs(1).iStudy;
        RefDataFile = sInputs(iChanInputs(iInput)).FileName;
    else
        [tmp, iStudyOut] = bst_process('GetOutputStudy', sProcess, sInputs);
        RefDataFile = [];
    end
    % Create structure
    ResultsMat = db_template('resultsmat');
    ResultsMat.ImagingKernel = [];
    switch Method
        case 'subtraction'
            switch sProcess.options.effect.Value
                case 'abs'
                    source_diff_dics.pow = abs((source_diff_dics.pow));
                case 'raw'
                    source_diff_dics.pow = source_diff_dics.pow;
            end
            ResultsMat.ImageGridAmp  = source_diff_dics.pow;
            ResultsMat.cfg           = source_diff_dics.cfg;

        case 'permutation'
            switch sProcess.options.effect.Value
                case 'abs'
                    stats2.stat = abs((stats2.stat));
                case 'raw'
                    stats2.stat = stats2.stat;
            end
            ResultsMat.ImageGridAmp  = stats2.stat;
            ResultsMat.cfg           = stat.cfg;
    end
    ResultsMat.nComponents   = 1;
    ResultsMat.Function      = Method;
    ResultsMat.Time          = 1;
    ResultsMat.DataFile      = RefDataFile;
    ResultsMat.HeadModelFile = HeadModelFile;
    ResultsMat.HeadModelType = HeadModelMat.HeadModelType;
    ResultsMat.ChannelFlag   = DataMat.ChannelFlag;
    ResultsMat.GoodChannel   = iChannelsData;
    ResultsMat.SurfaceFile   = HeadModelMat.SurfaceFile;
    ResultsMat.nAvg          = DataMat.nAvg;
    ResultsMat.Leff          = DataMat.Leff;
    ResultsMat.Comment       = ['DICS: ' Method, ' ',num2str(FOI),'Hz ', sprintf('%1.3fs-%1.3fs', PostStim)];
    switch lower(ResultsMat.HeadModelType)
        case 'volume'
            ResultsMat.GridLoc    = HeadModelMat.GridLoc;
        case 'surface'
            ResultsMat.GridLoc    = [];
        case 'mixed'
            ResultsMat.GridLoc    = HeadModelMat.GridLoc;
            ResultsMat.GridOrient = HeadModelMat.GridOrient;
    end
    ResultsMat = bst_history('add', ResultsMat, 'compute', ['ft_sourceanalysis: ' Method ' ' Modality ' ']);

    % === SAVE OUTPUT FILE ===
    % Output filename
    OutputDir = bst_fileparts(file_fullpath(DataFile));
    ResultFile = bst_process('GetNewFilename', OutputDir, ['results_', Method, '_', Modality]);
    % Save new file structure
    bst_save(ResultFile, ResultsMat, 'v6');

    % ===== REGISTER NEW FILE =====
    % Create new results structure
    newResult = db_template('results');
    newResult.Comment       = ResultsMat.Comment;
    newResult.FileName      = file_short(ResultFile);
    newResult.DataFile      = ResultsMat.DataFile;
    newResult.isLink        = 0;
    newResult.HeadModelType = ResultsMat.HeadModelType;
    % Get output study
    sStudyOut = bst_get('Study', iStudyOut);
    % Add new entry to the database
    iResult = length(sStudyOut.Result) + 1;
    sStudyOut.Result(iResult) = newResult;
    % Update Brainstorm database
    bst_set('Study', iStudyOut, sStudyOut);
    % Store output filename
    OutputFiles{end+1} = newResult.FileName;
    % Expand data node
    panel_protocols('SelectNode', [], newResult.FileName);

    % Delete the temporary files
    file_delete(TmpDir, 1, 1);
    % Save database
    db_save();
    % Hide progress bar
    bst_progress('stop');
end

%% ===== TIME-FREQUENCY =====
function [time_of_interest,freq_of_interest] = do_tfr_plot(cfg_main, tfr)
    % First compute the average over trials:
    cfg = [];
    freq_avg = ft_freqdescriptives(cfg, tfr);

    % And baseline-correct the average:
    cfg = [];
    cfg.baseline = cfg_main.baselinetime;
    cfg.baselinetype = 'db'; % Use decibel contrast here
    freq_avg_bsl = ft_freqbaseline(cfg, freq_avg);

    freq_avg_bsl.powspctrm(isnan(freq_avg_bsl.powspctrm))=0;
    meanpow = squeeze(mean(freq_avg_bsl.powspctrm, 1));

    tim_interp = linspace(cfg_main.toi(1), cfg_main.toi(2), 512);
    freq_interp = linspace(1, cfg_main.fmax, 512);

    % We need to make a full time/frequency grid of both the original and
    % interpolated coordinates. Matlab's meshgrid() does this for us:
    [tim_grid_orig, freq_grid_orig] = meshgrid(tfr.time, tfr.freq);
    [tim_grid_interp, freq_grid_interp] = meshgrid(tim_interp, freq_interp);

    % And interpolate:
    pow_interp = interp2(tim_grid_orig, freq_grid_orig, meanpow, tim_grid_interp, freq_grid_interp, 'spline');

    % while n==1
    pow_interp1  = pow_interp(50:end,50:end);
    tim_interp1  = tim_interp(50:end);
    freq_interp1 = freq_interp(50:end);

    [~,idx] = min(pow_interp1(:));
    [row,col] = ind2sub(size(pow_interp1),idx);

    time_of_interest = tim_interp1(col);
    freq_of_interest = freq_interp1(row);

    timind = nearest(tim_interp, time_of_interest);
    freqind = nearest(freq_interp, freq_of_interest);
    pow_at_toi = pow_interp(:,timind);
    pow_at_foi = pow_interp(freqind,:);

    % Plot figure
    if cfg_main.plotflag
        figure();
        ax_main  = axes('Position', [0.1 0.2 0.55 0.55]);
        ax_right = axes('Position', [0.7 0.2 0.1 0.55]);
        ax_top   = axes('Position', [0.1 0.8 0.55 0.1]);

        axes(ax_main);
        imagesc(tim_interp, freq_interp, pow_interp);
        % note we're storing a handle to the image im_main, needed later on
        xlim([cfg_main.toi(1), cfg_main.toi(2)]);
        axis xy;
        xlabel('Time (s)');
        ylabel('Frequency (Hz)');
        clim = max(abs(meanpow(:)));
        caxis([-clim clim]);
        % colormap(brewermap(256, '*RdYlBu'));
        hold on;
        plot(zeros(size(freq_interp)), freq_interp, 'k:');

        axes(ax_top);
        area(tim_interp, pow_at_foi, ...
            'EdgeColor', 'none', 'FaceColor', [0.5 0.5 0.5]);
        xlim([cfg_main.toi(1), cfg_main.toi(2)]);
        ylim([-clim clim]);
        box off;
        ax_top.XTickLabel = [];
        ylabel('Power (dB)');
        hold on;
        plot([0 0], [-clim clim], 'k:');

        axes(ax_right);
        area(freq_interp, pow_at_toi,...
            'EdgeColor', 'none', 'FaceColor', [0.5 0.5 0.5]);
        view([270 90]); % this rotates the plot
        ax_right.YDir = 'reverse';
        ylim([-clim clim]);
        box off;
        ax_right.XTickLabel = [];
        ylabel('Power (dB)');

        h = colorbar(ax_main, 'manual', 'Position', [0.85 0.2 0.05 0.55]);
        ylabel(h, 'Power vs baseline (dB)');

        % Main plot:
        axes(ax_main);
        plot(ones(size(freq_interp))*time_of_interest, freq_interp,...
            'Color', [0 0 0 0.1], 'LineWidth', 3);
        plot(tim_interp, ones(size(tim_interp))*freq_of_interest,...
            'Color', [0 0 0 0.1], 'LineWidth', 3);

        % Marginals:
        axes(ax_top);
        plot([time_of_interest time_of_interest], [0 clim],...
            'Color', [0 0 0 0.1], 'LineWidth', 3);
        axes(ax_right);
        hold on;
        plot([freq_of_interest freq_of_interest], [0 clim],...
            'Color', [0 0 0 0.1], 'LineWidth', 3);
    end
end

function [freq,ff, psd,tapsmofrq] = do_fft(cfg_mian, data)
    cfg              = [];
    cfg.method       = 'mtmfft';
    cfg.output       = 'fourier';
    cfg.keeptrials   = 'yes';
    cfg.foilim       = cfg_mian.foilim;
    cfg.tapsmofrq    = cfg_mian.tapsmofrq;
    cfg.taper        = cfg_mian.taper;
    cfg.pad          = 4;
    freq             = ft_freqanalysis(cfg, data);
    psd = squeeze(mean(mean(abs(freq.fourierspctrm),2),1));
    ff = linspace(1, cfg.foilim(2), length(psd));

    tapsmofrq = cfg.tapsmofrq;
end

function stat = do_source_stat_montcarlo(s_data)
    cfg = [];
    cfg.parameter        = 'pow';
    cfg.method           = 'montecarlo';
    cfg.statistic        = 'depsamplesT';
    cfg.correctm         = 'fdr';
    cfg.clusteralpha     = 0.001;
    cfg.tail             = 0;
    cfg.clustertail      = 0;
    cfg.alpha            = 0.05;
    cfg.numrandomization = 5000;

    ntrials                       = numel(s_data.bsl.trial);
    design                        = zeros(2,2*ntrials);
    design(1,1:ntrials)           = 1;
    design(1,ntrials+1:2*ntrials) = 2;
    design(2,1:ntrials)           = 1:ntrials;
    design(2,ntrials+1:2*ntrials) = 1:ntrials;

    cfg.design   = design;
    cfg.ivar     = 1;
    cfg.uvar     = 2;
    stat         = ft_sourcestatistics(cfg,s_data.pst,s_data.bsl);
end


%
% FILE NAME:    curateRxnAssoc.m
%
% PURPOSE: Curate rxnAssoc array structure by adding the reaction bounds
%          information extracted from HMR2 and Recon3D
%

% Load input
load('rxnAssoc.mat');       % rxnAssoc
load('humanGEM.mat');       % humanGEM v0.2.0
load('Recon3DRaven.mat');   % Recon3D

% Add fields for reaction bounds information
[~, indHMR]=ismember(rxnAssoc.rxnHMRID,ihuman.rxns);
rxnAssoc.lbHMR=ihuman.lb(indHMR);
rxnAssoc.ubHMR=ihuman.ub(indHMR);

[~, indRecon3D]=ismember(rxnAssoc.rxnRecon3DID,Recon3DRaven.rxns);
rxnAssoc.lbRecon3D=Recon3DRaven.lb(indRecon3D);
rxnAssoc.ubRecon3D=Recon3DRaven.ub(indRecon3D);

% Save to modelIntegration subfolder
save('rxnAssoc.mat','rxnAssoc');


function ind = gpBlockIndices(model, blockNo)

% GPBLOCKINDICES Return indices of given block.
% FORMAT
% DESC returns the indices of a given block for the PITC
% approximation.
% ARG model : the model for which the indices are being computed.
% ARG blockNum : the block number for which the indices are
% required.
% RETURN indices : the data indices associated with given block.
%
% SEEALSO : gpComputeAlpha, gpCovGrads, gpLogLikeGradients,
% gpLogLikelihood, gpUpdateAD
%
% COPYRIGHT : Neil D. Lawrence, 2006

% GP

if ~strcmp(model.approx, 'pitc')
  error('Block indices only relevant for pitc approximation.');
else
  if blockNo == 1
    startVal = 1;
  else
    startVal = model.blockEnd(blockNo-1)+1;
  end
  endVal = model.blockEnd(blockNo);
  ind = startVal:endVal;
end


function [verbose,verboseI,debug,doPlot,maxFunEvals,maxIter,optTol,progTol,method,...
    corrections,c1,c2,LS_init,cgSolve,qnUpdate,cgUpdate,initialHessType,...
    HessianModify,Fref,useComplex,numDiff,LS_saveHessianComp,...
    Damped,HvFunc,bbType,cycle,...
    HessianIter,outputFcn,useMex,useNegCurv,precFunc,...
    LS_type,LS_interp,LS_multi,DerivativeCheck] = ...
    minFunc_processInputOptions(o)

% Constants
SD = 0;
CSD = 1;
BB = 2;
CG = 3;
PCG = 4;
LBFGS = 5;
QNEWTON = 6;
NEWTON0 = 7;
NEWTON = 8;
TENSOR = 9;

verbose = 1;
verboseI= 1;
debug = 0;
doPlot = 0;
method = LBFGS;
cgSolve = 0;

o = toUpper(o);

if isfield(o,'DISPLAY')
    switch(upper(o.DISPLAY))
        case 0
            verbose = 0;
            verboseI = 0;
        case 'FINAL'
            verboseI = 0;
        case 'OFF'
            verbose = 0;
            verboseI = 0;
        case 'NONE'
            verbose = 0;
            verboseI = 0;
        case 'FULL'
            debug = 1;
        case 'EXCESSIVE'
            debug = 1;
            doPlot = 1;
    end
end

DerivativeCheck = 0;
if isfield(o,'DERIVATIVECHECK')
    switch(upper(o.DERIVATIVECHECK))
        case 1
            DerivativeCheck = 1;
        case 'ON'
            DerivativeCheck = 1;
    end
end

LS_init = 0;
LS_type = 1;
LS_interp = 2;
LS_multi = 0;
Fref = 1;
Damped = 0;
HessianIter = 1;
c2 = 0.9;
if isfield(o,'METHOD')
    m = upper(o.METHOD);
    switch(m)
        case 'TENSOR'
            method = TENSOR;
        case 'NEWTON'
            method = NEWTON;
        case 'MNEWTON'
            method = NEWTON;
            HessianIter = 5;
        case 'PNEWTON0'
            method = NEWTON0;
            cgSolve = 1;
        case 'NEWTON0'
            method = NEWTON0;
        case 'QNEWTON'
            method = QNEWTON;
            Damped = 1;
        case 'LBFGS'
            method = LBFGS;
        case 'BB'
            method = BB;
            LS_type = 0;
            Fref = 20;
        case 'PCG'
            method = PCG;
            c2 = 0.2;
            LS_init = 2;
        case 'SCG'
            method = CG;
            c2 = 0.2;
            LS_init = 4;
        case 'CG'
            method = CG;
            c2 = 0.2;
            LS_init = 2;
        case 'CSD'
            method = CSD;
            c2 = 0.2;
            Fref = 10;
            LS_init = 2;
        case 'SD'
            method = SD;
            LS_init = 2;
    end
end

maxFunEvals = getOpt(o,'MAXFUNEVALS',1000);
maxIter = getOpt(o,'MAXITER',500);
optTol = getOpt(o,'OPTTOL',1e-5);
progTol = getOpt(o,'PROGTOL',1e-9);
corrections = getOpt(o,'CORRECTIONS',100);
corrections = getOpt(o,'CORR',corrections);
c1 = getOpt(o,'C1',1e-4);
c2 = getOpt(o,'C2',c2);
LS_init = getOpt(o,'LS_INIT',LS_init);
cgSolve = getOpt(o,'CGSOLVE',cgSolve);
qnUpdate = getOpt(o,'QNUPDATE',3);
cgUpdate = getOpt(o,'CGUPDATE',2);
initialHessType = getOpt(o,'INITIALHESSTYPE',1);
HessianModify = getOpt(o,'HESSIANMODIFY',0);
Fref = getOpt(o,'FREF',Fref);
useComplex = getOpt(o,'USECOMPLEX',0);
numDiff = getOpt(o,'NUMDIFF',0);
LS_saveHessianComp = getOpt(o,'LS_SAVEHESSIANCOMP',1);
Damped = getOpt(o,'DAMPED',Damped);
HvFunc = getOpt(o,'HVFUNC',[]);
bbType = getOpt(o,'BBTYPE',0);
cycle = getOpt(o,'CYCLE',3);
HessianIter = getOpt(o,'HESSIANITER',HessianIter);
outputFcn = getOpt(o,'OUTPUTFCN',[]);
useMex = getOpt(o,'USEMEX',1);
useNegCurv = getOpt(o,'USENEGCURV',1);
precFunc = getOpt(o,'PRECFUNC',[]);
LS_type = getOpt(o,'LS_type',LS_type);
LS_interp = getOpt(o,'LS_interp',LS_interp);
LS_multi = getOpt(o,'LS_multi',LS_multi);
end

function [v] = getOpt(options,opt,default)
if isfield(options,opt)
    if ~isempty(getfield(options,opt))
        v = getfield(options,opt);
    else
        v = default;
    end
else
    v = default;
end
end

function [o] = toUpper(o)
if ~isempty(o)
    fn = fieldnames(o);
    for i = 1:length(fn)
        o = setfield(o,upper(fn{i}),getfield(o,fn{i}));
    end
end
end


function test_suite = test_createRay
%testCreateRay  One-line description here, please.
%   output = testCreateRay(input)
%
%   Example
%   testCreateRay
%
%   See also
%
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2009-04-22,    using Matlab 7.7.0.471 (R2008b)
% Copyright 2009 INRA - Cepia Software Platform.
% Licensed under the terms of the LGPL, see the file "license.txt"

test_suite = functiontests(localfunctions);

function testCreateRay2Points(testCase) %#ok<*DEFNU>

p1 = [1 1];
p2 = [2 3];
ray = createRay(p1, p2);

testCase.assertEqual(p1, ray(1,1:2), 'AbsTol', .01);
testCase.assertEqual(p2-p1, ray(1,3:4), 'AbsTol', .01);

function testCreateRay2Arrays(testCase)

p1 = [1 1;1 1];
p2 = [2 3;2 4];
ray = createRay(p1, p2);

testCase.assertEqual(2, size(ray, 1), 'AbsTol', .01);
testCase.assertEqual(p1, ray(:,1:2), 'AbsTol', .01);
testCase.assertEqual(p2-p1, ray(:,3:4), 'AbsTol', .01);


function requiredMetsStatus = checkModelFunction(model, requiredMets)
% Checks that the model is able to produce the list of metabolites defined in
% `requiredMets`
%
% USAGE:
%    requiredMetsStatus = checkModelFunction(model, requiredMets)
%
% INPUTS:
%    model:                 model structure
%    requiredMets:          cell array with the list of metabolites that
%                           the model need to be able to produce
%
% OUTPUT:
%    requiredMetsStatus:    0 = test fail,
%                           1 = test succeed
%
% Authors: - This script is an adapted version of the implementation from
%            https://github.com/jaeddy/mcadre.
%          - Modified and commented by A. Richelle,May 2017

    if isempty(requiredMets) %If requiredMets is empty, skip the step and always say the test is passed
        requiredMetsStatus = true;
        warning('No metabolites defined to check the model function')
    else
        % Add demand reactions for required metabolites
        % function evalc used to remove display of reaction added
        [~, model, requiredRxns] = evalc(['addDemandReaction(model,requiredMets);']);

        % Check that the demand reaction can carry fluxes (default value of
        % flux active - 1e-8)
        inactiveRequired = checkRxnFlux(model, requiredRxns);
        requiredMetsStatus = ~numel(inactiveRequired);
    end

end


% METAPLOTTOPO - plot concatenated multichannel data epochs in a topographic or
%               rectangular array. Uses a channel location file with the same
%               format as TOPOPLOT, or else plots data on a rectangular grid.
%
% Usage:
%    >> axes = metaplottopo(data, 'key1', 'val1', 'key2', 'val2')
%
% Inputs:
%   data       = data consisting of consecutive epochs of (chans,frames)
%                or (chans,frames,n)
%
% Optional inputs:
%  'chanlocs'  = [struct] channel structure or file plot ERPs at channel
%                locations. See help READLOCS for data channel format.
%  'geom'      = [rows cols] plot ERP in grid (overwrite previous option).
%                Grid size for rectangular matrix. Example: [6 4].
%  'title'     = [string] general plot title {def|'' -> none}
%  'chans'     = vector of channel numbers to plot {def|0 -> all}
%  'axsize'    = [x y] axis size {default [.07 .07]}
%  'plotfunc'  = [string] plot function name. If none is entered, axes
%                are created and returned.
%  'plotargs'  = [cell] plotting function arguments.
%  'datapos'   = [integer] position of data array in the function call.
%                Default is 1.
%  'squeeze'   = ['on'|'off'] squeeze data array after selecting channel.
%                Default is 'off'.
%
% Output:
%  Axes        = [real] array of axes handles of the same length as the
%                number of plotted channels.
%  Channames   = [cell] cell array of channel name for each plot
%
% Author: Arnaud Delorme, Scott Makeig, CERCO, CNRS, 2007-
%
% See also: PLOTTOPO

% Copyright (C) 2007, Arnaud Delorme, CERCO, arno@sccn.ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [Axes, outchannames ]= metaplottopo(data, varargin);

%
%%%%%%%%%%%%%%%%%%%%% Graphics Settings - can be customized %%%%%%%%%%%%%%%%%%
%
LINEWIDTH     = 0.7;     % data line widths (can be non-integer)
FONTSIZE      = 10;      % font size to use for labels
CHANFONTSIZE  = 7;       % font size to use for channel names
TICKFONTSIZE  = 8;       % font size to use for axis labels
TITLEFONTSIZE = 12;      % font size to use for the plot title
PLOT_WIDTH    = 0.95;     % 0.75, width and height of plot array on figure
PLOT_HEIGHT   = 0.88;    % 0.88
gcapos = get(gca,'Position'); axis off;
PLOT_WIDTH    = gcapos(3)*PLOT_WIDTH; % width and height of gca plot array on gca
PLOT_HEIGHT   = gcapos(4)*PLOT_HEIGHT;
MAXCHANS      = 256;     % can be increased
curfig = gcf;            % learn the current graphic figure number
%
%%%%%%%%%%%%%%%%%%%% Default settings - use commandline to override %%%%%%%%%%%
%
DEFAULT_AXWIDTH  = 0.05; %
DEFAULT_AXHEIGHT = 0.08; %
DEFAULT_SIGN = -1;                        % Default - plot positive-up
ISRECT = 0;                               % default
ISSPEC = 0;                               % Default - not spectral data

outchannames = {};
if nargin < 1
    help metaplottopo
    return
end

if iscell(data), nchans = size(data{1},1);
else             nchans = size(data,1);
end
g = finputcheck(varargin, { 'chanlocs'  ''    []          '';
    'chans'     'integer'               [1 size(data,1)]  [1:nchans];
    'geom'      'integer'               [1 Inf]     [];
    'title'     'string'                []          '';
    'plotfunc'  'string'                []          '';
    'plotargs'  'cell'                  []          {};
    'datapos'   'integer'               []          1;
    'calbar'    'real'                  []          [];
    'axcopycom' 'string'                []          '';
    'squeeze'   'string'                {'on' 'off'} 'off';
    'axcopycom' 'string'                {}          'off';
    'axsize'    'float'                 [0 1]       [nan nan]}, 'metaplottopo' );
if ischar(g), error(g); end
if length(g.chans) == 1 && g.chans(1) ~= 0, error('can not plot a single ERP'); end

[chans,framestotal]=size(data);           % data size

%
%%%%%%%%%%%%%%% Substitute defaults for missing parameters %%%%%%%%%%%%%%%%
%

if (isempty(g.chanlocs) || ~isfield(g.chanlocs, 'theta')) && isempty(g.geom)
    n = ceil(sqrt(length(g.chans)));
    g.geom = [n n];
end

%
%%%%%%%%%%%%%%%%%%%%%%%%%%% Test parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
axwidth  = g.axsize(1);
axheight = g.axsize(2);
if ~isempty(g.geom)
    if isnan(axheight) % if not specified
        axheight = gcapos(4)/(g.geom(1)+1);
        axwidth  = gcapos(3)/(g.geom(2)+1);
    end
else
    axheight = DEFAULT_AXHEIGHT*(gcapos(4)*1.25);
    axwidth =  DEFAULT_AXWIDTH*(gcapos(3)*1.3);
end
fprintf('Plotting data using axis size [%g,%g]\n',axwidth,axheight);

%
%%%%%%%%%%%%% Extend the size of the plotting area in the window %%%%%%%%%%%%
%
curfig = gcf;
h=figure(curfig);
set(h,'PaperUnits','normalized'); % use percentages to avoid US/A4 difference
set(h,'PaperPosition',[0.0235308 0.0272775 0.894169 0.909249]); % equivalent
orient portrait
axis('normal');

axcolor= get(0,'DefaultAxesXcolor'); % find what the default x-axis color is
vertcolor = 'k';
horicolor = vertcolor;

%
%%%%%%%%%%%%%%%%%%%%%%%%% Plot and label specified channels %%%%%%%%%%%%%%%%%%
%

if ~isempty(data)
    if iscell(data)
        data{1} = data{1}(g.chans,:,:,:);
        data{2} = data{2}(g.chans,:,:,:);
    else
        data = data(g.chans,:,:,:);
    end
end

%
%%%%%%%%%%%%%%%%%%%%%%%%%% Print plot info %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
figure(curfig); h=gca;title(g.title, 'interpreter', 'none'); % title plot
hold on
axis('off')

%
%%%%%%%%%%%%%%%%%%%%%%%%%%% Read chan_locs %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
if isempty(g.chanlocs) || ~isfield(g.chanlocs, 'theta') % plot in a rectangular grid
    ISRECT = 1;
    ht = g.geom(1);
    wd = g.geom(2);
    if length(g.chans) > ht*wd
        fprintf('metaplottopo(): (%d) channels to be plotted > grid size [%d %d]\n',...
            length(g.chans),ht,wd);
        return
    end
    halfht = (ht-1)/2;
    halfwd = (wd-1)/2;
    xvals = zeros(ht*wd,1);
    yvals = zeros(ht*wd,1);
    dist  = zeros(ht*wd,1);
    for j=1:ht
        for i=1:wd
            xvals(i+(j-1)*wd) = -halfwd+(i-1);
            yvals(i+(j-1)*wd) =  halfht-(j-1);
            % dist(i+(j-1)*wd) =  sqrt(xvals(j+(i-1)*ht).^2+yvals(j+(i-1)*ht).^2);
        end
    end
    % maxdist = max(dist);
    maxxvals = max(xvals);
    maxyvals = max(yvals);
    for j=1:ht
        for i=1:wd
            xvals(i+(j-1)*wd) = 0.499*xvals(i+(j-1)*wd)/maxxvals;
            yvals(i+(j-1)*wd) = 0.499*yvals(i+(j-1)*wd)/maxyvals;
        end
    end
    if ~exist('channames')
        channames = repmat(' ',ht*wd,4);
        for i=1:ht*wd
            channum = num2str(i);
            channames(i,1:length(channum)) = channum;
        end
    end

else % read chan_locs file
    % read the channel location file
    % ------------------------------
    if isstruct(g.chanlocs)
        nonemptychans = cellfun('isempty', { g.chanlocs.theta });
        nonemptychans = find(~nonemptychans);
        [tmp channames Th Rd] = readlocs(g.chanlocs(nonemptychans));
        channames = strvcat({ g.chanlocs.labels });
    else
        [tmp channames Th Rd] = readlocs(g.chanlocs);
        channames = strvcat(channames);
        nonemptychans = [1:length(channames)];
    end
    Th = pi/180*Th;                 % convert degrees to radians
    Rd = Rd;

    if length(g.chans) > length(g.chanlocs),
        error('metaplottopo(): data channels must be <= ''chanlocs'' channels')
    end

    [yvalstmp,xvalstmp] = pol2cart(Th,Rd); % translate from polar to cart. coordinates
    xvals(nonemptychans) = xvalstmp;
    yvals(nonemptychans) = yvalstmp;

    % find position for other channels
    % --------------------------------
    totalchans = length(g.chanlocs);
    emptychans = setdiff_bc(1:totalchans, nonemptychans);
    totalchans = floor(sqrt(totalchans))+1;
    for index = 1:length(emptychans)
        xvals(emptychans(index)) = 0.7+0.2*floor((index-1)/totalchans);
        yvals(emptychans(index)) = -0.4+mod(index-1,totalchans)/totalchans;
    end
    channames = channames(g.chans,:);
    xvals     = xvals(g.chans);
    yvals     = yvals(g.chans);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% xvals = 0.5+PLOT_WIDTH*xvals;   % controls width of  plot array on page!
% yvals = 0.5+PLOT_HEIGHT*yvals;  % controls height of plot array on page!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if length(xvals) > 1
    xvals = (xvals-mean([max(xvals) min(xvals)]))/(max(xvals)-min(xvals)); % recenter
    xvals = gcapos(1)+gcapos(3)/2+PLOT_WIDTH*xvals;   % controls width of plot
    % array on current axes
end
yvals = gcapos(2)+gcapos(4)/2+PLOT_HEIGHT*yvals;  % controls height of plot
% array on current axes
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Plot traces %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%

Axes = [];
fprintf('Plotting all channel...');
for c=1:length(g.chans), %%%%%%%% for each data channel %%%%%%%%%%%%%%%%%%%%%%%%%%

    xcenter = xvals(c); if isnan(xcenter), xcenter = 0.5; end;
    ycenter = yvals(c); if isnan(ycenter), ycenter = 0.5; end
    Axes = [Axes axes('Units','Normal','Position', ...
        [xcenter-axwidth/2 ycenter-axheight/2 axwidth axheight])];
    hold on;
    axis('off');
    %axes('Units','Normal','Position', [xcenter-axwidth/2 ycenter-axheight/2 axwidth axheight])
    %axis('off');

    %
    %%%%%%%%%%%%%%%%%%%%%%% Plot data traces %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %
    if ~isempty( g.plotfunc )
        %figure(curfig);
        eval( [ 'func = @' g.plotfunc ';' ] );
        if iscell(data), tmp = { g.plotargs{1:g.datapos(1)-1} data{1}(c,:,:,:,:) g.plotargs{g.datapos(1):g.datapos(2)-1} data{2}(c,:) g.plotargs{g.datapos(2):end}};
        else             tmp = { g.plotargs{1:g.datapos-1}    data(c,:,:,:,:)    g.plotargs{g.datapos:end} };
        end
        if strcmpi(g.squeeze, 'on') tmp{g.datapos} = squeeze(tmp{g.datapos}); end
        tmp = { tmp{:} 'title' channames(c,:) 'plotmode' 'topo'};
        feval(func, tmp{:});
    end
    outchannames{c} = deblank(channames(c,:));
end % c, chans / subplot

fprintf('\n');

%
%%%%%%%%%%%%%%%%%%%%% Make time and amp cal bar %%%%%%%%%%%%%%%%%%%%%%%%%
%
if ~isempty(g.calbar)
    ax = axes('Units','Normal','Position', [0.85 0.1 axwidth axheight]); % FIX!!!!
    axes(ax)
    axis('off');
    if   g.calbar(3) < g.calbar(4), g.ydir = 1;
    else g.calbar(5) = g.calbar(3); g.calbar(3) = []; g.ydir = -1;
    end
    [xmin xmax ymin ymax] = deal(g.calbar(1), g.calbar(2),g.calbar(3), g.calbar(4));
    figure(curfig);p=plot([0 0],[ymin ymax],'color','k'); % draw vert axis at zero
    if g.ydir == -1
        set(gca, 'ydir', 'reverse');
    end
    hold on
    figure(curfig);p=plot([xmin xmax],[0 0],'color','k'); % draw horizontal axis
    xlim([xmin xmax]);
    ylim([ymin ymax]);
    axis off;

    % draw text limits
    % ----------------
    xdiff = xmax - xmin;
    ydiff = ymax - ymin;
    signx = xmin-0.15*xdiff;
    figure(curfig);axis('off');h=text(double(signx),double(ymin),num2str(ymin,3)); % text ymin
    set(h,'FontSize',TICKFONTSIZE);               % choose font size
    set(h,'HorizontalAlignment','right','Clipping','off');

    figure(curfig);axis('off');h=text(double(signx), double(ymax),['+' num2str(ymax,3)]);  % text +ymax
    set(h,'FontSize',TICKFONTSIZE);         % choose font size
    set(h,'HorizontalAlignment','right','Clipping','off');

    ytick = g.ydir*(-ymax-0.3*ydiff);
    figure(curfig);tick = [int2str(xmin)]; h=text(double(xmin),double(ytick),tick);
    set(h,'FontSize',TICKFONTSIZE);         % choose font size
    set(h,'HorizontalAlignment','center',...
                        'Clipping','off');  % center text

    tick = 'Time (ms)'; figure(curfig);h=text(double(xmin+xdiff/2),double(ytick-0.5*g.ydir*ydiff),tick);
    set(h,'FontSize',TICKFONTSIZE);         % choose font size
    set(h,'HorizontalAlignment','center',...
                        'Clipping','off');  % center text

    tick = [int2str(xmax)]; figure(curfig);h=text(double(xmax),double(ytick),tick);
    set(h,'FontSize',TICKFONTSIZE);         % choose font size
    set(h,'HorizontalAlignment','center',...
                        'Clipping','off');  % center text

end

%        'set(gcbf, ''''unit'''', ''''pixel'''');' ...
%        'tmp = get(gcbf, ''''position'''');' ...
%        'tmp2 = get(0, ''''screensize'''');' ...
%        'if tmp(2)+500 > tmp2(4), tmp(2) = tmp2(4)-500; end;' ...
%        'set(gcbf, ''''position'''', [ tmp(1) tmp(2) 560   420]);' ...
if ~strcmpi(g.axcopycom, 'off')
    if length(g.chans) > 100
        fprintf('Disabling axis copy feature to speed up plotting');
    else
        if strcmpi(g.axcopycom, 'on')
            g.axcopycom = [ 'axis on;' ...
                    'tmp = get(gca, ''''userdata'''');' ...
                    'if ~isempty(tmp), xlabel(tmp{1});' ...
                    'ylabel(tmp{2});' ...
                    'if length(tmp) == 3 && ~isempty(tmp{3}), legend(tmp{3}{:}); end;' ...
                    'end; clear tmp tmp2;' ];
        end
        axcopy(gcf, g.axcopycom); % turn on popup feature
    end
end
icadefs;
set(gca,'Color',BACKCOLOR);               % set the background color