classdef lsl_streaminfo < handle
	% The stream_info object stores the declaration of a data stream.
	% Represents the following information:
	%  a) stream data format (#channels, channel format)
	%  b) core information (stream name, content type, sampling rate)
	%  c) optional meta-data about the stream content (channel labels, measurement units, etc.)
	%
	% Available to recipients of a stream and also written to disk when being recorded
	% (corresponds to the file header).

    properties
        LibHandle = 0;      % this is a handle to the liblsl library (to call its functions)
        InfoHandle = 0;     % this is a handle to an lsl_streaminfo object within the library.
    end

    methods

        function self = lsl_streaminfo(lib,name,type,channelcount,samplingrate,channelformat,sourceid)
            % Construct a new stream_info object.
            % StreamInfo = lsl_streaminfo(LibHandle,Name,Type,ChannelCount,SamplingRate,ChannelFormat,SourceId)
            %
            % Core stream information is specified here. Any remaining meta-data can be added later.
            %
            % In:
            % 	Name : Name of the stream. Describes the device (or product series) that this stream makes available
            %		   (for use by programs, experimenters or data analysts).
            %
            %   Type : Content type of the stream. Please see Table of Content Types in the documentation
            %          for naming recommendations.
            %
            %   ChannelCount : Number of channels per sample. This stays constant for the lifetime of the stream.
            %
            %   NominalSrate : The sampling rate (in Hz) as advertised by the data source,
            %                  if regular (otherwise set to IRREGULAR_RATE).
            %
            %   ChannelFormat : Format/type of each channel. If your channels have different formats,
            %                   consider supplying multiple streams or use the largest type that can
            %                   hold them all (such as cf_double64). The possible formats are:
            %                   'cf_float32', 'cf_double64', 'cf_string', 'cf_int32', 'cf_int16',
            %                   'cf_int8'
            %
            %   SourceId: Unique identifier of the source or device, if available (such as the serial number).
            %             Allows recipients to recover from failure even after the serving app or device crashes.
            %			  May in some cases also be constructed from device settings.

            self.LibHandle = lib;

            if ~exist('type','var') && ~ischar(name)
                % assume that the second argument is a handle
                self.InfoHandle = name;
            else
                % parse inputs
                if ~exist('channelcount','var') || isempty(channelcount) channelcount = 1; end
                if ~exist('samplingrate','var') || isempty(samplingrate) samplingrate = 0; end
                if ~exist('channelformat','var') || isempty(channelformat) channelformat = 'cf_float32'; end
                if ~exist('sourceid','var') || isempty(sourceid) sourceid = ''; end

                % translate channel formats
                if ischar(channelformat)
                    switch(channelformat)
                        case 'cf_float32'       % For up to 24-bit precision measurements in the appropriate physical unit
                            channelformat = 1;  % (e.g., microvolts). Integers from -16777216 to 16777216 are represented accurately.
                        case 'cf_double64'      % For universal numeric data as long as permitted by network & disk budget.
                            channelformat = 2;  % The largest representable integer is 53-bit.
                        case 'cf_string'        % For variable-length ASCII strings or data blobs, such as video frames,
                            channelformat = 3;  % complex event descriptions, etc.
                        case 'cf_int32'         % For high-rate digitized formats that require 32-bit precision. Depends critically on
                            channelformat = 4;  % meta-data to represent meaningful units. Useful for application event codes or other coded data.
                        case 'cf_int16'         % For very high rate signals (40Khz+) or consumer-grade audio
                            channelformat = 5;  % (for professional audio float is recommended).
                        case 'cf_int8'          % For binary signals or other coded data.
                            channelformat = 6;  % Not recommended for encoding string data.
                        case 'cf_int64'         % For future compatibility. Support for this type is not yet widespread in programming languages
                            channelformat = 7;  % and therefore not exposed in the API for all languages. Some builds of liblsl will not be able to
                                                % send or receive data of this type.
                        otherwise
                            error('Unsupported channel format.');
                    end
                end

                % create info object
                self.InfoHandle = lsl_create_streaminfo(lib,name,type,channelcount,samplingrate,channelformat,sourceid);
            end
        end

        function delete(self)
            % Destroy the streaminfo when it is deleted.
            lsl_destroy_streaminfo(self.LibHandle,self.InfoHandle);
        end

		% === Core Information ===
		% (these fields are assigned at construction)

        function result = name(self)
            % Get the name of the stream.
            % This is a human-readable name. For streams offered by device modules, it refers to the type of device or product series
            % that is generating the data of the stream. If the source is an application, the name may be a more generic or specific identifier.
            % Multiple streams with the same name can coexist, though potentially at the cost of
            % ambiguity (for the recording app or experimenter).

            result = lsl_get_name(self.LibHandle,self.InfoHandle);
        end

        function result = type(self)
            % Get the content type of the stream.
            % The content type is a short string such as "EEG", "Gaze" which describes the content carried by the channel (if known).
            % If a stream contains mixed content this value need not be assigned but may instead be stored in the description of channel types.
            % To be useful to applications and automated processing systems using the recommended content types is preferred.
            % See Table of Content Types in the documentation.

            result = lsl_get_type(self.LibHandle,self.InfoHandle);
        end

        function result = channel_count(self)
            % Get the number of channels of the stream.
            % A stream has at least one channel; the channel count stays constant for all samples.

            result = lsl_get_channel_count(self.LibHandle,self.InfoHandle);
        end

        function result = nominal_srate(self)
            % Get the sampling rate of the stream, according to the source (in Hz).
            % If a stream is irregularly sampled, this should be set to IRREGULAR_RATE.
            %
            % Note that no data will be lost even if this sampling rate is incorrect or if a device has temporary
            % hiccups, since all samples will be recorded anyway (except for those dropped by the device itself). However,
            % when the recording is imported into an application, a good importer may correct such errors more accurately
            % if the advertised sampling rate was close to the specs of the device.

            result = lsl_get_nominal_srate(self.LibHandle,self.InfoHandle);
        end

        function result = channel_format(self)
            % Get the channel format of the stream.
            % All channels in a stream have the same format. However, a device might offer multiple time-synched streams
            % each with its own format.

            channel_format_strings = {'cf_float32','cf_double64','cf_string','cf_int32','cf_int16','cf_int8','cf_int64'};
            result = channel_format_strings{lsl_get_channel_format(self.LibHandle,self.InfoHandle)};
        end

        function result = source_id(self)
            % Get the unique identifier of the stream's source, if any.
            % The unique source (or device) identifier is an optional piece of information that, if available, allows that
            % endpoints (such as the recording program) can re-acquire a stream automatically once it is back online.

            result = lsl_get_source_id(self.LibHandle,self.InfoHandle);
        end

		% === Additional Network Info ===
		% (these fields are auto-assigned once the streaminfo is bound to an outlet)

        function result = version(self)
            % Get the protocol version used to deliver the stream.

            result = lsl_get_version(self.LibHandle,self.InfoHandle);
        end

        function result = created_at(self)
            % Get the creation time stamp of the stream.
            % This is the time stamp when the stream was first created
            % (as determined via lsl_local_clock() on the providing machine).

            result = lsl_get_created_at(self.LibHandle,self.InfoHandle);
        end

        function result = uid(self)
            % Get the unique ID of the stream outlet (once assigned).
            % This is a unique identifier of the stream outlet, and is guaranteed to be different
            % across multiple instantiations of the same outlet (e.g., after a re-start).

            result = lsl_get_uid(self.LibHandle,self.InfoHandle);
        end

        function result = session_id(self)
            % Get the session ID for the given stream.
            % The session id is an optional human-assigned identifier of the recording session.
            % While it is rarely used, it can be used to prevent concurrent recording activitites
            % on the same sub-network (e.g., in multiple experiment areas) from seeing each other's streams
            % (assigned via a configuration file by the experimenter, see Configuration File in the docs).

            result = lsl_get_session_id(self.LibHandle,self.InfoHandle);
        end

        function result = desc(self)
            % Get access to the extended description of the stream.
            % It is highly recommended that at least the channel labels are described here.
            % See code examples in the documentation. Other information, such as amplifier settings,
            % measurement units if deviating from defaults, setup information, subject information, etc.,
            % can be specified here, as well. See Meta-Data Recommendations in the docs.

            result = lsl_xml_ptr(self.LibHandle,lsl_get_desc(self.LibHandle,self.InfoHandle));
        end

        function result = as_xml(self)
            % Retrieve the entire stream_info in XML format.
            % This yields an XML document (in string form) whose top-level element is <info>. The info element contains
            % one element for each field of the stream_info class, including:
            %  a) the core elements <name>, <type>, <channel_count>, <nominal_srate>, <channel_format>, <source_id>
            %  b) the misc elements <version>, <created_at>, <uid>, <session_id>, <v4address>, <v4data_port>, <v4service_port>, <v6address>, <v6data_port>, <v6service_port>
            %  c) the extended description element <desc> with user-defined sub-elements.

            result = lsl_get_xml(self.LibHandle,self.InfoHandle);
        end

    end
end


classdef GmsVariablesAdapter < handle

    properties (Access = private)
       adaptedDataBase
    end

    properties (Access = private)
        nnode
        nfacet
        nelem
        nodeInfo
        connecInfo
        facetsInfo
    end

    methods (Access = public)

        function obj = GmsVariablesAdapter(readDataBase)
           obj.init(readDataBase);
        end

        function aDB = getAdaptedDB(obj)
            obj.obtainCoordinates();
            obj.obtainConnectivities();
            obj.obtainSetsInfo();
            obj.obtainMasterAndSlaveRelation();
            obj.obtainCorners();
            aDB = obj.adaptedDataBase;
        end

    end

    methods (Access = private)

        function init(obj,d)
            obj.nnode      = d.nnode;
            obj.nfacet     = d.nfacet;
            obj.nelem      = d.nelem;
            obj.nodeInfo   = d.nodeInfo;
            obj.connecInfo = d.connecInfo;
            obj.facetsInfo = d.facetsInfo;
        end

        function obtainCoordinates(obj)
            coordi = obj.nodeInfo;
            obj.adaptedDataBase.coord = zeros(obj.nnode,3);
            obj.adaptedDataBase.coord = coordi(:,1:2);
        end

        function obtainSetsInfo(obj)
            connect = obj.connecInfo;
            elementInSet = connect(:,4);
            sets = unique(elementInSet);
            nsets = size(sets,1);
            isElemInThisSet = cell(nsets,1);
            for iset = 1:nsets
                setNum = sets(iset);
                isElemInThisSet{iset} = find(elementInSet == setNum);
            end
            obj.adaptedDataBase.isElemInThisSet = isElemInThisSet;
        end

        function obtainConnectivities(obj)
            connect = obj.connecInfo;
            obj.adaptedDataBase.connec = connect(:,1:3);
        end

        function obtainMasterAndSlaveRelation(obj)
            coord = obj.adaptedDataBase.coord;
            msRelator = MasterSlaveRelator(coord);
            masterSlave = msRelator.getRelation();
            obj.adaptedDataBase.masterSlave = masterSlave;
        end

        function obtainCorners(obj)
            coord = obj.adaptedDataBase.coord;
            cD = CellNodesDescriptor(coord);
            corners = cD.cornerNodes;
            obj.adaptedDataBase.corners = corners;
        end

    end

end


