function varargout = process_ft_channelrepair( varargin )
% PROCESS_FT_CHANNELREPAIR: Call FieldTrip function ft_channelrepair.
% Replace bad channels with interpolations of neighboring values.

% @=============================================================================
% 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: Roey Schurr, Francois Tadel, 2015

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'FieldTrip: ft_channelrepair';
    sProcess.Category    = 'File';
    sProcess.SubGroup    = 'Standardize';
    sProcess.Index       = 309;
    sProcess.Description = 'http://www.fieldtriptoolbox.org/reference/ft_channelrepair';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data'};
    sProcess.OutputTypes = {'data'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;

    % Definition of the options
    % === INTEPROLATION METHOD
    sProcess.options.warning.Comment = ['Note that you cannot indicate the bad channels here,
' ...
                                        'you need to mark them from the interface before.

' ...
                                        'Interpolation method:'];
    sProcess.options.warning.Type    = 'label';
    sProcess.options.method.Comment = {'Nearest: Neighbours weighted by distance', 'Average: Mean of all neighbours', 'Spline: Spherical spline', 'Slap: Surface Laplacian'};
    sProcess.options.method.Type    = 'radio';
    sProcess.options.method.Value   = 1;
    % === MAXIMAL DISTANCE BETWEEN NEIGHBOURS
    sProcess.options.maxdist.Comment = 'Maximal distance between neighbours: ';
    sProcess.options.maxdist.Type    = 'value';
    sProcess.options.maxdist.Value   = {4, 'cm', 1};
    % === SENSOR TYPES
    sProcess.options.sensortypes.Comment = 'Sensor types (empty=all): ';
    sProcess.options.sensortypes.Type    = 'text';
    sProcess.options.sensortypes.Value   = 'EEG';
end

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

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInput) %#ok<DEFNU>
    % Initialize returned list of files
    OutputFiles = {};
    % Initialize FieldTrip
    [isInstalled, errMsg] = bst_plugin('Install', 'fieldtrip');
    if ~isInstalled
        bst_report('Error', sProcess, [], errMsg);
        return;
    end
    bst_plugin('SetProgressLogo', 'fieldtrip');
    % Get option values
    MaxDist = sProcess.options.maxdist.Value{1} / 100;   % Convert from centimeters to meters
    SensorTypes = sProcess.options.sensortypes.Value;
    switch (sProcess.options.method.Value)
        case 1,    Method  = 'nearest';   % replacs the electrode with the average of its neighbours weighted by distance
        case 2,    Method  = 'average';
        case 3,    Method  = 'spline';
        case 4,    Method  = 'slap';
        otherwise, error('Invalid method');
    end

    % ===== LOAD DATA =====
    % Convert to FieldTrip structures
    [ftData, DataMat, ChannelMat] = out_fieldtrip_data(sInput.FileName, sInput.ChannelFile, SensorTypes, 0);

    % ===== FIND NEIGHBORS =====
    % Prepare structure of neighbouring electrodes
    neicfg = struct();
    neicfg.method        = 'distance';
    neicfg.neighbourdist = MaxDist;
    if isfield(ftData, 'elec')
        neicfg.elec = ftData.elec;
    end
    if isfield(ftData, 'grad')
        neicfg.grad = ftData.grad;
    end
    neighbours = ft_prepare_neighbours(neicfg);

    % ===== INTERPOLATE CHANNELS =====
    % Find bad channels
    iBadChan = find(DataMat.ChannelFlag == -1);
    badchannel = {ChannelMat.Channel(iBadChan).Name};
    % Preprare structure
    intcfg = struct();
    intcfg.neighbours = neighbours;
    intcfg.method     = Method;
    intcfg.badchannel = badchannel';
    intcfg.trials     = 1;
    interpolatedData = ft_channelrepair(intcfg, ftData);

    % ===== GET RESULTS =====
    % Get indices of the channels that were updated
    [tmp,I,J] = intersect({ChannelMat.Channel(iBadChan).Name}, ftData.label);
    % Replace interpolated channels
    DataMat.F(iBadChan(I),:) = interpolatedData.trial{1}(J,:);
    % Set those channels as good
    DataMat.ChannelFlag(iBadChan(I),:) = 1;
    % Add history comment
    DataMat = bst_history('add', DataMat, 'interpbad', ['Replaced bad channels with method "' Method '" (' num2str(MaxDist*100) 'cm): ' sprintf('%s ', intcfg.badchannel{:})]);
    if isfield(interpolatedData, 'cfg') && isfield(interpolatedData.cfg, 'version')
        DataMat = bst_history('add', DataMat, 'fieldtrip', interpolatedData.cfg.version.name);
        DataMat = bst_history('add', DataMat, 'fieldtrip', interpolatedData.cfg.version.id);
    end
    % Add comment tag
    DataMat.Comment = [DataMat.Comment ' | interpbad'];

    % ===== SAVE THE RESULTS =====
    % Create output filename
    [fPath, fBase, fExt] = bst_fileparts(file_fullpath(sInput.FileName));
    OutputFiles{1} = file_unique(bst_fullfile(fPath, [fBase '_interpbad', fExt]));
    % Save on disk
    bst_save(OutputFiles{1}, DataMat, 'v6');
    % Register in database
    db_add_data(sInput.iStudy, OutputFiles{1}, DataMat);
    % Remove logo
    bst_plugin('SetProgressLogo', []);