%Cytokinesis process test case
%
% Author: Jayodita Sanghvi, jayodita@stanford.edu
% Author: Jared Jacobs, jmjacobs@stanford.edu
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 9/8/2010
classdef Cytokinesis_Test < edu.stanford.covert.cell.sim.ProcessTestCase
    methods
        function this = Cytokinesis_Test(name)
            this = this@edu.stanford.covert.cell.sim.ProcessTestCase(name);
        end

        function loadTestFixture(this)
            this.loadTestFixture@edu.stanford.covert.cell.sim.ProcessTestCase();
            m = this.process;

            c = m.chromosome;
            c.segregated = true;

            g = m.geometry;
            g.width = 200 * 1e-9;
            g.pinchedDiameter = 200 * 1e-9;
        end
    end

    %tests
    methods
        function testNumEdges(this)
            m = this.process;
            g = m.geometry;
            r = m.ftsZRing;
            assertTrue(r.numEdges > 0);
            assertTrue(r.numEdges * r.filamentLengthInNm*1e-9 < g.pinchedCircumference);
        end

        function testBindingUnlimited(this)
             m = this.process;
             r = m.ftsZRing;

             m.substrates(:) = 1e6;
             m.enzymes(m.enzymeIndexs_ftsZ_GTP_polymer) = 1e3;
             m.rateFilamentBindingMembrane = 1;  %force binding
             m.rateFtsZGtpHydrolysis = 0;

             m.evolveState();
             assertEqual(0, r.numEdgesOneStraight);
             assertEqual(r.numEdges, r.numEdgesTwoStraight);
             assertEqual(1e3-2*r.numEdges, m.enzymes(m.enzymeIndexs_ftsZ_GTP_polymer));
             assertEqual(2*r.numEdges, m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer));
        end

        function testBindingPolymerLimited(this)
             m = this.process;
             r = m.ftsZRing;

             m.substrates(:) = 1e6;
             m.enzymes(m.enzymeIndexs_ftsZ_GTP_polymer) = 1;
             m.rateFilamentBindingMembrane = 1;  %force binding

             m.evolveState();
             assertEqual(1, r.numEdgesOneStraight);
             assertEqual(0, m.enzymes(m.enzymeIndexs_ftsZ_GTP_polymer));
             assertEqual(1, m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer));
        end

        function testBendingUnlimited(this)
            m = this.process;
            g = m.geometry;
            r = m.ftsZRing;

            r.numEdgesTwoStraight = r.numEdges;
            m.substrates(:) = 1e6;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer) = 2*r.numEdges;
            m.rateFtsZGtpHydrolysis = 1;  %force bending
            m.rateFilamentDissociation = 0;

            initialDiameter = g.pinchedDiameter;

            m.evolveState();
            assertEqual(0, r.numEdgesTwoStraight);
            assertEqual(r.numEdges, r.numEdgesTwoBent);
            assertEqual(0, r.numResidualBent);
            assertEqual(2*r.numEdges, m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer));
            assertEqual(0, m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer));
            assertEqual(1e6-(2*r.numEdges*r.numFtsZSubunitsPerFilament), m.substrates(m.substrateIndexs_water));
            assertEqual(1e6+(2*r.numEdges*r.numFtsZSubunitsPerFilament), m.substrates(m.substrateIndexs_phosphate));
            assertEqual(1e6+(2*r.numEdges*r.numFtsZSubunitsPerFilament), m.substrates(m.substrateIndexs_hydrogen));
            assertTrue(g.pinchedDiameter < initialDiameter);
        end

        function testBendingRateLimited(this)
            m = this.process;
            g = m.geometry;
            r = m.ftsZRing;

            r.numEdgesTwoStraight = r.numEdges;
            m.substrates(:) = 1e6;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer) = 30;

            initialDiameter = g.pinchedDiameter;

            m.evolveState();
            assertTrue(r.numEdgesTwoBent > 0);
            assertTrue(r.numEdgesTwoBent < r.numEdges);
            assertEqual(initialDiameter, g.pinchedDiameter);

            m.rateFtsZGtpHydrolysis = 1;  %force bending
            m.rateFilamentDissociation = 0;

            m.evolveState();
            assertEqual(0, r.numEdgesTwoStraight);
            assertEqual(r.numEdges, r.numEdgesTwoBent);
            assertTrue(g.pinchedDiameter < initialDiameter);
        end

        function testFirstRingFallingOffUnlimited(this)
            m = this.process;
            r = m.ftsZRing;

            m.substrates(:) = 0;
            m.enzymes(:) = 0;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer) = 30;
            r.numEdgesTwoBent = r.numEdges;
            m.rateFilamentDissociation = 1;  %force falling off

            m.evolveState();
            assertEqual(0, r.numEdgesTwoBent);
            assertEqual(r.numEdges, r.numResidualBent);
            assertEqual(r.numEdges, m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer));
            assertEqual(...
                r.numEdges*r.numFtsZSubunitsPerFilament,...
                m.enzymes(m.enzymeIndexs_ftsZ_GDP));
        end

        function testFirstRingFallingOffRateLimited(this)
            m = this.process;
            r = m.ftsZRing;

            m.substrates(:) = 0;
            m.enzymes(:) = 0;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer) = 30;
            r.numEdgesTwoBent = r.numEdges;

            m.evolveState();
            assertTrue(r.numEdgesTwoBent < r.numEdges);
            assertTrue(r.numEdgesTwoBent > 0);
            assertTrue(r.numResidualBent > 0);
            assertTrue(r.numResidualBent < r.numEdges);

            m.rateFilamentDissociation = 1;  %force falling off

            m.evolveState();
            assertEqual(0, r.numEdgesTwoBent);
            assertEqual(r.numEdges, r.numResidualBent);
        end

        function testSecondRingFallingOffUnlimited(this)
            m = this.process;
            r = m.ftsZRing;

            r.numEdgesOneStraight = r.numEdges;
            r.numResidualBent = 5;
            m.enzymes(:) = 0;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer) = r.numEdgesOneStraight;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer) = r.numResidualBent;

            m.rateFilamentDissociation = 1;

            m.evolveState();
            assertEqual(0, m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer));
            assertEqual(0, r.numResidualBent);
            assertEqual(...
                5 * r.numFtsZSubunitsPerFilament,...
                m.enzymes(m.enzymeIndexs_ftsZ_GDP));
        end

        function testSecondRingFallingOffRateLimited(this)
            m = this.process;
            r = m.ftsZRing;

            r.numEdgesOneStraight = r.numEdges;
            r.numResidualBent = 12;
            m.enzymes(:) = 0;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GTP_polymer) = r.numEdgesOneStraight;
            m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer) = r.numResidualBent;

            m.evolveState();
            assertTrue(r.numResidualBent < 15);
            assertTrue(r.numResidualBent > 0);

            m.rateFilamentDissociation = 1;

            m.evolveState();
            assertEqual(0, m.boundEnzymes(m.enzymeIndexs_ftsZ_GDP_polymer));
            assertEqual(0, r.numResidualBent);
            assertEqual(...
                12 * r.numFtsZSubunitsPerFilament,...
                m.enzymes(m.enzymeIndexs_ftsZ_GDP));
        end

        function testPinchCell(this)
            m = this.process;
            g = m.geometry;
            r = m.ftsZRing;

            m.enzymes(:) = 1e6;
            m.substrates(:) = 1e6;
            m.rateFtsZGtpHydrolysis = 1;
            m.rateFilamentBindingMembrane = 1;
            m.rateFilamentDissociation = 1;

            for j = 1:75
                m.evolveState();
            end

            assertEqual(0, g.pinchedDiameter);
            assertTrue(g.pinched);
            assertEqual(0, r.numEdges);
            assertEqual(0, r.numEdgesOneStraight);
            assertEqual(0, r.numEdgesTwoStraight);
            assertEqual(0, r.numEdgesTwoBent);
            assertEqual(0, r.numResidualBent);
        end

        function testNoEnzymes(this)
            m = this.process;
            m.enzymes(:) = 0;
            m.substrates(:) = 0;
            m.rateFtsZGtpHydrolysis = 1;
            m.rateFilamentBindingMembrane = 1;
            m.rateFilamentDissociation = 1;
            for j = 1:10
                m.evolveState();
            end

            g = m.geometry;
            assertEqual(g.width, g.pinchedDiameter);
        end

        function testNoWater(this)
            m = this.process;
            m.enzymes(:) = 1e6;
            m.substrates(:) = 1e6;
            m.substrates(m.substrateIndexs_water) = 0;
            m.rateFtsZGtpHydrolysis = 1;
            m.rateFilamentBindingMembrane = 1;
            m.rateFilamentDissociation = 1;
            for j = 1:10
                m.evolveState();
            end

            g = m.geometry;
            assertEqual(g.width, g.pinchedDiameter);
        end

        function testGeneEssentiality(this)
            m = this.process;
            g = m.geometry;
            g.pinchedDiameter = g.width;
            m.enzymes(:) = 1e6;
            m.substrates(:) = 1e6;
            m.rateFtsZGtpHydrolysis = 1;
            m.rateFilamentBindingMembrane = 1;
            m.rateFilamentDissociation = 1;

            initialPinchedDiameter = g.pinchedDiameter;

            this.helpTestGeneEssentiality(...
                {'MG_224'},... %cell division protein ftsZ
                @(m, i) m.geometry.pinchedDiameter < initialPinchedDiameter,...
                struct('lengthSec',10));
        end
    end
end


function params = SetDefaultParams(...
  params,default_windows,default_trans_types,relativeParams)

fns = params(1:2:end-1);
if ismember('windows',fns) || ...
    (ismember('window_offsets',fns) && ...
     (ismember('window_radii',fns) || ...
      (ismember('min_window_radius',fns) && ...
       ismember('max_window_radius',fns) && ...
       ismember('nwindow_radii',fns)))),
else
  params(end+1:end+2) = {'windows',default_windows};
end
if ~ismember('trans_types',fns),
  params(end+1:end+2) = {'trans_types',default_trans_types};
end
params(end+1:end+2) = {'relativeParams',relativeParams};


function [translatedAbundances,normalizedAbundances,unmappedRows]=translateMetagenome2AGORA(MetagenomeAbundancePath,sequencingDepth,reconstructionResource)
% Translates organism identifiers in a published metagenomic or 16S rRNA
% data file with organism abundances (retrieved e.g., from  MetaPhlAn) to
% AGORA pan-model IDs. This will not catch every case since the format of
% input files with abundance data greatly varies. Feel free to modify this
% function and submit a pull request to enable more input files to be
% translated to AGORA. Moreover, slight spelling variations in taxa across
% input files may  result to taxa not being mapped. Check the unmappedRows
% output to identify these cases and modify the function accordingly.
% Pan-models that can be used to create microbiome models in mgPipe can be
% created with the function createPanModels.
% Consider running the function updateTaxonomyInfoAGORA to retrieve the
% most recent taxonomic assignment for the taxa to map. You might need to
% update your input file based on updated taxonomic classifications.
%
% USAGE:
%
%   [translatedAbundances,normalizedAbundances,unmappedRows]=translateMetagenome2AGORA(MetagenomeAbundancePath,sequencingDepth,reconstructionResource)
%
% INPUT:
%   MetagenomeAbundancePath   String containing the path to csv file with
%                             organism abundance data retrieved from
%                             16S rRNA or metagenomic samples (example:
%                             'SRP065497_taxonomy_abundances_v3.0.tsv').
%
% OPTIONAL INPUTS:
%   sequencingDepth           Sequencing depth on the taxonomical level
%                             in the input data (e.g., genus, species).
%                             Allowed inputs are 'Species','Genus',
%                             'Family','Order', 'Class', 'Phylum'.
%                             Default: 'Species'.
%  reconstructionResource     Name of the reconstruction resource to map
%                             the abundances to. Allowed inputs are 'AGORA',
%                             'AGORA2'. Default: 'AGORA'
%
% OUTPUTS:
%   translatedAbundances      Abundances with organism names from the
%                             input file translated to AGORA pan-model IDs
%   normalizedAbundances      Translated abundances normalized so they sum
%                             up to 1 for each sample
%   unmappedRows              Taxa on the selected taxonomical level that
%                             could not be mapped to AGORA pan-models
%
% .. Author: Almut Heinken, 02/2019
%                           10/2019: edited mapping based on an output file
%                           of the NG-Tax pipeline (Ramiro-Garcia et al.,
%                           F1000Res 2016). Made input more intuitive.
%                           11/2021: adapted based on updated taxonomy,
%                           added option to map to either AGORA or AGORA2

if nargin <2
    sequencingDepth = 'Species';
end

if nargin <3
    reconstructionResource = 'AGORA';
end

% read the csv file with the abundance data
metagenome_abundance = readInputTableForPipeline(MetagenomeAbundancePath);
metagenome_abundance{1,1}='';

% list the taxonomical levels
taxLevels={
    'Phylum'
    'Class'
    'Order'
    'Family'
    'Genus'
    'Species'
    };

if strcmp(reconstructionResource,'AGORA')
    infoFile = readInputTableForPipeline('AGORA_infoFile.xlsx');
elseif strcmp(reconstructionResource,'AGORA2')
    infoFile = readInputTableForPipeline('AGORA2_infoFile.xlsx');
else
    error('Incorrect input for reconstruction resource!')
end

% List all taxa in the AGORA resource
agoraTaxa={};
for i=1:length(taxLevels)
    % gets all pan-model IDs
    findTaxCol = find(strcmp(infoFile(1, :), taxLevels{i}));
    allTaxa = unique(infoFile(2:end, findTaxCol));
    allTaxa(:,1)=strrep(allTaxa(:,1),' ','_');
    allTaxa(:,1)=strcat('pan',allTaxa(:,1));
    agoraTaxa=vertcat(agoraTaxa,allTaxa);
end
allTaxa = unique(infoFile(2:end, find(strcmp(infoFile(1, :), 'ModelAGORA'))));
agoraTaxa=vertcat(allTaxa,agoraTaxa);

% format the input file so it can be matched
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),';','|');

% match different phrasing
% This may not catch all cases! Feel free to add additional cases of
% mismatching nomenclature and submit a pull request.
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Candidatus_','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_Family_XI_Incertae_Sedis',' Incertae Sedis XI');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_Family_XIII_Incertae_Sedis',' Incertae Sedis XIII');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'typhimurium','enterica');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_1','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_2','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_3','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_4','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_5','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_6','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_7','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_8','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_9','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),' family','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),' order','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'[','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),']','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),' ','_');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'.','_');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'-','_');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'__','_');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'___','_');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Ruminococcus_gauvreauii_group','Ruminococcus');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Ruminococcus_torques_group','Mediterraneibacter');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Ruminococcus_gnavus_group','Mediterraneibacter');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Eubacterium_coprostanoligenes_group','Eubacterium');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Eubacterium_eligens_group','Lachnospira');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Eubacterium_hallii_group','Anaerobutyricum');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Eubacterium_ruminantium_group','Eubacterium');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Eubacterium_ventriosum_group','Eubacterium');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Eubacterium_xylanophilum_group','Eubacterium');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'Clostridium_innocuum_group','Erysipelatoclostridium');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_sensu_stricto_1','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_sensu_stricto','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_sensu_stricto','');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Actinomyces|s_Actinomyces_cardiffensis','g_Schaalia|s_Actinomyces_cardiffensis');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Actinomyces|s_Actinomyces_georgiae','g_Schaalia|s_Actinomyces_georgiae');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Actinomyces|s_Actinomyces_meyeri','g_Schaalia|s_Actinomyces_meyeri');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Actinomyces|s_Actinomyces_odontolyticus','g_Schaalia|s_Actinomyces_odontolytica');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Actinomyces|s_Actinomyces_odontolyticus','g_Schaalia|s_Actinomyces_odontolytica');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Actinomyces|s_Actinomyces_odontolyticus','g_Schaalia|s_Actinomyces_odontolytica');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Absiella|s_Absiella_dolichum','g_Amedibacillus|s_Absiella_dolichus');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Eubacterium|s_Eubacterium_hallii','g_Anaerobutyricum|s_Eubacterium_hallii');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Clostridium|s_citroniae','g_Lachnoclostridium|s_citroniae');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Clostridium|s_difficile','g_Clostridioides|s_difficile');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Clostridium|s_sordellii','g_Paeniclostridium|s_sordellii');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Clostridium|s_hathewayi','g_Hungatella|s_hathewayi');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Eubacterium|s_biforme','g_Holdemanella|s_biformis');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Eubacterium|s_dolichum','g_Amedibacillus|s_dolichus');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Propionibacterium|s_acnes','g_Cutibacterium|s_acnes');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Propionibacterium|s_granulosum','g_Cutibacterium|s_granulosum');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Ruminococcus|s_gnavus','g_Mediterraneibacter|s_gnavus');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'g_Ruminococcus|s_torques','g_Mediterraneibacter|s_torques');
% remove all rows that are not of the desired sequencing depth and edit the
% taxon descriptions to enable mapping to AGORA
% specific for MetaPhlAn input files
sLevels={
    'Species'   's_'
    'Genus'     'g_'
    'Family'    'f_'
    'Order'     'o_'
    'Class' 	'c_'
    'Phylum'    'p_'
    };
mpsDepth=sLevels{find(strcmp(sLevels(:,1),sequencingDepth)),2};
delRows=[];
cnt=1;
for i=2:size(metagenome_abundance,1)
    findSDepth=strsplit(metagenome_abundance{i,1},'|');
    % remove taxon entries without any mapping
    delArray=[];
    delCnt=1;
    for j=1:length(findSDepth)
        if any(strcmp(findSDepth{j},{'s_','g_','f_','o_','c_'}))
            delArray(delCnt)=j;
            delCnt=delCnt+1;
        end
    end
    findSDepth(delArray)=[];
    if length(findSDepth)>1
            % only get the sequencing level you are looking for
            if strncmp(findSDepth{1,end},mpsDepth,2) && length(findSDepth{1,end})>2
                % if the genus information is missing on the species level
                if strncmp(findSDepth{1,end},'s_',2)
                    sname=regexprep(findSDepth{1,end},mpsDepth,'','once');
                    % if species name also contains genus
                    if contains(sname,'_')
                        sname=strsplit(sname,'_');
                        sname=sname{1,2};
                    end
                    gname=regexprep(findSDepth{1,end-1},'g_','','once');
                    if ~strncmp(gname,sname,length(gname))
                        metagenome_abundance{i,1}=strcat(gname,'_',sname);
                    end
                else
                    metagenome_abundance{i,1}=findSDepth{1,end};
                    metagenome_abundance{i,1}=regexprep(metagenome_abundance{i,1},mpsDepth,'','once');
                end
            else
                delRows(cnt,1)=i;
                cnt=cnt+1;
            end
    end
end
metagenome_abundance(delRows,:)=[];

% Remove unclassified organisms
agoraTaxa(strncmp('unclassified', agoraTaxa, 12)) = [];
metagenome_abundance(~cellfun(@isempty, strfind(metagenome_abundance(:,1),'_uncl')),:) = [];

% replace unknown species with pan-genus model
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),'_cf','');

% summarize duplicate entries
[uniqueA,i,j] = unique(metagenome_abundance(:,1));
n  = accumarray(j(:),1);
Dupes=uniqueA(find(n>1));
delArray=[];
cnt=1;
for i=1:length(Dupes)
    indexToDupes = find(strcmp(metagenome_abundance(:,1),Dupes{i}));
    for j=2:length(indexToDupes)
        for k=2:size(metagenome_abundance,2)
            if contains(version,'(R202') % for Matlab R2020a and newer
                metagenome_abundance{indexToDupes(1),k}=metagenome_abundance{indexToDupes(1),k}+metagenome_abundance{indexToDupes(j),k};
            else
                metagenome_abundance{indexToDupes(1),k}=num2str(str2double(metagenome_abundance{indexToDupes(1),k})+str2double(metagenome_abundance{indexToDupes(j),k}));
            end
        end
        delArray(cnt,1)=indexToDupes(j);
        cnt=cnt+1;
    end
end
metagenome_abundance(delArray,:)=[];

% match and find overlapping IDs
% metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),mpsDepth,'');
metagenome_abundance(:,1)=strrep(metagenome_abundance(:,1),' ','_');
metagenome_abundance(2:end,1)=strcat('pan',metagenome_abundance(2:end,1));

% remove and report taxa in abundance data table that could not be found in
% AGORA and/or are not of the desired sequencing depth

findTaxCol = find(strcmp(infoFile(1, :), sequencingDepth));
allTaxa = unique(infoFile(2:end, findTaxCol));
allTaxa(:,1)=strrep(allTaxa(:,1),' ','_');
allTaxa(:,1)=strcat('pan',allTaxa(:,1));
[unmappedRows,IA] = setdiff(metagenome_abundance(:,1),allTaxa);
IA(1,:)=[];
metagenome_abundance(IA,:)=[];
translatedAbundances=metagenome_abundance;

% normalize the abundances so that sum for each individual is 1
normalizedAbundances=translatedAbundances;
for i=2:size(translatedAbundances,2)
    if contains(version,'(R202') % for Matlab R2020a and newer
        if sum(cell2mat(translatedAbundances(2:end,i)))>0
            for j=2:size(translatedAbundances,1)
                normalizedAbundances{j,i}=cell2mat(translatedAbundances(j,i))/sum(cell2mat(translatedAbundances(2:end,i)));
            end
        else
            for j=2:size(translatedAbundances,1)
                normalizedAbundances{j,i}=translatedAbundances{j,i};
            end
        end
    else
    if sum(str2double(translatedAbundances(2:end,i)))>0
        for j=2:size(translatedAbundances,1)
            normalizedAbundances{j,i}=num2str(str2double(translatedAbundances(j,i))/sum(str2double(translatedAbundances(2:end,i))));
        end
    else
        for j=2:size(translatedAbundances,1)
            normalizedAbundances{j,i}=translatedAbundances{j,i};
        end
    end
    end
end

% export the translated abundances
writetable(cell2table(translatedAbundances),'translatedAbundances','FileType','text','WriteVariableNames',false,'Delimiter','tab');

end


function mtrResampleSISPathways(fgFilenames,samplerOptsFilename,numSamples,fgResampledFilename,bCalcStats)