end


function [A_zV, a_zV, B_zV, b_zV] = GetPBConsts(DepthsV, EnergyC, PBDataS, flag);

%       - constantsAaBb(1) - depth
%       - constantsAaBb(2) - A
%       - constantsAaBb(3) - a
%       - constantsAaBb(4) - B
%       - constantsAaBb(5) - b
%
%     Example
%     using Ahnesjoe's notation
%     0.075  2.0958E-02  22.777100  1.8582E-05  0.058308
%     0.225  2.4948E-02  12.957500  2.1138E-05  0.067823
%
%LM:  JOD, 3 Nov 03, allow nearest neighbor indexing.
%JOD, 17 Nov 03, added scale factor, default at 100.
%
% 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/>.

scale = 100;
%For convenience, scale up A and B coefficients, so dose values are closer to unity.

if nargin == 4 & strcmp(flag,'nearest')

  Aahn6=PBDataS.aahn6b;
  Aahn18=flipud(PBDataS.aahn18b);

  MaxDepth=50;

  if EnergyC==6
   ParamMat=Aahn6;

  else
   if EnergyC==18
    ParamMat=Aahn18;
   end
  end

  DepthTableV=ParamMat(:,1);

  %our own linear interpolation (faster than Matlab).
  %compute two interpolation indices:
  if any([DepthsV>MaxDepth])
       disp('Warning!  Some depths exceed 50 cm, which is dosimetry limit!')
  end

  DepthsV=DepthsV.*[DepthsV<=MaxDepth]+MaxDepth.*[DepthsV>MaxDepth];

  IndexLow=floor((DepthsV+0.075)/0.15)+1;
  IndexHigh=ceil((DepthsV+0.075)/0.15)+1;

  DepthLow=DepthTableV(IndexLow);
  DepthHigh=DepthTableV(IndexHigh);

  indexV = round((DepthsV+0.075)/0.15)+1;

  A_zV= ParamMat(indexV,2);
  a_zV= ParamMat(indexV,3);
  B_zV= ParamMat(indexV,4);
  b_zV= ParamMat(indexV,5);

  A_zV = A_zV * scale;
  B_zV = B_zV * scale;

  return

end

%-----------code below not currently used (terrible!)---------------------%

Aahn6=PBDataS.aahn6b;
Aahn18=flipud(PBDataS.aahn18b);
Aahn8MCF=flipud(PBDataS.aahn8MMM);

MaxDepth=50;

if EnergyC==6
 ParamMat=Aahn6;
else
 if EnergyC==18
  ParamMat=Aahn18;
 end
end

if EnergyC==8
   ParamMat=Aahn8MCF;
   MaxDepth=20;
end

DepthTableV=ParamMat(:,1);

%our own linear interpolation (faster than Matlab).
%compute two interpolation indices:
if any([DepthsV>MaxDepth])
     warning('Some depths exceed 50 cm, which is dosimetry limit!')
 end

DepthsV=DepthsV.*[DepthsV<=MaxDepth]+MaxDepth.*[DepthsV>MaxDepth];

IndexLow=floor((DepthsV+0.075)/0.15)+1;
IndexHigh=ceil((DepthsV+0.075)/0.15)+1;

if EnergyC==8
    IndexLow=floor((DepthsV)/0.253) + 1;
    IndexHigh=ceil((DepthsV)/0.253) + 1;
    IndexLow(IndexLow > length(ParamMat(:,1))) = length(ParamMat(:,1));
    IndexHigh(IndexHigh > length(ParamMat(:,1))) = length(ParamMat(:,1));
end

DepthLow=DepthTableV(IndexLow);
DepthHigh=DepthTableV(IndexHigh);

A_zV=((ParamMat(IndexLow,2).*(DepthHigh-DepthsV)+...
    ParamMat(IndexHigh,2).*(DepthsV-DepthLow))./(DepthHigh-DepthLow+10*eps)).*...
      [DepthHigh~=DepthLow]+ParamMat(IndexLow,2).*[DepthHigh==DepthLow];
a_zV=((ParamMat(IndexLow,3).*(DepthHigh-DepthsV)+...
    ParamMat(IndexHigh,3).*(DepthsV-DepthLow))./(DepthHigh-DepthLow+10*eps)).*...
      [DepthHigh~=DepthLow]+ParamMat(IndexLow,3).*[DepthHigh==DepthLow];
B_zV=((ParamMat(IndexLow,4).*(DepthHigh-DepthsV)+...
   ParamMat(IndexHigh,4).*(DepthsV-DepthLow))./(DepthHigh-DepthLow+10*eps)).*...
      [DepthHigh~=DepthLow]+ParamMat(IndexLow,4).*[DepthHigh==DepthLow];
b_zV=((ParamMat(IndexLow,5).*(DepthHigh-DepthsV)+...
     ParamMat(IndexHigh,5).*(DepthsV-DepthLow))./(DepthHigh-DepthLow+10*eps)).*...
       [DepthHigh~=DepthLow]+ParamMat(IndexLow,5).*[DepthHigh==DepthLow];

%clear IndexLow IndexHigh DepthLow DepthHigh

%Scale final answers for convenience:

A_zV = A_zV * scale;
B_zV = B_zV * scale;