% Run metrotrac
% mtrResampleSISPathways({'paths_newpdf.dat'},'met_params.txt',1000000,'res
% amp_newpdf.dat',1);
%% MUST MAKE SURE THIS PROGRAM IS COMPILED AND IN RIGHT LOCATION
if(ispc)
    executable = which('updatestats.exe');
else
    error('Not compiled for Linux.');
end

% Get importance weight file that we need to resample with
for ii = 1:length(fgFilenames)
    statvec_names{ii} = sprintf('temp%d_statvec_iw.dat',ii);
    if( bCalcStats )
        args = sprintf(' -v %s -i %s -o temp.dat -s 4 -sf %s', samplerOptsFilename, fgFilenames{ii}, statvec_names{ii});
        cmd = [executable args];
        disp(cmd); disp('...')
        [s, ret_info] = system(cmd,'-echo');
        disp('Done')
    end
end

% Resample
%resampleSISPathways(numSamples, {'temp_statvec_iw.dat'}, {fgFilename},
%fgResampledFilename);
resampleSISPathways(numSamples, statvec_names, fgFilenames, fgResampledFilename);

if( bCalcStats )
    % Cleanup temp files
    delete('temp.dat');
end

% for ii = 1:length(statvec_names)
%     delete(statvec_names{ii});
% end

return;

function resampleSISPathways(numSamples, weightFilenames, pdbInFilenames, pdbOutFilename)

numFiles = length(weightFilenames);
sizeFiles = [];

% Load multiple importance weight files
iw = [];
for f = 1:numFiles
    iwtemp = mtrLoadStatvec(weightFilenames{f});
    iw = [iw; iwtemp];
    sizeFiles(f) = length(iwtemp);
end
iw(isnan(iw)) = -Inf;
iw = exp(iw);

% Resample pathways according to normalized weight
sample_ind = randsample([1:length(iw)],numSamples,true,iw);
unique_sample_ind = unique(sample_ind);
unique_sample_w = 0;
for i = 1:length(unique_sample_ind)
    unique_sample_w(i) =  sum(sample_ind == unique_sample_ind(i));
end

pdbtemp = [];
% Load the pathways and create resampled database
for f = 1:numFiles
    disp('Loading Pathway Database...');
    pdbtemp{f} = mtrPathwayDatabase();
    pdbtemp{f} = loadDatabase(pdbtemp{f},pdbInFilenames{f});
end

% create new database
pdb = mtrPathwayDatabase();
pdb = pdbtemp{1};
sub_unique_sample_ind = unique_sample_ind( unique_sample_ind <= length(pdbtemp{1}.pathways) & unique_sample_ind > 0);
pdb.pathways = pdbtemp{1}.pathways(sub_unique_sample_ind);

for f = 2:numFiles
    unique_sample_ind = unique_sample_ind - sizeFiles(f-1);
    sub_unique_sample_ind = unique_sample_ind( unique_sample_ind <= length(pdbtemp{f}.pathways) & unique_sample_ind > 0);
    pdb.pathways = [pdb.pathways pdbtemp{f}.pathways(sub_unique_sample_ind)];
end
% Clear statistic headers
pdb.pathway_statistic_headers = [];

% Create statistic header of just new weights
is_luminance_encoding = 1;
is_computed_per_point = 0;
is_viewable_stat = 1;
agg_name = 'resampleWeight';
local_name = 'NA';
uid = '1';
pdb = addStatisticHeader(pdb,agg_name,local_name,is_luminance_encoding, is_computed_per_point, is_viewable_stat, 1);

% Add the weight data to the path
for p = 1:length(pdb.pathways)
    pdb.pathways(p).path_stat_vector = unique_sample_w(p);
end

% Save out resampled database
disp('Saving new resampled database...');
saveDatabase(pdb,pdbOutFilename);

return;


function varargout = panel_coordinates(varargin)
% PANEL_COORDINATES: Create a panel to add/remove/edit scouts attached to a given 3DViz figure.
%
% USAGE:  bstPanelNew = panel_coordinates('CreatePanel')

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

eval(macro_method);
end

%% ===== CREATE PANEL =====
function bstPanelNew = CreatePanel() %#ok<DEFNU>
    panelName = 'Coordinates';
    % Java initializations
    import java.awt.*;
    import javax.swing.*;
    import org.brainstorm.icon.*;
    % CONSTANTS
    TEXT_HEIGHT = java_scaled('value', 20);
    TEXT_WIDTH  = java_scaled('value', 40);
    jFontText = bst_get('Font', 11);
    % Create tools panel
    jPanelNew = gui_component('Panel');

    % ===== CREATE TOOLBAR =====
    jToolbar = gui_component('Toolbar', jPanelNew, BorderLayout.NORTH);
    jToolbar.setPreferredSize(java_scaled('dimension', 100,25));
        % Button "Select vertex"
        jButtonSelect = gui_component('ToolbarToggle', jToolbar, [], 'Select', IconLoader.ICON_SCOUT_NEW, 'Select surface point', @(h,ev)SetSelectionState(ev.getSource.isSelected()));
        % Button "View in MRI Viewer"
        gui_component('ToolbarButton', jToolbar, [], 'View/MRI', IconLoader.ICON_VIEW_SCOUT_IN_MRI, 'View point in MRI Viewer', @ViewInMriViewer);
        % Button "Remove selection"
        gui_component('ToolbarButton', jToolbar, [], 'Del', IconLoader.ICON_DELETE, 'Remove point selection', @RemoveSelection);

    % ===== Main panel =====
    jPanelMain = gui_river();
        % ===== Coordinates =====
        jPanelCoordinates = gui_river('Coordinates (millimeters)');
            % Coordinates
            jPanelCoordinates.add('br', gui_component('label', jPanelCoordinates, 'tab', ' '));
            jLabelX = gui_component('label', jPanelCoordinates, 'tab', '   X');
            jLabelY = gui_component('label', jPanelCoordinates, 'tab', '   Y');
            jLabelZ = gui_component('label', jPanelCoordinates, 'tab', '   Z');
            jLabelX.setHorizontalAlignment(javax.swing.JLabel.CENTER);
            jLabelY.setHorizontalAlignment(javax.swing.JLabel.CENTER);
            jLabelZ.setHorizontalAlignment(javax.swing.JLabel.CENTER);
            jLabelX.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelY.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelZ.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelX.setFont(jFontText);
            jLabelY.setFont(jFontText);
            jLabelZ.setFont(jFontText);
            % === MRI ===
            jPanelCoordinates.add('br', gui_component('label', jPanelCoordinates, 'tab', 'MRI: '));
            jLabelCoordMriX = JLabel('-');
            jLabelCoordMriY = JLabel('-');
            jLabelCoordMriZ = JLabel('-');
            jLabelCoordMriX.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordMriY.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordMriZ.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordMriX.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordMriY.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordMriZ.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordMriX.setFont(jFontText);
            jLabelCoordMriY.setFont(jFontText);
            jLabelCoordMriZ.setFont(jFontText);
            jPanelCoordinates.add('tab', jLabelCoordMriX);
            jPanelCoordinates.add('tab', jLabelCoordMriY);
            jPanelCoordinates.add('tab', jLabelCoordMriZ);
            % === SCS ===
            jPanelCoordinates.add('br', gui_component('label', jPanelCoordinates, 'tab', 'SCS: '));
            jLabelCoordScsX = JLabel('-');
            jLabelCoordScsY = JLabel('-');
            jLabelCoordScsZ = JLabel('-');
            jLabelCoordScsX.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordScsY.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordScsZ.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordScsX.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordScsY.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordScsZ.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordScsX.setFont(jFontText);
            jLabelCoordScsY.setFont(jFontText);
            jLabelCoordScsZ.setFont(jFontText);
            jPanelCoordinates.add('tab', jLabelCoordScsX);
            jPanelCoordinates.add('tab', jLabelCoordScsY);
            jPanelCoordinates.add('tab', jLabelCoordScsZ);
            % === WORLD ===
            jPanelCoordinates.add('br', gui_component('label', jPanelCoordinates, 'tab', 'World: '));
            jLabelCoordWrlX = JLabel('-');
            jLabelCoordWrlY = JLabel('-');
            jLabelCoordWrlZ = JLabel('-');
            jLabelCoordWrlX.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordWrlY.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordWrlZ.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordWrlX.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordWrlY.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordWrlZ.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordWrlX.setFont(jFontText);
            jLabelCoordWrlY.setFont(jFontText);
            jLabelCoordWrlZ.setFont(jFontText);
            jPanelCoordinates.add('tab', jLabelCoordWrlX);
            jPanelCoordinates.add('tab', jLabelCoordWrlY);
            jPanelCoordinates.add('tab', jLabelCoordWrlZ);
            % === MNI ===
            jPanelCoordinates.add('br', gui_component('label', jPanelCoordinates, 'tab', 'MNI: '));
            jLabelCoordMniX = JLabel('-');
            jLabelCoordMniY = JLabel('-');
            jLabelCoordMniZ = JLabel('-');
            jLabelCoordMniX.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordMniY.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordMniZ.setHorizontalAlignment(javax.swing.JLabel.RIGHT);
            jLabelCoordMniX.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordMniY.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordMniZ.setPreferredSize(Dimension(TEXT_WIDTH, TEXT_HEIGHT));
            jLabelCoordMniX.setFont(jFontText);
            jLabelCoordMniY.setFont(jFontText);
            jLabelCoordMniZ.setFont(jFontText);
            jPanelCoordinates.add('tab', jLabelCoordMniX);
            jPanelCoordinates.add('tab', jLabelCoordMniY);
            jPanelCoordinates.add('tab', jLabelCoordMniZ);
            % === VERTEX INDICE ===
            jLabelVertexInd = JLabel('No vertex selected');
            jLabelVertexInd.setFont(jFontText);
            jPanelCoordinates.add('br', jLabelVertexInd);

        jPanelMain.add('hfill', jPanelCoordinates);
    jPanelNew.add(jPanelMain, BorderLayout.CENTER);

    % Create the BstPanel object that is returned by the function
    % => constructor BstPanel(jHandle, panelName, sControls)
    bstPanelNew = BstPanel(panelName, ...
                           jPanelNew, ...
                           struct('jButtonSelect',     jButtonSelect, ...
                                  'jPanelCoordinates', jPanelCoordinates, ...
                                  'jLabelCoordMriX',   jLabelCoordMriX, ...
                                  'jLabelCoordMriY',   jLabelCoordMriY, ...
                                  'jLabelCoordMriZ',   jLabelCoordMriZ, ...
                                  'jLabelCoordScsX',   jLabelCoordScsX, ...
                                  'jLabelCoordScsY',   jLabelCoordScsY, ...
                                  'jLabelCoordScsZ',   jLabelCoordScsZ, ...
                                  'jLabelCoordWrlX',   jLabelCoordWrlX, ...
                                  'jLabelCoordWrlY',   jLabelCoordWrlY, ...
                                  'jLabelCoordWrlZ',   jLabelCoordWrlZ, ...
                                  'jLabelCoordMniX',   jLabelCoordMniX, ...
                                  'jLabelCoordMniY',   jLabelCoordMniY, ...
                                  'jLabelCoordMniZ',   jLabelCoordMniZ, ...
                                  'jLabelVertexInd',   jLabelVertexInd));

end

%% =================================================================================
%  === EXTERNAL PANEL CALLBACKS  ===================================================
%  =================================================================================
%% ===== UPDATE CALLBACK =====
function UpdatePanel()
    % Get panel controls
    ctrl = bst_get('PanelControls', 'Coordinates');
    if isempty(ctrl)
        return
    end
    % Get current figure
    hFig = bst_figures('GetCurrentFigure', '3D');
    % If a figure is available: get if a point select
    if ~isempty(hFig) && ishandle(hFig)
        CoordinatesSelector = getappdata(hFig, 'CoordinatesSelector');
    else
        CoordinatesSelector = [];
    end
    % Update coordinates (text fields)
    % MRI
    if ~isempty(CoordinatesSelector) && ~isempty(CoordinatesSelector.MRI)
        ctrl.jLabelCoordMriX.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.MRI(1)));
        ctrl.jLabelCoordMriY.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.MRI(2)));
        ctrl.jLabelCoordMriZ.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.MRI(3)));
    else
        ctrl.jLabelCoordMriX.setText('-');
        ctrl.jLabelCoordMriY.setText('-');
        ctrl.jLabelCoordMriZ.setText('-');
    end
    % SCS
    if ~isempty(CoordinatesSelector) && ~isempty(CoordinatesSelector.SCS)
        ctrl.jLabelCoordScsX.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.SCS(1)));
        ctrl.jLabelCoordScsY.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.SCS(2)));
        ctrl.jLabelCoordScsZ.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.SCS(3)));
    else
        ctrl.jLabelCoordScsX.setText('-');
        ctrl.jLabelCoordScsY.setText('-');
        ctrl.jLabelCoordScsZ.setText('-');
    end
    % World
    if ~isempty(CoordinatesSelector) && ~isempty(CoordinatesSelector.World)
        ctrl.jLabelCoordWrlX.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.World(1)));
        ctrl.jLabelCoordWrlY.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.World(2)));
        ctrl.jLabelCoordWrlZ.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.World(3)));
    else
        ctrl.jLabelCoordWrlX.setText('-');
        ctrl.jLabelCoordWrlY.setText('-');
        ctrl.jLabelCoordWrlZ.setText('-');
    end
    % MNI
    if ~isempty(CoordinatesSelector) && ~isempty(CoordinatesSelector.MNI)
        ctrl.jLabelCoordMniX.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.MNI(1)));
        ctrl.jLabelCoordMniY.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.MNI(2)));
        ctrl.jLabelCoordMniZ.setText(sprintf('%3.1f', 1000 * CoordinatesSelector.MNI(3)));
    else
        ctrl.jLabelCoordMniX.setText('-');
        ctrl.jLabelCoordMniY.setText('-');
        ctrl.jLabelCoordMniZ.setText('-');
    end
    % Vertex indice
    if ~isempty(CoordinatesSelector) && ~isempty(CoordinatesSelector.iVertex)
        if ~isempty(CoordinatesSelector.Value)
            strValue = ['     Value: ' num2str(CoordinatesSelector.Value)];
        else
            strValue = '';
        end
        ctrl.jLabelVertexInd.setText(sprintf('<HTML>Vertex #%d %s', CoordinatesSelector.iVertex, strValue));
    else
        ctrl.jLabelVertexInd.setText('No vertex selected');
    end
end

%% ===== FOCUS CHANGED ======
function FocusChangedCallback(isFocused) %#ok<DEFNU>
    if ~isFocused
        RemoveSelection();
    end
end

%% ===== CURRENT FIGURE CHANGED =====
function CurrentFigureChanged_Callback() %#ok<DEFNU>
    UpdatePanel();
end

%% ===============================================================================
%  ====== POINTS SELECTION =======================================================
%  ===============================================================================
%% ===== POINT SELECTION : start/stop =====
% Manual selection of a surface point : start(1), or stop(0)
function SetSelectionState(isSelected)
    % Get panel controls
    ctrl = bst_get('PanelControls', 'Coordinates');
    if isempty(ctrl)
        return
    end
    % Get list of all figures
    hFigures = bst_figures('GetAllFigures');
    if isempty(hFigures)
        ctrl.jButtonSelect.setSelected(0);
        return
    end
    % Start selection
    if isSelected
        % Push toolbar "Select" button
        ctrl.jButtonSelect.setSelected(1);
        % Set 3DViz figures in 'SelectingCorticalSpot' mode
        for hFig = hFigures
            % Keep only figures with surfaces
            TessInfo = getappdata(hFig, 'Surface');
            if ~isempty(TessInfo)
                setappdata(hFig, 'isSelectingCoordinates', 1);
                set(hFig, 'Pointer', 'cross');
            end
        end
    % Stop selection
    else
        % Release toolbar "Select" button
        ctrl.jButtonSelect.setSelected(0);
        % Exit 3DViz figures from SelectingCorticalSpot mode
        for hFig = hFigures
            set(hFig, 'Pointer', 'arrow');
            setappdata(hFig, 'isSelectingCoordinates', 0);
        end
    end
end

%% ===== SELECT POINT =====
% Usage : SelectPoint(hFig) : Point location = user click in figure hFIg
function vi = SelectPoint(hFig, AcceptMri) %#ok<DEFNU>
    if (nargin < 2) || isempty(AcceptMri)
        AcceptMri = 1;
    end
    % Get axes handle
    hAxes = findobj(hFig, '-depth', 1, 'Tag', 'Axes3D');
    % Find the closest surface point that was selected
    [TessInfo, iTess, pout, vout, vi, hPatch] = ClickPointInSurface(hFig);
    if isempty(TessInfo)
        return
    end
    Value = [];

    % Get real location of the selected point (in SCS coord, millimeters)
    switch (TessInfo(iTess).Name)
        case 'Anatomy'
            % If MRI is not accepted
            if ~AcceptMri
                vi = [];
                return
            end
            scsLoc  = pout';
            plotLoc = scsLoc;
            iVertex = [];

            % Get MRI
            sMri = bst_memory('GetMri', TessInfo(iTess).SurfaceFile);

        case {'Scalp', 'InnerSkull', 'OuterSkull', 'Cortex', 'Other', 'FEM'}
            sSurf = bst_memory('GetSurface', TessInfo(iTess).SurfaceFile);
            scsLoc = sSurf.Vertices(vi,:);
            plotLoc = vout;
            iVertex = vi;
            % Get value
            if ~isempty(TessInfo(iTess).Data)
                if ~isempty(TessInfo(iTess).DataSource.GridAtlas)
                    Value = [];
                elseif isempty(TessInfo(iTess).DataSource.Atlas)
                    Value = TessInfo(iTess).Data(vi);
                else
                    % Look for the selected point in one of the scouts
                    iRow = panel_scout('GetScoutForVertex', TessInfo(iTess).DataSource.Atlas, vi);
                    % Return value
                    Value = TessInfo(iTess).Data(iRow(1));
                end
            end
            % Get subject
            SubjectFile = getappdata(hFig, 'SubjectFile');
            if ~isempty(SubjectFile)
                sSubject = bst_get('Subject', SubjectFile);
                % == GET MRI ==
                % If subject has a MRI defined
                if ~isempty(sSubject.iAnatomy)
                    % Load MRI
                    MriFile = sSubject.Anatomy(sSubject.iAnatomy).FileName;
                    sMri = bst_memory('LoadMri', MriFile);
                else
                    sMri = [];
                end
            else
                sMri = [];
            end
        otherwise
            scsLoc = vout;
            plotLoc = scsLoc;
            iVertex = [];
            sMri = [];
    end

    % ===== CONVERT TO ALL COORDINATES SYSTEMS =====
    % Save selected point
    CoordinatesSelector.SCS     = scsLoc;
    CoordinatesSelector.MRI     = cs_convert(sMri, 'scs', 'mri', scsLoc);
    CoordinatesSelector.MNI     = cs_convert(sMri, 'scs', 'mni', scsLoc);
    CoordinatesSelector.World   = cs_convert(sMri, 'scs', 'world', scsLoc);
    CoordinatesSelector.iVertex = iVertex;
    CoordinatesSelector.Value   = Value;
    CoordinatesSelector.hPatch  = hPatch;
    setappdata(hFig, 'CoordinatesSelector', CoordinatesSelector);

    % ===== PLOT MARKER =====
    % Remove previous mark
    delete(findobj(hAxes, '-depth', 1, 'Tag', 'ptCoordinates'));
    % Mark new point
    line(plotLoc(1)*1.005, plotLoc(2)*1.005, plotLoc(3)*1.005, ...
         'MarkerFaceColor', [1 1 0], ...
         'MarkerEdgeColor', [1 1 0], ...
         'Marker',          '+',  ...
         'MarkerSize',      12, ...
         'LineWidth',       2, ...
         'Parent',          hAxes, ...
         'Tag',             'ptCoordinates');
    % Update "Coordinates" panel
    UpdatePanel();
end

%% ===== POINT SELECTION: Surface detection =====
function [TessInfo, iTess, pout, vout, vi, hPatch] = ClickPointInSurface(hFig, SurfacesType)
    % Parse inputs
    if (nargin < 2)
        SurfacesType = [];
    end
    iTess = [];
    pout = {};
    vout = {};
    vi = {};
    hPatch = [];

    % === GET SURFACES INFORMATION ===
    % Get axes handle
    hAxes = findobj(hFig, '-depth', 1, 'Tag', 'Axes3D');
    % Get camera position
    CameraPosition = get(hAxes, 'CameraPosition');
    % Get all the surfaces in the figure
    TessInfo = getappdata(hFig, 'Surface');
    if isempty(TessInfo)
        return
    end

    % === CHECK SURFACE TYPE ===
    % Keep only surfaces that are of the required type
    if ~isempty(SurfacesType)
        iAcceptableTess = find(strcmpi({TessInfo.Name}, SurfacesType));
    else
        iAcceptableTess = 1:length(TessInfo);
    end

    % ===== GET SELECTION ON THE CLOSEST SURFACE =====
    % Get the closest point for all the surfaces and patches
    hPatch = [TessInfo(iAcceptableTess).hPatch];
    hPatch = hPatch(ishandle(hPatch));
    patchDist = zeros(1,length(hPatch));
    for i = 1:length(hPatch)
        [pout{i}, vout{i}, vi{i}] = select3d(hPatch(i));
        if ~isempty(pout{i})
            patchDist(i) = norm(pout{i}' - CameraPosition);
        else
            patchDist(i) = Inf;
        end
    end
    if all(isinf(patchDist))
        TessInfo = [];
        pout = [];
        vout = [];
        vi = [];
        return
    end
    % Find closest surface from the camera
    [minDist, iClosestPatch] = min(patchDist);
    % Keep only the point from the closest surface
    hPatch = hPatch(iClosestPatch);
    pout   = pout{iClosestPatch};
    vout   = vout{iClosestPatch};
    vi     = vi{iClosestPatch};

    % Find to which surface this tesselation belongs
    for i = 1:length(TessInfo)
        if any(TessInfo(i).hPatch == hPatch);
            iTess = i;
            break;
        end
    end
end

%% ===== REMOVE SELECTION =====
function RemoveSelection(varargin)
    % Unselect selection button
    SetSelectionState(0);
    % Find all selected points
    hCoord = findobj(0, 'Tag', 'ptCoordinates');
    % Remove coordinates from the figures
    for i = 1:length(hCoord)
        hFig = get(get(hCoord(i), 'Parent'), 'Parent');
        if ~isempty(hFig) && isappdata(hFig, 'CoordinatesSelector')
            rmappdata(hFig, 'CoordinatesSelector');
        end
    end
    % Delete selected points
    delete(hCoord);
    % Update displayed coordinates
    UpdatePanel();
end

%% ===== VIEW IN MRI VIEWER =====
function ViewInMriViewer(varargin)
    global GlobalData;
    % Get current 3D figure
    [hFig,iFig,iDS] = bst_figures('GetCurrentFigure', '3D');
    if isempty(hFig)
        return
    end
    % Get current selected point
    CoordinatesSelector = getappdata(hFig, 'CoordinatesSelector');
    if isempty(CoordinatesSelector) || isempty(CoordinatesSelector.MRI)
        return
    end
    % Get subject and subject's MRI
    sSubject = bst_get('Subject', GlobalData.DataSet(iDS).SubjectFile);
    if isempty(sSubject) || isempty(sSubject.iAnatomy)
        return
    end
    % Display subject's anatomy in MRI Viewer
    hFig = view_mri(sSubject.Anatomy(sSubject.iAnatomy).FileName);
    % Select the required point
    figure_mri('SetLocation', 'mri', hFig, [], CoordinatesSelector.MRI);
end


function f = uplus(f)
%UPLUS   Unary plus of a TRIGTECH.
%   UPLUS(F) = F.
%
% See also UMINUS, PLUS.

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

% This function does not change anything.

end


function y=remove_CP(x,Ncp,Noff)
% Remove cyclic prefix
if nargin<3
    Noff=0;
end
y=x(:,Ncp+1-Noff:end-Noff);


function generateOutput(dataTree)
global UNIT_TEST

if dataTree.IsEmpty()
    return
end

UNIT_TEST = true; %#ok<NASGU>
iG = dataTree.GetCurrElemIndexID();
banner = sprintf('Re-calculating derived data at %s with the following processing stream:\n\n', char(datetime(datetime, 'Format','HH:mm:ss, MMMM d, yyyy')));
dataTree.PrintProcStream(banner);
dataTree.groups(iG).Calc();
dataTree.groups(iG).Save();
UNIT_TEST = false;


function h=m_gshhs_h(varargin)
% M_GSHHS_H Add a coastline to a given map using the 'high' resolution of
%           the Global Self-consistant Hierarchical High-resolution
%           Shorelines.
%
%         M_GSHHS_H( (standard line option,...,...) ) draws the coastline
%         as a simple line.
%         M_GSHHS_H('patch' ( ,standard patch options,...,...) ) draws the
%         coastline as a number of patches.
%
%         M_GSHHS_H('save',FILENAME) saves the extracted coastline data
%         for the current projection in a file FILENAME. This allows
%         speedier replotting using M_USERCOAST(FILENAME).
%
%         See also M_PROJ, M_GRID, M_COAST, M_GSHHS_L, M_GSHHS_I, M_GSHHS_C
%         M_USERCOAST

% Rich Pawlowicz (rich@ocgy.ubc.ca) 15/June/98
%
%
% This software is provided "as is" without warranty of any kind. But
% it's mine, so you can't sell it.

% Changes
%  Nov/2017 - changed this into a stub calling gshhs.m (kept
%             for backwards compatability)

h=m_gshhs('hc',varargin{:});


function Z = replace(X,Y,W)
%REPLACE Substitutes variables
%
%Z = REPLACE(Y,X,W)  Replaces any occurence of variable Y in X with W

Z = ncvar_replace(X,Y,W);


function [pts] = annotation_load( fname, data_name )

if strcmp(data_name,'helen') == 1
    pts = helen_annotation_load( fname );
elseif strcmp(data_name,'lfpw') == 1
    pts = lfpw_annotation_load( fname );
elseif strcmp(data_name,'w300') == 1
    pts = w300_annotation_load( fname );
else
    disp(['dataset name: ' data_name ' is invalid!']);
end

end


function mri = ft_defacevolume(cfg, mri)

% FT_DEFACEVOLUME allows you to de-identify an anatomical MRI by erasing specific
% regions, such as the face and ears. The interactive graphical user interface allows
% you to position a box over the anatomical data inside which all anatomical voxel
% values will be replaced by zero. You might have to call this function multiple
% times when both face and ears need to be removed. Following defacing, you should
% check the result with FT_SOURCEPLOT.
%
% Use as
%   mri = ft_defacevolume(cfg, mri)
%
% The configuration can contain the following options
%   cfg.method     = 'interactive', 'spm' (default = 'interactive')
%
% If you specify the box method, the following options apply
%   cfg.translate  = initial position of the center of the box (default = [0 0 0])
%   cfg.scale      = initial size of the box along each dimension (default is automatic)
%   cfg.rotate     = initial rotation of the box (default = [0 0 0])
%   cfg.selection  = which voxels to keep, can be 'inside' or 'outside' (default = 'outside')
%   cfg.smooth     = 'no' or the FWHM of the gaussian kernel in voxels (default = 'no')
%   cfg.keepbrain  = 'no' or 'yes', segment and retain the brain (default = 'no')
%   cfg.feedback   = 'no' or 'yes', whether to provide graphical feedback (default = 'no')
%
% If you specify no smoothing, the selected area will be zero-masked. If you
% specify a certain amount of smoothing (in voxels FWHM), the selected area will
% be replaced by a smoothed version of the data.
%
% The spm method does not have any options, it uses SPM_DEFACE from the
% SPM12 toolbox.
%
% See also FT_ANONYMIZEDATA, FT_DEFACEMESH, FT_ANALYSISPIPELINE, FT_SOURCEPLOT

% Copyright (C) 2015-2022, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar    mri
ft_preamble provenance mri

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% for backward compatibility
cfg = ft_checkconfig(cfg, 'renamedval', {'method', 'box', 'interactive'});

% set the defaults
cfg.method         = ft_getopt(cfg, 'method', 'interactive');
cfg.rotate         = ft_getopt(cfg, 'rotate', [0 0 0]);
cfg.scale          = ft_getopt(cfg, 'scale'); % the automatic default is determined further down
cfg.translate      = ft_getopt(cfg, 'translate', [0 0 0]);
cfg.transformorder = ft_getopt(cfg, 'transformorder', {'scale', 'rotate', 'translate'}); % T*R*S
cfg.selection      = ft_getopt(cfg, 'selection', 'outside');
cfg.smooth         = ft_getopt(cfg, 'smooth', 'no');
cfg.keepbrain      = ft_getopt(cfg, 'keepbrain', 'no');
cfg.feedback       = ft_getopt(cfg, 'feedback', 'no');

ismri  = ft_datatype(mri, 'volume') && isfield(mri, 'anatomy');
ismesh = isfield(mri, 'pos'); % triangles are optional

if ismri
  % check if the input data is valid for this function
  mri = ft_checkdata(mri, 'datatype', 'volume', 'feedback', 'yes');
end

switch cfg.method
  case 'spm'
    % this requires SPM12 on the path
    ft_hastoolbox('spm12', 1);

    % defacing relies on coregistration, which relies on the MRI being reasonably aligned for SPM
    mri = ft_checkdata(mri, 'hascoordsys', 'yes');

    % remember the original transformation matrix and coordinate system
    original = [];
    original.transform = mri.transform;
    original.coordsys  = mri.coordsys;
    mri = ft_convert_coordsys(mri, 'acpc');

    filename1 = {[tempname '.nii']};
    ft_write_mri(filename1{1}, mri, 'dataformat', 'nifti');

    % % apply a least squares pre-alignment step in order to make spm_deface more robust
    % % this could be done conditional on the modality/contrast, which is part of the BIDS filename
    % template = spm_vol(fullfile(spm('Dir'),'canonical','avg152PD.nii'));
    % template = spm_vol(fullfile(spm('Dir'),'canonical','avg152T1.nii'));
    % template = spm_vol(fullfile(spm('Dir'),'canonical','avg152T2.nii'));
    % filevol = spm_vol(filename1{1});
    % M = spm_affreg(template, filevol);
    % spm_get_space(filename1{1}, M * filevol.mat);

    filename2 = spm_deface(filename1);
    mri = ft_read_mri(filename2{1});

    % put the original transformation matrix and coordinate system back
    mri.transform = original.transform;
    mri.coordsys = original.coordsys;

    % clean up the temporary files
    delete(filename1{1});
    delete(filename2{1});

  case 'interactive'
    % this is an alternative implementation of the interactive method usinf FT_INTERACTIVEREALIGN
    % it aligns a box to the MRI or mesh, and then removes the points inside that box

    if ismri
      % enhance the contrast of the volumetric data, see also FT_VOLUMEREALIGN
      dat  = double(mri.anatomy);
      dum  = unique(dat(:));
      dmin = dum(round(0.05*numel(dum))); % take the 5% value of the histogram
      dmax = dum(round(0.95*numel(dum))); % take the 95% value of the histogram
      dat  = (dat-dmin)./(dmax-dmin);
      mri.anatomy = dat;
    end

    % construct a box with a unit length expressed in the units of the input mri or mesh
    % rather than using a triangulation, this specifies polygons for each of the 6 edges of the box
    box.unit = mri.unit;
    box.pos = [
       1  1  1
       1 -1  1
      -1 -1  1
      -1  1  1
       1  1 -1
       1 -1 -1
      -1 -1 -1
      -1  1 -1
      ]/2;
    box.poly = [
      1 2 3 4
      1 5 6 2
      2 6 7 3
      3 7 8 4
      4 8 5 1
      5 8 7 6
      ];

    % the default is to scale the box to 75 mm (or equivalent)
    defaultscale = [75 75 75] * ft_scalingfactor('mm', mri.unit);
    surfaceonly = isfield(mri, 'tet') | isfield(mri, 'hex'); % only for tetrahedral or hexahedral meshes

    tmpcfg = keepfields(cfg, {'scale', 'rotate', 'translate', 'transformorder'});
    tmpcfg.scale = ft_getopt(cfg, 'scale', defaultscale);
    tmpcfg.showlight = 'no';
    tmpcfg.showalpha = 'no'; % do not use a global alpha level
    tmpcfg.showapply = 'no'; % do not show the apply button
    tmpcfg.template.axes = 'yes';
    if ismri
      tmpcfg.template.mri = mri;
    elseif ismesh
      tmpcfg.template.mesh = mri;
    end
    tmpcfg.individual.mesh = box;
    tmpcfg.individual.meshstyle = {'edgecolor', 'k', 'facecolor', 'y', 'facealpha', 0.3, 'surfaceonly', surfaceonly};
    tmpcfg = ft_interactiverealign(tmpcfg);

    % remember these for potential reuse outside of this function
    cfg.rotate    = tmpcfg.rotate;
    cfg.scale     = tmpcfg.scale;
    cfg.translate = tmpcfg.translate;

    % the template remains fixed, the individual is moved around
    R = rotate   (cfg.rotate);
    T = translate(cfg.translate);
    S = scale    (cfg.scale);
    % this is the transformation to get from the individual to the template
    transform = combine_transform(R, S, T, cfg.transformorder);

    if ismri
      % rather than converting the box to the MRI, do it the other way around
      [X, Y, Z] = ndgrid(1:mri.dim(1), 1:mri.dim(2), 1:mri.dim(3));
      voxpos = ft_warp_apply(mri.transform, [X(:) Y(:) Z(:)]);  % voxel positions in head coordinates
      voxpos = ft_warp_apply(inv(transform), voxpos);           % voxel positions in box coordinates

      remove = ...
        voxpos(:,1) > -0.5 & ...
        voxpos(:,1) < +0.5 & ...
        voxpos(:,2) > -0.5 & ...
        voxpos(:,2) < +0.5 & ...
        voxpos(:,3) > -0.5 & ...
        voxpos(:,3) < +0.5;

    elseif ismesh || issource
      % rather than converting the box to the mesh, do it the other way around
      meshpos = ft_warp_apply(inv(transform), mri.pos);         % vertex positions in box coordinates

      remove = ...
        meshpos(:,1) > -0.5 & ...
        meshpos(:,1) < +0.5 & ...
        meshpos(:,2) > -0.5 & ...
        meshpos(:,2) < +0.5 & ...
        meshpos(:,3) > -0.5 & ...
        meshpos(:,3) < +0.5;
    end

    if strcmp(cfg.selection, 'inside')
      % invert the selection, i.e., keep the voxels inside the box
      remove = ~remove;
    end

    if ismri
      if istrue(cfg.keepbrain)
        tmpcfg = [];
        tmpcfg.output = {'brain'};
        seg = ft_volumesegment(tmpcfg, mri);
        fprintf('keeping voxels in brain segmentation\n');
        % keep the tissue of the brain
        remove(seg.brain) = 0;
        clear seg
      end

      if istrue(cfg.feedback)
        tmpmri = keepfields(mri, {'anatomy', 'transform', 'coordsys', 'units', 'dim'});
        tmpmri.remove = remove;
        tmpcfg = [];
        tmpcfg.funparameter = 'remove';
        ft_sourceplot(tmpcfg, tmpmri);
      end

      if isequal(cfg.smooth, 'no')
        fprintf('zero-filling %.0f%% of the volume\n', 100*mean(remove));
        mri.anatomy(remove) = 0;
      else
        tmp = mri.anatomy;
        tmp = (1 + 0.5.*randn(size(tmp))).*tmp; % add 50% noise to each voxel
        tmp = volumesmooth(tmp, cfg.smooth, 'anatomy');
        fprintf('smoothing %.0f%% of the volume\n', 100*mean(remove));
        mri.anatomy(remove) = tmp(remove);
      end

    elseif ismesh
      % determine all fields that might need to be defaced
      fn = setdiff(fieldnames(mri), ignorefields('deface'));
      dimord = cell(size(fn));
      for i=1:numel(fn)
        dimord{i} = getdimord(mri, fn{i});
      end
      % this applies to headshapes and meshes in general
      fprintf('keeping %d and removing %d vertices in the mesh\n', sum(remove==0), sum(remove==1));
      if isfield(mri, 'tri')
        [mri.pos, mri.tri] = remove_vertices(mri.pos, mri.tri, remove);
      elseif isfield(mri, 'tet')
        [mri.pos, mri.tet] = remove_vertices(mri.pos, mri.tet, remove);
      elseif isfield(mri, 'hex')
        [mri.pos, mri.hex] = remove_vertices(mri.pos, mri.hex, remove);
      else
        mri.pos = mri.pos(~remove,1:3);
      end
      for i=1:numel(fn)
        dimtok = tokenize(dimord{i}, '_');
        % do some sanity checks
        if any(strcmp(dimtok, '{pos}'))
          ft_error('not supported');
        end
        if numel(dimtok)>5
          ft_error('too many dimensions');
        end
        % remove the same positions from each matching dimension
        if numel(dimtok)>0 && strcmp(dimtok{1}, 'pos')
          mri.(fn{i}) = mri.(fn{i})(~remove,:,:,:,:);
        end
        if numel(dimtok)>1 && strcmp(dimtok{2}, 'pos')
          mri.(fn{i}) = mri.(fn{i})(:,~remove,:,:,:);
        end
        if numel(dimtok)>2 && strcmp(dimtok{3}, 'pos')
          mri.(fn{i}) = mri.(fn{i})(:,:,~remove,:,:);
        end
        if numel(dimtok)>3 && strcmp(dimtok{4}, 'pos')
          mri.(fn{i}) = mri.(fn{i})(:,:,:,~remove,:);
        end
        if numel(dimtok)>4 && strcmp(dimtok{5}, 'pos')
          mri.(fn{i}) = mri.(fn{i})(:,:,:,:,~remove);
        end
      end % for fn
      mri = removefields(mri, {'dim', 'transform'}); % these fields don't apply any more
    end % ismesh

  otherwise
    ft_error('unsupported method');
end % switch method

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble previous mri
ft_postamble provenance mri
ft_postamble history mri
ft_postamble savevar mri


function mtrScoreNonConTrackPaths(subjectDir, fgFile, roi1File, roi2File, smoothParam, shapeParam, lengthParam, optionsFile, fgOutFile)

% mtrScoreNonConTrackPaths(subjectDir, fgFile, roi1File, roi2File, smoothParam, shapeParam, lengthParam, optionsFile, fgOutFile)
%
% 06/10/2007: AJS created it.
%
% This function takes STT fibers (fgFile), takes only those that pass
% through two desired ROIs (roi1File, roi2File), clips them, then writes
% the clipped fibers and their ConTrack scores to file (fgOutFile).
%
% It requires certain parameters that Tony will hopefully document here
% soon (smoothParam, lengthParam, optionsFile).

%% Setup the options file
% Prepare the roi filename by concatenating the ROI names
[pathstr, file1, ext, versn] = fileparts(roi1File); %#ok<NASGU>
[pathstr, file2, ext, versn] = fileparts(roi2File); %#ok<NASGU>
maskFile = fullfile(pathstr,['mask_' file1 '_' file2 '.nii.gz']);

mtrCreateConTrackOptionsFromROIs(subjectDir, roi1File, roi2File, optionsFile, optionsFile, 'junk', maskFile);

%% Update options file with user parameters
dt6 = load(fullfile(subjectDir,'dt6.mat'),'xformToAcPc');
mtr = mtrLoad(fullfile(subjectDir,'conTrack',optionsFile),dt6.xformToAcPc);
mtr = mtrSet(mtr, 'smooth_std', smoothParam);
mtr = mtrSet(mtr, 'abs_normal', lengthParam);
mtr = mtrSet(mtr, 'shape_params_vec', [shapeParam 0.15 100]); %#ok<NASGU>

%% Clip the pathways to the supplied ROIs
mtrClipFiberGroupToROIs( fullfile(subjectDir,'fibers',fgFile), ...
                         fullfile(subjectDir,'dt6.mat'), ...
                         fullfile(subjectDir,'ROIs',roi1File), ...
                         fullfile(subjectDir,'ROIs',roi2File), ...
                         fullfile(subjectDir,'bin','wmMask.nii.gz'), ...
                         fullfile(subjectDir,'fibers',fgOutFile) );

%% Export to format for the executable
[pathstr, file, ext, versn] = fileparts(fgOutFile); %#ok<NASGU>
fgOutFileDat = fullfile(pathstr,[file '.dat']);
mtrExportFiberGroupToMetrotrac( fullfile(subjectDir,'fibers',fgOutFileDat), ...
                                fullfile(subjectDir,'fibers',fgOutFile), ...
                                fullfile(subjectDir,'bin','fa.nii.gz') );

%% Check environment we are running in
if(ispc)
    executable = which('resample_pdb.exe');
elseif(strcmp(computer,'GLNXA64'))
    executable = which('resample_pdb.glxa64');
elseif (strcmp(computer,'GLNX86'))
    executable = which('resample_pdb.glx');
else
    error('Not compiled for %s.',computer);
end

%% Run the executable
workDir = fullfile(subjectDir,'conTrack');
args = sprintf(' -i %s -o tempScoreNonCTRK  -lpn %d %s', optionsFile, intmax, fullfile(subjectDir,'fibers',fgOutFileDat));
cmd = [executable args];
disp(cmd); disp('...')
if(ispc)
    mtrExecuteWinBash(cmd,workDir);
else
    system(cmd,'-echo');
end
disp('Done')

if( exist(fullfile(subjectDir,'fibers',fgOutFileDat),'file') )
    delete(fullfile(subjectDir,'fibers',fgOutFileDat));
end
tempPaths = dir(fullfile(subjectDir,'conTrack','tempScoreNonCTRK*.dat'));
if(size(tempPaths,1)>1)
    fileList = tempPaths(1).name;
    for ii = 2:size(tempPaths,1); fileList = [fileList ', ' tempPaths(ii).name]; end
    error(['Special temp name already used for intermediate path creation please remove the following files ' fileList]);
end
movefile(fullfile(subjectDir,'conTrack',tempPaths(1).name),fullfile(subjectDir,'fibers',fgOutFileDat),'f');
disp(['Final pathway file has been written to ' fullfile(subjectDir,'fibers',fgOutFileDat)]);

return;


function M = dunzip(Z)
% DUNZIP - decompress gzipped stream of bytes
% FORMAT M = dzip(Z)
% Z  -  compressed variable to decompress (uint8 vector)
% M  -  decompressed output
%
% See also DZIP

% Carefully tested, but no warranty; use at your own risk.
% Michael Kleder, Nov 2005
% Modified by Guillaume Flandin, May 2008

import com.mathworks.mlwidgets.io.InterruptibleStreamCopier
a   = java.io.ByteArrayInputStream(Z);
b   = java.util.zip.InflaterInputStream(a);
isc = InterruptibleStreamCopier.getInterruptibleStreamCopier;
c   = java.io.ByteArrayOutputStream;
isc.copyStream(b,c);
M   = c.toByteArray;


function [newMap] = changeRxnColorAndWidth(map, rxnList, color, areaWidth)
% Change color and areaWidth of reactions from a list of names
%
% USAGE:
%
%   [newMap] = changeRxnColorAndareaWidth(map, rxnList, color, areaWidth);
%
% INPUTS:
%   map:            File from CellDesigner parsed to MATLAB format
%   rxnList:        List of reactions
%
% OPTIONAL INPUTS:
%   color:          New color of reactions from list (default: 'RED')
%   areaWidth:      New areaWidth of reactions from list (default: 8)
%
% OUTPUT:
%   newMap:         Matlab structure of map with reaction modifications
%
% .. Authors:
%       - A.Danielsdottir 17/07/2017 LCSB. Belval. Luxembourg
%       - N.Sompairac - Institut Curie, Paris, 17/07/2017.

    if nargin < 4
        areaWidth = 8;
    end
    if nargin < 3
        color = 'RED';
    end

    newMap = map;
    colors = createColorsMap;

    index = find(ismember(newMap.rxnName, rxnList));
    for j = index'
        newMap.rxnColor{j, 1} = colors(color);
        newMap.rxnWidth{j, 1} = areaWidth;

        % Use the existence of reactant lines to check if the newMap has the
        % complete structure.
        if any(strcmp('rxnReactantLineColor', fieldnames(newMap))) == 1
            if ~isempty(newMap.rxnReactantLineColor{j})
                for k = 1:length(newMap.rxnReactantLineColor{j})
                    newMap.rxnReactantLineColor{j, 1}{k, 1} = colors(color);
                    newMap.rxnReactantLineWidth{j, 1}{k, 1} = areaWidth;
                end
            end
            if ~isempty(newMap.rxnProductLineColor{j})
                for m = 1:1:length(newMap.rxnProductLineColor{j})
                    newMap.rxnProductLineColor{j, 1}{m, 1} = colors(color);
                    newMap.rxnProductLineWidth{j, 1}{m, 1} = areaWidth;
                end
            end
        end
    end

end


function optS = setOptsExe(opt_str,optS)
%function optS = setOptsExe(opt_str,optS)
%Parse a command that sets the field of a structure, and return the
%structure.
%LM:  J.O.Deasy, 3 Sept, 2002.
%
%copyright (c) 2001, J.O. Deasy and Washington University in St. Louis.
%Use is granted for non-commercial and non-clinical applications.
%No warranty is expressed or implied for any use whatever.
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

if nargin == 1
  optS = [];
end

opt_str = deblank2(opt_str);
if ~isempty(strfind(opt_str,'=')) && ~strcmp(opt_str(1),'%')
  if ~strcmp(word(opt_str,1),'function') %skip function line if present
   %Remove any comments:
   ind1 = strfind('%',opt_str);
   if ~isempty(ind1)
     if ind1 == 1
       return
     end
     opt_str = opt_str(1:ind1-1);
   end
   %remove trailing ';'
   ind1 = strfind(';',opt_str);
   if ~isempty(ind1)
     opt_str = opt_str(1:ind1-1);
   end
   %parse the command
   %num = words(opt_str);
   %Get the first wor
   firstW = word(opt_str,1);
   %Find the period:
   ind2 = strfind('.',firstW);
   leaf = firstW(ind2+1:length(firstW));
   %Get setting: everything past the '=' sign
   ind3 = strfind(opt_str,'=');
   value = opt_str(ind3+1:length(opt_str));
   value = deblank2(value);
   %Convert numerical assignments to numbers:

   %catch cell arrays
   if strcmp(value(1),'{')
     %build cell array of strings
     iV = strfind(value,'''');
     value2 = {};
     for i = 1 : length(iV)/2
       j = i * 2 - 1;
       str = value(iV(j)+1:iV(j+1)-1);
       value2 = {value2{:},str};
     end
     value = value2;
   else
     if ~isempty(str2num(value))
       value = str2num(value);
     else  %remove apostrophes
       iV = strfind(value,'''');
       if ~isempty(iV)
         value(iV) = [];
       end
     end
   end

   %Finally, make the assignment:
   optS = setfield(optS, leaf, value);
  end
end


function list = translateList(list, trList1, trList2)
% Translate a list of identifiers (either numerical or cell array) using a dictionary
%
% USAGE:
%
%     list = translateList(list, trList1, trList2)
%
% INPUTS:
%    list:       original list
%    trList1:    list of elemets to be changed
%    trList2:    list of elements to be changed into
%
% OUTPUTS:
%    list:       list after changes
%
% EXAMPLE:
%
%    % Define original list
%    list = {'a', 'b', 'c'}
%
%    % Define dictionary
%    trList1 = {'b', 'c'}
%    trList2 = {'B', 'C'}
%
%    newList = translateList(list, trList1, trList2);
%
%    % returns
%    newList = {'a', 'B', 'C'};

[isInList, listInd] = ismember(list, trList1);

list(isInList) = trList2(listInd(listInd ~= 0));


function imdb = getCliveDatabase_train(Dir)

imdb.imgpath = cell(1,1162);
imgpath = fullfile(Dir,'Data','AllImages_release.mat');
img = load(imgpath);
img = img.AllImages_release;

mospath = fullfile(Dir,'Data','AllMOS_release.mat');
mos = load(mospath);
mos = mos.AllMOS_release;
imdb.mos = mos(8:end);

for i = 8:1169
    file_name = img{i,1};
    imdb.imgpath{i-7} = fullfile('Images',file_name);
end

sel = randperm(1162);
train_sel = sel(1:round(0.8*1162));
test_sel = sel(round(0.8*1162)+1:end);

train_path = imdb.imgpath(train_sel);
test_path = imdb.imgpath(test_sel);

train_mos = imdb.mos(train_sel);
test_mos = imdb.mos(test_sel);

imdb.images.id = 1:1162 ;
imdb.images.set = [ones(1,size(train_path,2)),2*ones(1,size(test_path,2))];
imdb.images.label = [train_mos,test_mos];

imdb.classes.description = {'LIVE_CHAN'};
imdb.images.name = [train_path,test_path] ;
imdb.imageDir = Dir ;
% fclose(fileID);


function riverplot(layer1fmri_obj, varargin)
% Make a riverplot of relationships among images, stored in an fmri_data object,
% or two sets of images, stored in two fmri_data objects.
%
% - With one input, uses self
% - With two inputs, uses first as Layer 1 and second as Layer 2 of riverplot
% - Uses obj.image_names for titles of images, so set these before calling this function
%
% :Usage:
% ::
%
%     [list outputs here] = function_name(list inputs here, [optional inputs])
%
% For objects: Type methods(object_name) for a list of special commands
%              Type help object_name.method_name for help on specific
%              methods.
%
% ..
%     Author and copyright information:
%
%     Copyright (C) 2016 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:
%
%   **layer1fmri_obj:**
%        An fmri_data object.  Default is to plot associations between
%        images in this object.
%
% :Optional Inputs:
%   **layer2:**
%        A second fmri_data object.  This is layer 2 of the plot.  Plots
%        assocations between images in layer1 object and layer2 object
%
%   **r:**
%        Change similarity metric to correlation.  Default is cosine
%        similarity.
%
%   **dice:**
%        Change similarity metric to dice coefficient.  Default is cosine
%        similarity.
%
%   **pos:**
%        Change similarity matrix thresholding to positive values only.
%        Default is to use original similarity matrix.
%
%   **neg:**
%        Change similarity matrix thresholding to negative values only.
%        Default is to use original similarity matrix.
%
%   **abs:**
%        Change similarity matrix thresholding to absolute values.
%        Default is to use original similarity matrix.
%
%   **reorder:**
%        Automatic reordering of rows of plot to try to improve visibility.
%
%   **recolor:**
%        Automatic color assignment of layer2 colors based on layer1
%        weights
%
%   **layer1order:**
%        Followed by custom order for Layer 1, specified by vector of
%        integers, e.g., [1 3 2 4 5 6]
%
%   **layer2order:**
%        Followed by custom order for Layer 2, specified by vector of
%        integers, e.g., [1 3 2 4 5 6]
%
%   **colors1:**
%        Followed by cell vector of [r g b] colors for each image in Layer 1
%
%   **colors2:**
%        Followed by cell vector of [r g b] colors for each image in Layer 2
%
%   **thin:**
%        Turn off thick/full-rectangle ribbon width coming from Layer 1
%
%
% :Outputs:
%
%   **layer1:**
%        Vector of structures describing rectangles in Layer 1, with
%        handles, positions, etc.
%
%   **layer2:**
%        Vector of structures describing rectangles in Layer 2, with
%        handles, positions, etc.
%
%   **ribbons:**
%        Vector of structures describing ribbons, with
%        handles, positions, etc.
%
%   **layer2colors:**
%        cell array specifying layer2 colors if recolor option is used
%
%
% :Examples:
% ::
%
% layer1fmri_obj = fmri_data(placeboimgs);
% layer2fmri_obj = fmri_data(appraisalimgs);
% riverplot(layer1fmri_obj, 'layer2', layer2fmri_obj);
%
% Add colors and names:
% layer1colors = {[.8 .8 .3] [.3 .3 1]};
% layer2colors = {[0 .2 .8] [1 1 .2] [1 .4 .4] [0 .6 0] [1 .7 0]};  %seaborn_colors(n1);
%
% layer1names = {'Placebo increases', 'Placebo decreases'};  %stats.networknames;
% layer2names = {'Neg Emo' 'Pos Emo' 'Emo Reg' 'Social Cog' 'Self'};
%
% layer1fmri_obj.image_names = char(layer1names{:});
% layer2fmri_obj.image_names = char(layer2names{:});
%
% riverplot(layer1fmri_obj, 'layer2', layer2fmri_obj, 'layer1colors', layer1colors, 'layer2colors', layer2colors);
%
% Example: Plot Buckner lab network maps with themselves:
% [bucknermaps, networknames] = load_image_set('bucknerlab');
% bucknermaps.image_names = char(networknames{:});
%
% layer1colors = bucknerlab_colors(7);
% riverplot(bucknermaps, 'pos', 'reorder', 'layer1colors', layer1colors, 'layer2colors', layer1colors);
%
% riverplot(bucknermaps, 'pos', 'layer1colors', layer1colors, 'layer2colors', layer1colors);
%
%
% :References:
%   Based off of concept in River Plot R package.
%
% :See also:
%   - image_similarity_plot, tor_polar_plot
%

% ..
%    Programmers' notes:
%    List dates and changes here, and author of changes
%    Created July 2016 by Tor Wager
% ..

% BELOW IS A STANDARD TEMPLATE FOR DEFINING VARIABLE (OPTIONAL) INPUT
% ARGUMENTS. MANY FUNCTIONS NEED TO PARSE OPTIONAL ARGS, SO THIS MAY BE
% USEFUL.

% ..
%    DEFAULTS AND INPUTS
% ..

% optional inputs with default values

layer2fmri_obj = layer1fmri_obj;  % Default: Plot object with itself
sim_metric = 'cosine_sim';
threshold = 'none';

nlayer1 = size(layer1fmri_obj.dat, 2);
nlayer2 = size(layer2fmri_obj.dat, 2);

layer1colors = custom_colors([.2 .8 .2], [.8 .8 .2], nlayer1);
layer2colors = custom_colors([.2 .2 .8], [.2 .8 .8], nlayer2);

doreorder = 0;
recolor = 0;

layer1coveragestr = 'layer1fullcoverage';

layer1order = [];

layer2order = [];

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

            case {'layer2', 'layer2fmri_obj'},
                layer2fmri_obj = varargin{i+1}; varargin{i+1} = [];
                nlayer2 = size(layer2fmri_obj.dat, 2);
                layer2colors = custom_colors([.2 .2 .8], [.2 .8 .8], nlayer2);

            case 'sim_metric', sim_metric = varargin{i+1}; varargin{i+1} = [];
            case 'threshold', threshold = varargin{i+1}; varargin{i+1} = [];

            case {'colors1', 'layer1colors'}, layer1colors = varargin{i+1}; varargin{i+1} = [];
            case {'colors2', 'layer2colors'}, layer2colors = varargin{i+1}; varargin{i+1} = [];

            case 'reorder', doreorder = 1;
              case 'recolor', recolor = 1;

            case {'r', 'corr', 'correlation'}
                sim_metric = 'r';

            case 'dice'
                sim_metric = 'dice';

            case 'pos'
                threshold = 'pos';

            case 'neg'
                threshold = 'neg';

            case 'abs'
                threshold = 'abs';

            case 'thin'
                layer1coveragestr = ' ';

            case 'layer1order', layer1order = varargin{i+1}; varargin{i+1} = [];
            case 'layer2order', layer2order = varargin{i+1}; varargin{i+1} = [];

            otherwise, warning(['Unknown input string option:' varargin{i}]);
        end
    end
end

% -------------------------------------------------------------------------
% Get similarity matrix
% -------------------------------------------------------------------------

switch sim_metric

    case 'cosine_sim' % default

        stats = image_similarity_plot(layer1fmri_obj, 'mapset', layer2fmri_obj, 'noplot', 'cosine_similarity');
        sim_matrix = stats.r;

    case {'r', 'corr', 'correlation'}

        stats = image_similarity_plot(layer1fmri_obj, 'mapset', layer2fmri_obj, 'noplot');
        sim_matrix = stats.r;

    case 'dice'

        dice_coeff = dice_coeff_image(cat(layer1fmri_obj, layer2fmri_obj));

        sim_matrix = dice_coeff(1:nlayer1, nlayer1+1:end)';

    otherwise
        error('Unknown sim_metric. Choose r, cosine_sim, or dice');
end

% -------------------------------------------------------------------------
% Threshold
% -------------------------------------------------------------------------

switch threshold

    case 'none'
        % do nothing - default

    case {'pos', 'positive'}

        % Threshold to look at positive associations
        sim_matrix(sim_matrix < 0) = 0;

    case {'neg', 'negative'}
        sim_matrix = -sim_matrix;
        sim_matrix(sim_matrix < 0) = 0;

    case 'abs'
        sim_matrix = abs(sim_matrix);

    otherwise
        error('Unknown threshold type. Choose pos, neg, or abs');
end

% -------------------------------------------------------------------------
% Names and sizes
% -------------------------------------------------------------------------

layer1names = format_strings_for_legend(layer1fmri_obj.image_names);
layer2names = format_strings_for_legend(layer2fmri_obj.image_names);

[n2, n1] = size(sim_matrix);

if length(layer1names) < n1, error('Layer 1: Not enough names entered. Add names in input layer1 fmri_object.image_names'); end
if length(layer2names) < n2, error('Layer 2: Not enough names entered. Add names in input layer2 fmri_object.image_names'); end

% -------------------------------------------------------------------------
% Reorder rows and columns if asked for
% -------------------------------------------------------------------------

if doreorder

    %  REORDER rows here based on similarity to make plot look cleaner
    [sim_matrix, layer2names, layer2colors] = riverplot_reorder_matrix(sim_matrix, layer2names, layer2colors);

end

if ~isempty(layer1order)

    sim_matrix = sim_matrix(:, layer1order);
    layer1names = layer1names(layer1order);
    layer1colors = layer1colors(layer1order);

end

if ~isempty(layer2order)

    sim_matrix = sim_matrix(layer2order, :);
    layer2names = layer2names(layer2order);
    layer2colors = layer2colors(layer2order);

end

if recolor
    layer2colors=riverplot_recolor_layer2(sim_matrix,layer1colors);
end

% -------------------------------------------------------------------------
% Find start coordinate points for layers
% -------------------------------------------------------------------------

layer2x = max(n1, n2) ./ 2;  % 1/2 largest array size, to keep x proportions balanced with y

% each rect is 1 unit, starting at y = 0 for layer. so if same size, no y_loc offset
% midpoint should be same for both
mid2 = (n2 + 1) ./ 2;
% mid1 should equal mid2  : % (n1 + 1) ./ 2 - y_loc = mid 2
y_loc = -(n1 + 1) ./ 2 + mid2;

% -------------------------------------------------------------------------
% Create the plot
% -------------------------------------------------------------------------

create_figure('riverplot');
set(gca, 'YDir', 'reverse');
layer1 = riverplot_draw_layer(0, n1, 'colors', layer1colors, 'y_loc', y_loc);
layer2 = riverplot_draw_layer(layer2x, n2, 'colors', layer2colors);

ribbons = riverplot_ribbon_matrix(layer1, layer2, sim_matrix, 'colors', layer1colors, layer1coveragestr, 'steepness', 0);

% Turn off lines
riverplot_toggle_lines(ribbons);

% Increase opacity
riverplot_set_ribbon_property(ribbons, 'FaceAlpha', .6);

set(gca, 'XLim', [-3 layer2x+3]);

% Add names
layer1 = riverplot_layer_names(layer1, layer1names);
layer2 = riverplot_layer_names(layer2, layer2names, 'right');

axis off

end % function