function varargout = process_pac_analysis( varargin )
% PROCESS_PAC_ANALYSIS: Further analysis of tpac maps.
%
% @=============================================================================
% 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: Soheila Samiee, 2014-2017
%   - 2.0: SS. Aug. 2017
%                - Imported in public brainstorm rep
%   - 2.1: SS. Jul. 2018
%                - Bug fix in average over sources (mean)
%
eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Basic Analysis of tPAC maps';
    sProcess.FileTag     = '';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = {'Frequency','Time-resolved Phase-Amplitude Coupling'};
    sProcess.Index       = 1019;
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'timefreq'};
    sProcess.OutputTypes = {'timefreq'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    sProcess.isSeparator = 0;

    % === ANALYSIS TO BE DONE
    sProcess.options.label.Comment = 'Analysis:';
    sProcess.options.label.Type    = 'label';
    sProcess.options.analyze_type.Comment = {'Mean (Over sources)', ...
        'Median (Over sources)','Z-score on time (If no negative time, on total recording)', ...
        'Mean (Over time)'};
    sProcess.options.analyze_type.Type    = 'radio';
    sProcess.options.analyze_type.Value   = 1;

        % === Using phase
    sProcess.options.usePhase.Comment = 'Use phase in averaging (mean)';
    sProcess.options.usePhase.Type    = 'checkbox';
    sProcess.options.usePhase.Value   = 0;

end

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

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInput) %#ok<DEFNU>
    OutputFiles = {};
    isMean   = 0;
    isMedian = 0;
    isZscore = 0;
    isTimeMean = 0;

    usePhase = sProcess.options.usePhase.Value;

    % Get options
    if sProcess.options.analyze_type.Value ==1
        isMean  = 1;
        tag = '| mean';
    elseif sProcess.options.analyze_type.Value ==2
        isMedian  = 1;
        tag = '| median';
    elseif sProcess.options.analyze_type.Value ==3
        isZscore = 1;
        tag = '| zscore';
    elseif sProcess.options.analyze_type.Value ==4
        isTimeMean = 1;
        tag = '| TimeMean';
    end

    % Load TF file
    tpacMat = in_bst_timefreq(sInput(1).FileName, 0);
    % Error
    if isempty(tpacMat)
        bst_report('Error', sProcess, sInput, Messages);
        return;
    end

    % Apply the appropriate function
    tpac_avg = tpacMat.sPAC.DynamicPAC;
    if usePhase
        tpac_avg_phase = tpacMat.sPAC.DynamicPhase;
    end

    if isZscore
        iBaseline = find(tpacMat.Time<0);
        if isempty(iBaseline)
            iBaseline = 1:length(tpacMat.Time);
        end
        tpac_avg = process_zscore('Compute', tpac_avg, iBaseline);

    elseif isMean
        if usePhase
            tmp = mean(tpac_avg.*exp(1i*tpac_avg_phase),1);
            tpac_avg = abs(tmp);
            tpac_avg_phase = angle(tmp);
        else
            tpac_avg = mean(tpac_avg,1);
        end

        if length(sInput)>1
            N = length(sInput);
            for iFile = 2:N
                TimefreqMat2 = in_bst_timefreq(sInput(iFile).FileName, 0);
                if usePhase
                    tmp = mean(TimefreqMat2.sPAC.DynamicPAC.*exp(1i*TimefreqMat2.sPAC.DynamicPhase),1);
                    TimefreqMat2.sPAC.DynamicPAC  = abs(tmp);
                    TimefreqMat2.sPAC.DynamicPhase  = angle(tmp);
                else
                    TimefreqMat2.sPAC.DynamicPAC = mean(TimefreqMat2.sPAC.DynamicPAC,1);
                end
                TimefreqMat2.TF = TimefreqMat2.sPAC.DynamicPAC;
                %Saving the files
                TimefreqMat2.Comment = [TimefreqMat2.Comment, ' ', tag];
                % Output filename: add file tag
                FileTag = strtrim(strrep(tag, '|', ''));
                pathName = file_fullpath(sInput(iFile).FileName);
                OutputFile = strrep(pathName, '.mat', ['_' FileTag '.mat']);
                OutputFile = file_unique(OutputFile);
                % Save file
                bst_save(OutputFile, TimefreqMat2, 'v6');
                % Add file to database structure
                db_add_data(sInput(iFile).iStudy, OutputFile, TimefreqMat2);
                OutputFiles{end + 1} = OutputFile;
            end
        end

    elseif isMedian
        tpac_avg = median(tpac_avg,1);
        if length(sInput)>1
            N = length(sInput);
            for iFile = 2:N
                TimefreqMat2 = in_bst_timefreq(sInput(iFile).FileName, 0);
                TimefreqMat2.sPAC.DynamicPAC = median(TimefreqMat2.sPAC.DynamicPAC,1);
                TimefreqMat2.TF = median(TimefreqMat2.sPAC.DynamicPAC,1);
                %Saving the files
                TimefreqMat2.Comment = [TimefreqMat2.Comment, ' ', tag];
                % Output filename: add file tag
                FileTag = strtrim(strrep(tag, '|', ''));
                pathName = file_fullpath(sInput(iFile).FileName);
                OutputFile = strrep(pathName, '.mat', ['_' FileTag '.mat']);
                OutputFile = file_unique(OutputFile);
                % Save file
                bst_save(OutputFile, TimefreqMat2, 'v6');
                % Add file to database structure
                db_add_data(sInput(iFile).iStudy, OutputFile, TimefreqMat2);
                OutputFiles{end + 1} = OutputFile;
            end
        end

    elseif isTimeMean
        if length(sInput)==1
            tpac_avg = repmat(mean(tpac_avg,2),[1,size(tpac_avg,2),1]);
            [PACmax,tmp] = max(abs(tpac_avg),[],1);
            tpacMat.TF = squeeze(PACmax)';
        elseif length(sInput)>1
            N = length(sInput);
            for iFile = 2:N
                tPACMat2 = in_bst_timefreq(sInput(iFile).FileName, 0);
%                 TimefreqMat2.sPAC.DynamicPAC = median(TimefreqMat2.sPAC.DynamicPAC,1);
%                 TimefreqMat2.TF = median(TimefreqMat2.sPAC.DynamicPAC,1);
                tpac_avg = repmat(mean(tPACMat2.sPAC.DynamicPAC,2),[1,size(tPACMat2.sPAC.DynamicPAC,2),1]);
                [PACmax,tmp] = max(abs(tpac_avg),[],1);
                tPACMat2.TF = tpac_avg;%squeeze(PACmax)';
                tPACMat2.sPAC.DynamicPAC = tpac_avg;

                %Saving the files
                tPACMat2.Comment = [tPACMat2.Comment, ' ', tag];
                % Output filename: add file tag
                FileTag = strtrim(strrep(tag, '|', ''));
                pathName = file_fullpath(sInput(iFile).FileName);
                OutputFile = strrep(pathName, '.mat', ['_' FileTag '.mat']);
                OutputFile = file_unique(OutputFile);
                % Save file
                bst_save(OutputFile, tPACMat2, 'v6');
                % Add file to database structure
                db_add_data(sInput(iFile).iStudy, OutputFile, tPACMat2);
                OutputFiles{end + 1} = OutputFile;
            end
        end
        tpacMat.sPAC.DynamicPAC = tpac_avg;
        tpacMat.TF = tpac_avg;
        if usePhase
            tpacMat.sPAC.DynamicPhase = tpac_avg_phase;
        end
    end

    tpacMat.TF = tpac_avg;
    tpacMat.sPAC.DynamicPAC = tpac_avg;
    if usePhase
        tpacMat.sPAC.DynamicPhase = tpac_avg_phase;
    end

    % === SAVING THE DATA IN BRAINSTORM ===
    % Getting the study
    [sOutputStudy, iOutputStudy] = bst_process('GetOutputStudy', sProcess, sInput(1));
    % Comment
    tpacMat.Comment = [tpacMat.Comment, ' ', tag];
    % Output filename: add file tag
    FileTag = strtrim(strrep(tag, '|', ''));
    pathName = file_fullpath(sInput(1).FileName);
    % Preparing the output file
    OutputFile = strrep(pathName, '.mat', ['_' FileTag '.mat']);
    OutputFile = file_unique(OutputFile);
    % Save on disk
    bst_save(OutputFile, tpacMat, 'v6');
    % Register in database
    db_add_data(iOutputStudy, OutputFile, tpacMat);
    OutputFiles{end + 1} = OutputFile;
end


function test_bug1166

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_prepare_headmodel ft_headmodel_bem_asa

% This function tests that the inputs for the headmodel functions are
% read-in correctly

% ASA headmodel
filename = dccnpath('/home/common/matlab/fieldtrip/template/headmodel/skin/standard_skin_1222.vol');
cfg = [];
cfg.method = 'asa';
cfg.hdmfile = filename;
vol1 = ft_prepare_headmodel(cfg);
vol2 = ft_headmodel_asa(filename);
vol1 = rmfield(vol1,'cfg');
if ~isequal(vol1,vol2)
  error('test failed!')
end

% DIPOLI headmodel
bnd = vol1.bnd;
cfg = [];
cfg.method = 'dipoli';
vol1 = ft_prepare_headmodel(cfg,bnd);
vol1 = rmfield(vol1,'cfg');
vol1 = rmfield(vol1,'unit');
vol2 = ft_headmodel_dipoli(bnd);
if ~isequal(vol1,vol2)
  error('test failed!')
end

% Next sections obsolete, as hdmfile and headshape no longer possible
% options for input use with dipoli
%
% cfg = [];
% cfg.method = 'dipoli';
% cfg.hdmfile = filename;
% vol3 = ft_prepare_headmodel(cfg);
% vol3 = rmfield(vol3,'cfg');
% vol3 = rmfield(vol3,'unit');
% if ~isequal(vol1,vol3)
%   error('test failed!')
% end
%
% cfg = [];
% cfg.method = 'dipoli';
% cfg.headshape = filename;
% vol4 = ft_prepare_headmodel(cfg);
% vol4 = rmfield(vol4,'cfg');
% vol4 = rmfield(vol4,'unit');
% if ~isequal(vol1,vol4)
%   error('test failed!')
% end
%
%


% Earth Observing System Data Visualization
%
%   Part 1 (Main Example)
%
%   This M-file contains the sequence of MATLAB commands listed in the
%   November 2002 MATLAB Digest article on "Reading and Visualizing Data
%   from the Earth Observing System (EOS)," up to the section that
%   requires the Mapping Toolbox.
%
%   Rob Comer and Chris Lawton
%   Copyright 2002 The MathWorks, Inc.

%== DATA SELECTION AND HDFTOOL============================================

% Load the sea surface temperature (SST) swath data set, the corresponding
% geolocation data set, and the NDVI grid into HDFTOOL.
hdftool('MOD28L2.A2001185.0830.003.2001308112641.hdf')
hdftool('MOD03.A2001185.0830.003.2001305034045.hdf')
hdftool('PAL_CLIMATE_JUL_01-10_2001.HDF')

%== COLOR SCALES AND SHARING A COLORMAP ==================================

% We'll want separate colorbars for SST and NDVI, and we'll want tick marks
% labelled in physical units. So instead of using MTALAB's COLORBAR
% command, we'll use our COLORSCALE function. Because each figure can only
% have one colormap, we concatenate a 128-color SST colormap with a
% 128-color NDVI colormap.

% Load the colormap and display a standard colorbar.
load colormaps
cmap = [sstCmap; ndviCmap];   % We saved our colormap in two equal parts
figure
colormap(cmap)
set(axes,'Visible','off')
h = colorbar('horiz');
set(get(h,'Title'),'String','Standard Colorbar')

% Examine the colormap with COLORMAPEDITOR.
colormapeditor

% Assign colormap ranges to SST and NDVI, and relate them to physical
% units.
sstCmapLim  = [3 128];
sstDataLim  = [0  35];
ndviCmapLim = [  131   256];
ndviDataLim = [-0.25  1.00];

% Add color scales for SST and NDVI to the current figure.
pos = get(h,'Position'); % Position color scales relative to the color bar
colorscale(sstCmapLim, sstDataLim,5,'horiz','Position',pos + [0 0.70 0 0])
title('SST Colorscale')
xlabel('degrees Celsius')
colorscale(ndviCmapLim,ndviDataLim,0.2,'horiz','Position',pos + [0 0.35 0 0])
title('NDVI Colorscale')

%== READING MODIS SEA SURFACE TEMPERATURE SWATH ==========================

% Read MODIS Sea-Surface Temperature (SST).
mod28L2File = 'MOD28L2.A2001185.0830.003.2001308112641.hdf';
sstInfo = hdfinfo(mod28L2File,'eos');
sstSwath = sstInfo.Swath;
idx = {[1 1],[4 4],[]}; % Subset by 4 in each dimension
sst = hdfread(sstSwath,'Fields','sst','Index',idx);

% Read flags and construct land mask.
commonFlags = hdfread(sstSwath,'Fields','common_flags','Index',idx);
land = (bitget(commonFlags,8) == 1);

% Read SST attributes: scale and units.
sstInfoHDF = hdfinfo(mod28L2File);
datasets = sstInfoHDF.Vgroup.Vgroup(2).SDS;
sstDataset = datasets(strmatch('sst',{datasets.Name},'exact'));
sstAttr    = sstDataset.Attributes;
sstSlope     = double(sstAttr(strmatch('Slope',    {sstAttr.Name})).Value)
sstIntercept = double(sstAttr(strmatch('Intercept',{sstAttr.Name})).Value)
sstUnits     = sstAttr(strmatch('Units',{sstAttr.Name})).Value

% Read MODIS geolocation data fields.
mod03File = 'MOD03.A2001185.0830.003.2001305034045.hdf';
geoInfo = hdfinfo(mod03File,'eos');
sstLat = double(hdfread(geoInfo.Swath,'Fields','Latitude', 'Index',idx));
sstLon = double(hdfread(geoInfo.Swath,'Fields','Longitude','Index',idx));
sstLat(sstLat == -999) = NaN;
sstLon(sstLon == -999) = NaN;

%== MAPPING SST VALUES TO THE COLORMAP ===================================

% The SST values are encoded in the HDF-EOS data file.  To convert an encoded
% data value to absolute temperature units (degrees Celsius), one must
% multiply by sst_slope, then add sst_intercept. However, instead of
% converting to an intermediate double-valued array in degrees Celsius,
% we directly scale and shift the encoded SST values to correspond to the
% SST section of the colormap, in preparation for visualizing them via
% direct color mapping (i.e., with 'CDataMapping' set to 'direct'). First
% determine coefficients a(1) and a(2) such that a(1) * sst + a(2) will
% linearly map the encoded SST values directly onto the portion of the
% SST section of our colormap.

% Find the encoded SST limits that correspond to the temperature limits
% in sstDataLim.
sstCodelim = (sstDataLim - sstIntercept)/sstSlope;

% Solve a 2-by-2 linear system:
%   a(1) * sstCodelim + a(2) = sstCmapLim - 1
a = (sstCmapLim - 1)/[sstCodelim; [1 1]];

% Finally, scale and shift, clip to stay within the appropriate part
% of the colormap, and convert to uint8 (for economy of storage).
% Also, be sure to remap the special value of zero and apply the land mask.
sstMapped = a(1) * double(sst) + a(2);  % Scale and shift
sstMapped(sstMapped < sstCmapLim(1)-1) = sstCmapLim(1)-1;  % Clip bottom
sstMapped(sstMapped > sstCmapLim(2)-1) = sstCmapLim(2)-1;  % Clip top
sstMapped(sst == 0) = 0;   % Remap special value (displays as white)
sstMapped(land) = 1;       % Apply land mask (displays as dark gray)
sstMapped = uint8(round(sstMapped)); % Save storage with uint8

%== VISUALIZING THE MODIS SST SWATH ======================================

% Display the re-mapped SST as an indexed-color image using the colormap.
figure('Position',[100 100 size(sstMapped,2) size(sstMapped,1)])
colormap(cmap)
image(sstMapped)
set(gca,'Position',[0 0 1 1],'DataAspectRatio',[1 1 1]);

% Apply CONTOUR to the latitude and longitude geolocation arrays to
% create a lat-lon grid that illustrates the location of the swath and its
% spatial distortions. Use a contour interval of 5 degrees.
interval = 5;
latContourValues = interval ...
    * (ceil(min(sstLat(:))/interval) : floor(max(sstLat(:))/interval));
lonContourValues = interval ...
    * (ceil(min(sstLon(:))/interval) : floor(max(sstLon(:))/interval));
hold on
[c,h] = contour(sstLat,latContourValues,'k'); clabel(c,h)
[c,h] = contour(sstLon,lonContourValues,'k'); clabel(c,h)
hold off

% Use the geolocation fields as inputs to SURFACE to re-display the swath in
% latitude-longitude coordinates, the use COLORSCALE to add a color scale bar
% with units indicated in degrees Celsius.
figure
colormap(cmap)
axes('DataAspectRatio',[1 1 1],...
    'XTick',-180:5:180,'YTick',-90:5:90,...
    'XLim',[20 50],'YLim',[12 37],...
    'XGrid','on','YGrid','on')
xlabel('Longitude, degrees')
ylabel('Latitude, degrees')
title('Geolocated SST swath, July 4, 2001')
surface(sstLon, sstLat, double(sstMapped) + 1,...
    'Linestyle','none','CDataMapping','Direct')
% Add color scale
pos = get(gca,'Position');
set(gca,'Position',pos + [-0.06 0 0 0])
colorscale(sstCmapLim, sstDataLim, 5, 'vert',...
           'Position',[0.92 pos(2) 0.025 pos(4)])
xlabel('SST')
ylabel('degrees Celsius')

%== READING AVHRR GLOBAL NDVI GRID =======================================

% Read NDVI data from the AVHRR Climate file.
ndviFile = 'PAL_CLIMATE_JUL_01-10_2001.HDF';
ndvi = hdfread(ndviFile,'Data-Set-2');

% Read NDVI data description and attributes.
ndviInfo = hdfinfo(ndviFile);
ndviAttr = ndviInfo.SDS.Attributes;
ndviAddOffset   = double(ndviAttr(...
    strmatch('add_offset',  {ndviAttr.Name},'exact')).Value)
ndviScaleFactor = double(ndviAttr(...
    strmatch('scale_factor',{ndviAttr.Name},'exact')).Value)

%== MAPPING NDVI VALUES TO THE COLORMAP ==================================

% The approach here is the same as for the sea surface temperature data
% mappings, except that the linear relationship between the encoded NDVI
% values and actual NDVI levels is expressed in terms of an offset and
% scale factor rather than an intercept and slope. To convert an encoded
% data value to absolute NDVI units, one must subtract the offset, then
% multiply by the scale factor.

% Find the encoded NDVI limits that correspond to the absolute NDVI limits
% in ndviDataLim.
ndviCodelim = ndviDataLim/ndviScaleFactor + ndviAddOffset;

% Solve a 2-by-2 linear system:
%   b(1) * ndviCodelim + b(2) = ndviCmapLim - 1
b = (ndviCmapLim - 1)/[ndviCodelim; [1 1]];

% Finally, scale and shift, clip to stay within the appropriate part
% of the colormap, and convert to uint8. Also, be sure to individually
% remap the special values (1 denoting water and 0 missing data).
ndviMapped = b(1) * double(ndvi) + b(2);  % Scale and shift
ndviMapped(ndviMapped < ndviCmapLim(1)-1) = ndviCmapLim(1)-1;  % Clip bottom
ndviMapped(ndviMapped > ndviCmapLim(2)-1) = ndviCmapLim(2)-1;  % Clip top
ndviMapped(ndvi==0) = 130-1;   % Missing data (displays as gray)
ndviMapped(ndvi==1) = 129-1;   % Water (displays as blue)
ndviMapped = uint8(round(ndviMapped));  % Save storage with uint8

%== VISUALIZING THE NDVI GRID ============================================

% Show the NDVI grid over global latitude-longitude axes, and add a color
% scale.
figure
colormap(cmap)
image(ndviMapped,'XData',[-179.5 179.5],'YData',[89.5 -89.5])
set(gca,'YDir','normal','DataAspectRatio',[1 1 1],...
    'XTick',[-180 : 30 : 180],'YTick',[-90 : 30 : 90],...
    'XLim',[-180 180],'YLim',[-90 90])
xlabel('Longitude, degrees')
ylabel('Latitude, degrees')
title('NDVI from AVHRR, July 1-10, 2001')
% Add color scale
pos = get(gca,'Position');
set(gca,'Position',pos + [0 0.075 0 0])
colorscale( ndviCmapLim, ndviDataLim, 0.2, 'horiz','Position',[pos(1:3) 0.05])
xlabel('NDVI')

%== COMBINED SST AND NDVI DISPLAY ========================================

% Now show the NDVI and SST together on a latitude-longitude grid
% after zooming in on the region containing the SST swath. We add a
% color scale for each data set.
figure('Renderer','zbuffer')
colormap(cmap)
image(ndviMapped,'XData',[-179.5 179.5],'YData',[89.5 -89.5])
set(gca,'YDir','normal','DataAspectRatio',[1 1 1],...
    'XTick',-180:5:180,'YTick',-90:5:90,...  % Ticks every 5 degrees
    'XLim',[15 60],'YLim',[0 45]) % Axes limits in degrees
xlabel('Longitude, degrees')
ylabel('Latitude, degrees')
title('SST swath plotted over one-degree NDVI grid')
surface(sstLon, sstLat, ones(size(sstLon)), double(sstMapped) + 1,...
    'Linestyle','none','CDataMapping','Direct');
% Add color scales
pos = get(gca,'Position');
set(gca,'Position',pos + [-0.12 0 0 0])
colorscale(ndviCmapLim, ndviDataLim, 0.2, 'vert',...
           'Position',[0.9 pos(2) 0.025 pos(4)])
xlabel('NDVI')
colorscale(sstCmapLim, sstDataLim, 5, 'vert',...
           'Position',[0.8 pos(2) 0.025 pos(4)])
xlabel('SST')
ylabel('degrees Celsius')


function varargout = guiDatePicker(varargin)
% GUIDATEPICKER MATLAB code for guiDatePicker.fig
%      GUIDATEPICKER, by itself, creates a new GUIDATEPICKER or raises the existing
%      singleton*.
%
%      H = GUIDATEPICKER returns the handle to a new GUIDATEPICKER or the handle to
%      the existing singleton*.
%
%      GUIDATEPICKER('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in GUIDATEPICKER.M with the given input arguments.
%
%      GUIDATEPICKER('Property','Value',...) creates a new GUIDATEPICKER or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before guiDatePicker_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to guiDatePicker_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help guiDatePicker

% Last Modified by GUIDE v2.5 21-Apr-2011 12:54:26

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

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

% --- Executes just before guiDatePicker is made visible.
function guiDatePicker_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to guiDatePicker (see VARARGIN)
year = 2000:1:2020;
dt = varargin{1}; %
if ~isempty(dt)
    [YYi, MMi, DDi, hhI, mmI, ssI] = datevec(dt);
    wo = find(year==YYi);
    set(handles.Year,'Value',wo);
    set(handles.Month,'Value',MMi);
    set(handles.Hours,'String',num2str(hhI));
    set(handles.Minutes,'String',num2str(mmI));
end

y = get(handles.Year,'String');
yi = get(handles.Year,'Value');
m = get(handles.Month,'String');
mi = get(handles.Month,'Value');
yy = y(yi);

YY = str2double(yy);
MM = mi;
ldom = eomday(YY,MM);
for i = 1:ldom
    dtn(i) = datenum([YY MM i 0 0 0]);
    dts{i} = datestr(dtn);
    %disp(char(dts(i)));
end
    [n,s] = weekday(datenum([YY MM 1 0 0 0]));
% clear calendar
for i = 1:7
    for j = 1:6
        D{j,i} = '';
    end
end
% update calendar
num = 0;
for i = n:7
    num = num + 1;
    D{1,i} = num;
end
for j = 2:6
    for i = 1:7
        num = num + 1;
        if num > ldom
            break;
        end
        D{j,i} = num;
    end
end

set(handles.Calendar,'Data',D);

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

% Close handles structure
guidata(hObject, handles);

% UIWAIT makes guiDatePicker wait for user response (see UIRESUME)
uiwait(handles.figure1);

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

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

% --- Executes on selection change in Year.
function Year_Callback(hObject, eventdata, handles)
% hObject    handle to Year (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns Year contents as cell array
%        contents{get(hObject,'Value')} returns selected item from Year

y = get(handles.Year,'String');
yi = get(handles.Year,'Value');
m = get(handles.Month,'String');
mi = get(handles.Month,'Value');
yy = y(yi);

YY = str2double(yy);
MM = mi;
ldom = eomday(YY,MM);
for i = 1:ldom
    dtn(i) = datenum([YY MM i 0 0 0]);
    dts{i} = datestr(dtn);
    %disp(char(dts(i)));
end
    [n,s] = weekday(datenum([YY MM 1 0 0 0]));
% clear calendar
for i = 1:7
    for j = 1:6
        D{j,i} = '';
    end
end
% update calendar
num = 0;
for i = n:7
    num = num + 1;
    D{1,i} = num;
end
for j = 2:6
    for i = 1:7
        num = num + 1;
        if num > ldom
            break;
        end
        D{j,i} = num;
    end
end

set(handles.Calendar,'Data',D);

% --- Executes during object creation, after setting all properties.
function Year_CreateFcn(hObject, eventdata, handles)
% hObject    handle to Year (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on selection change in Month.
function Month_Callback(hObject, eventdata, handles)
% hObject    handle to Month (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns Month contents as cell array
%        contents{get(hObject,'Value')} returns selected item from Month
y = get(handles.Year,'String');
yi = get(handles.Year,'Value');
m = get(handles.Month,'String');
mi = get(handles.Month,'Value');
yy = y(yi);

YY = str2double(yy);
MM = mi;
ldom = eomday(YY,MM);
for i = 1:ldom
    dtn(i) = datenum([YY MM i 0 0 0]);
    dts{i} = datestr(dtn);
    %disp(char(dts(i)));
end
    [n,s] = weekday(datenum([YY MM 1 0 0 0]));
% clear calendar
for i = 1:7
    for j = 1:6
        D{j,i} = '';
    end
end
% update calendar
num = 0;
for i = n:7
    num = num + 1;
    D{1,i} = num;
end
for j = 2:6
    for i = 1:7
        num = num + 1;
        if num > ldom
            break;
        end
        D{j,i} = num;
    end
end

set(handles.Calendar,'Data',D);

% --- Executes during object creation, after setting all properties.
function Month_CreateFcn(hObject, eventdata, handles)
% hObject    handle to Month (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on button press in Close.
function Close_Callback(hObject, eventdata, handles)
% hObject    handle to Close (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

y = get(handles.Year,'String');
yi = get(handles.Year,'Value');
m = get(handles.Month,'String');
mi = get(handles.Month,'Value');
yy = y(yi);

YY = str2double(yy);
MM = mi;
if isfield(handles,'Day')
    DD = handles.Day;
    hh = str2double(get(handles.Hours,'String'));
    mm = str2double(get(handles.Minutes,'String'));

    % x = datenum([YY MM DD hh mm 0])
    handles.output = datenum([YY MM DD hh mm 0]);
    % Close handles structure
    guidata(hObject, handles);

    uiresume;
else
   errordlg('Day was not selected','DatePicker Error');
end

% --- Executes during object creation, after setting all properties.
function Calendar_CreateFcn(hObject, eventdata, handles)
% hObject    handle to CTDtab (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% --- Executes when selected cell(s) is changed in Calendar.
function Calendar_CellSelectionCallback(hObject, eventdata, handles)
% hObject    handle to Calendar (see GCBO)
% eventdata  structure with the following fields (see UITABLE)
%	Indices: row and column indices of the cell(s) currently selecteds
% handles    structure with handles and user data (see GUIDATA)
        sel = eventdata.Indices;      % Get selection indices (row, col)
                                     % Noncontiguous selections are ok
        selcols = unique(sel(:,2));   % Get all selected data col IDs
        selrows = unique(sel(:,1));
        num = get(hObject,'Data');  % Get copy of uitable data

x = num(selrows,selcols);
handles.Day = x{1};

% Close handles structure
guidata(hObject, handles);

function Hours_Callback(hObject, eventdata, handles)
% hObject    handle to Hours (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of Hours as text
%        str2double(get(hObject,'String')) returns contents of Hours as a double

% --- Executes during object creation, after setting all properties.
function Hours_CreateFcn(hObject, eventdata, handles)
% hObject    handle to Hours (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function Minutes_Callback(hObject, eventdata, handles)
% hObject    handle to Minutes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of Minutes as text
%        str2double(get(hObject,'String')) returns contents of Minutes as a double

% --- Executes during object creation, after setting all properties.
function Minutes_CreateFcn(hObject, eventdata, handles)
% hObject    handle to Minutes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function dmapErrors = evalDepthMaps(class,jobID,metric)
    globals;
    if(nargin<3)
        metric = 'zmae';
    end
    dmapDir = jobDirs(class,jobID,'dmap');
    evalFileName = jobDirs(class,jobID,'evalDepth');
    statesDir = jobDirs(class, jobID, 'state');
    gtDmapDir = fullfile(cachedir,class,'gtDepthMap');
    if(~exist(dmapDir,'dir'))
        error('Depth maps not found. Generate them first!');
    end

    if(~exist(gtDmapDir,'dir'))
        error('Ground truth depth maps not found. Generate them first!');
    end

    switch metric
        case 'corr'
            disp('Using Depth Correlation');
        case 'zmae'
            disp('Using Z-MAE');
        case 'absrel'
            disp('Using absolute relative error');
    end

    fnames = getFileNamesFromDirectory(dmapDir,'types',{'.mat'});
    fnames = removeFlipNames(fnames);

    disp('Depth map evaluation');

    dmapErrors = zeros(length(fnames),1);
    p =  TimedProgressBar( length(fnames), 30, ...
    'Depth Map Errors: Remaining ', ', Completed ', 'Depth Map evaluation Time: ' );

    parfor i=1:length(fnames)
        GtFile = fullfile(gtDmapDir,fnames{i});
        dmapFile = fullfile(dmapDir,fnames{i});
        state = load(fullfile(statesDir,fnames{i}),'state');
        state = state.state;
        if(~exist(GtFile,'file'))
            warning('Gt depth map not found: %s\n',GtFile);
            dmapErrors(i) = nan;
            continue;
        end
        gtdmap = load(GtFile,'dmap');
        gtdmap = gtdmap.dmap;
        dmap = load(dmapFile,'dmap');dmap = dmap.dmap;
        switch metric
            case 'corr'
                dmapErrors(i) = dmapMetricCorr(dmap,gtdmap,state.gtmask);
            case 'zmae'
                dmapErrors(i) = dmapMetricZMAE(dmap,gtdmap,state.gtmask);
            case 'absrel'
                dmapErrors(i) = dmapMetricRel(dmap,gtdmap,state.gtmask);
        end

        % Visualization
        if 0
            gtdmap(isinf(gtdmap)) = nan;
            dmap(isinf(dmap)) = nan;
            gtdmap(~state.mask) = nan;
            dmap(~state.mask) = nan;
            subplot(1,2,1);
            out = visualizeDEM(dmap);
            imagesc(imcrop(out,state.bbox));axis equal off;
            title(sprintf('Opt Depth Map - %.4f',dmapErrors(i)));
            subplot(1,2,2)
            out = visualizeDEM(gtdmap);
            imagesc(imcrop(out,state.bbox)); axis equal off;
            title('Gt Depth Map');
            pause;
            clf;
        end
        p.progress;
    end
    p.stop;
    save(evalFileName,'dmapErrors','fnames');
end


function [final_sur ] = detect_sym( obblist, Rmat, Dmat, RELATIONS, LABELLEN )

% detect the surround and symmetry relations in the obblist
% The symmetry relation is disabled in this version

% checked_label = [];
% load(pathparam.label2vec_filename);
% box_vec = label2vec_map('Box');
% box_label = binary2dec(box_vec{1});

% search for the candidate_list
% each candidate is a list of objects with similar sizes which may be
% involved in a symmetry or surround relation
objnum = size(obblist,1);
sizelist = obblist(:,10:12);
size_thre = 0.03;
matched = [];
candidate_list = {};
for i = 1:objnum
    idx = find(matched==i);
    if(length(idx)==0)
        delta = abs(sizelist - repmat(sizelist(i,:),objnum,1));
        delta = delta - size_thre;
        delta = delta(:,1)+delta(:,2)+delta(:,3);
        idx = find(delta<=0);
        matched = union(matched,idx);
        if(length(idx)>1)
            candidate_list{length(candidate_list)+1} = idx;
        end
    end
end

%% extract the objects with similar heights in each candidate
ncandidate_list = {};
up = [0;1;0];
height_thre = 0.1;
for i = 1:length(candidate_list)
    clist = candidate_list{i};
    heights = obblist(clist,1:3)*up;
    matchednums = zeros(length(clist),1);
    for j = 1:length(clist)
        tmplist = abs(heights-heights(j));
        ind = find(tmplist<height_thre);
        matchednums(j) = length(ind);
    end
    [~,I] = max(matchednums);
    tmplist = abs(heights-heights(j));
	ind = find(tmplist<height_thre);
  	if(length(ind)>1)
        ncandidate_list{length(ncandidate_list)+1} = clist(ind);
    end
end
candidate_list = ncandidate_list;

%% search for all sets without central obj
% [ sym ] = search_symmetryset( candidate_list, obblist, Rmat, Dmat, LABELLEN );
%% search for all sets surrounding one obj
[ cen_sym, cen_sur ] = search_surroundset( candidate_list, obblist, Rmat, Dmat, RELATIONS, LABELLEN );
sur = {};
for i = 1:size(cen_sym,1)
    sur{size(sur,1)+1,1} = cen_sym{i,1};
    sur{size(sur,1),2} = cen_sym{i,2};
end
for i = 1:size(cen_sur,1)
    sur{size(sur,1)+1,1} = cen_sur{i,1};
    sur{size(sur,1),2} = cen_sur{i,2};
end
% sur = [cen_sym,cen_sur];

% sort the sur pairs based on the distance
dlist = zeros(size(sur,1),1);
for i = 1:size(sur,1)
    list = sur{i,1};
    c = sur{i,2};
    dlist(i) = Dmat(list(1),c);
end
[~,I] = sort(dlist);
sur = sur(I,:);

% except for the central object, each object can only be involved in one surround relation
isinvolved = zeros(size(obblist,1),1);
final_sur = {};
for i = 1:size(sur,1)
    list = sur{i,1};
    if(sum(isinvolved(list))==0)
        % add this surround pair
        s = [sur{i,2};list(:)];
        final_sur{length(final_sur)+1} = s;
    end
end

end


function [clusters,CLU,subclusters] = mask2clusters(P,varargin)
% Extracts clusters and con img data from mask
%
% Use with *mask_intersection.m*
%
% To get clusters but not extract data, enter only one argument.
%
% To get clusters and choose extraction imgs with the GUI, enter an empty [] 2nd argument.
%
% :Usage:
% ::
%
%    [clusters,CLU,subclusters] = mask2clusters(img mask file with voxels,[imgs to extract data from],[df])
%
%
% DOES *NOT* CONVERT BETWEEN DIFFERENT VOXEL SIZES AND POSITIONS BETWEEN IMNAMES AND SPM/VOL STRUCTS
%
% :See also: roi_probe
%
% If no imgs are entered, Z-scores are values from mask
%
% If df is entered, values in mask img are converted to Z-scores with spm_t2z.m
%
% If extract img names are empty and df is entered, assume we're using
% values from mask as t-values and convert to Z-scores
%
% WARNING: for spm2 compatibility, ABSOLUTE VALUES of voxel sizes are
% returned; e.g., ignores analyze flipping in SPM2.
%
% % Matlab 6.5/OSX bug gives seg fault or something if mask is too big.
%
% :Example:
% ::
%
%    cl = mask2clusters('myimage.img',[img string mtx],[]); % no z-score
%    conversion, extracts data from [img string mtx]
%
%    cl = mask2clusters('rob_tmap_0002_filt_t_3-05_k10_neg.img')
%
%    % This one works with already-loaded image data and a mat matrix:
%    V = spm_vol('rob_tmap_0002_filt_t_3-05_k10_neg.img'); dat = spm_read_vols(V);
%    cl = mask2clusters(dat,V.mat);
%
%
% ..
%    tor wager
%
%    modification 2/27/03
% ..

% ..
%    set up inputs
% ..
clusters = [];
df = []; imP = [];  resl = 0; vmat = [];

for i = 1:length(varargin)
    tmp = varargin{i};
    if isstr(tmp), imP = tmp;       % images to extract data from
    elseif isstruct(tmp), vmat = tmp.mat;  % it's an spm_vol V struct
    elseif isscalar(tmp), df = tmp; % df to convert from t to z-scores
    elseif all(size(tmp) > 1), vmat = tmp;      % it's a SPM .mat matrix to be used with raw data instead of string img name
    end
end

if isempty(P)
    P = spm_get(1,'*img','Select mask image with voxels to extract');
end

if ischar(P)
    P = deblank(P);

    if isempty(which(P)) && ~exist(P,'file')
        disp(['Cannot find file: ' P]);
        return
    end

    if ~isempty(which(P)), P = which(P); end
end

% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% get coordinates and height values from the mask
% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

% img2voxel can take raw data, if vmat is entered; if string, vmat not
% used.
[CLU.XYZ,CLU.XYZmm,CLU.Z,CLU.V] = img2voxel(P,vmat);
CLU.XYZ = CLU.XYZ(1:3,:);

% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% change pseudo-T  or T values to Z scores
% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if ~isempty(imP)
    if isempty(df) & length(varargin) > 1, df = size(imP,1) - 1;, end     % to get df
    if df == 0, df = [];, end
end

if ~isempty(df)
    disp(['Converting to Z scores based on ' num2str(df) ' df.'])
    [CLU.Z] = spm_t2z(CLU.Z,df);
else
    df = 0;
    %disp('Saving values in mask file in clusters.Z (no z-score conversion)')
    %CLU.Z = ones(1,size(CLU.XYZ,2));
end

if size(CLU.Z,1) > size(CLU.Z,2), CLU.Z = CLU.Z';, end

% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
% fill in other fields
% +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

% adjust crit_t from 0 to 1 for compatibility with fixed_TSU (Talairach)
% needs to be a non-zero value to display on Talairach
% ------------------------------------------------------
CLU.crit_t = 1;

CLU.cl_size = 0;
CLU.df = df;

% for compatibility with SPM struct and cluster analysis
% ------------------------------------------------------
CLU.voxSize = diag(CLU.V.mat)';
CLU.voxSize = CLU.voxSize(1:3);
CLU.VOX = CLU.voxSize;		% compatible with VOL structure
CLU.M = CLU.V(1).mat;

CLU.u = CLU.crit_t;
CLU.k = CLU.cl_size;

if isfield(CLU.V,'fname')
    [a,b,c] = fileparts(CLU.V.fname);
    CLU.title = [b];
else
    CLU.title = 'Analyze image file.';
end

if resl, imP = spm_get(Inf,'*img','Select imgs for data extraction.');
else, % input imP              % to get P
end

%if ~exist(P(1,:))
%    warning('Image files cannot be found in ind. subject directories: Path has changed?')
    % tries to re-find P - only works for my specific directory
%    for i = 1:size(P,1)
%        [d,f,e] = fileparts(P(1,:));
%        [dummy,d] = fileparts(d);
%        newP{i,1}=fullfile('..',d,[f e]);
%    end
%    P = cell2mat(newP);
%    disp(['Found: ' P(1,:) ' etc.'])
%end

if isempty(CLU),
    warning('EMPTY ... mask2clusters found no eligible voxels.')
    clusters = [];
    return
end

if isempty(CLU.XYZ) | isempty(CLU.Z)
    warning('EMPTY ... mask2clusters found no eligible voxels.')
    clusters = [];
    return
end

[clusters] = tor_extract_rois(imP,CLU,CLU,1);
for i = 1:length(clusters),
    clusters(i).P = P;,
    clusters(i).imP = imP;,
    if size(imP,1) == 1 & df == 0,
        %disp('Saving values in mask file in clusters.Z')
        clusters(i).Z = clusters(i).all_data;
    end

    % for SPM2 compatibility
    clusters(i).voxSize = abs(clusters(i).voxSize);
end

if nargout > 2, [subclusters] = cluster_princomp(clusters); end

return


function [gIX,rankscore] = RankByStimLock(hfig,gIX)
fishset = getappdata(hfig,'fishset');

isStimAvr = getappdata(hfig,'isStimAvr');
isRawtime = getappdata(hfig,'isRawtime');
if isStimAvr == 1 || isRawtime == 1
    setappdata(hfig,'isStimAvr',0);
    setappdata(hfig,'isRawtime',0);
    global h_isStimAvr h_israwtime; %#ok<TLEV>
    h_isStimAvr.Value = 0;
    h_israwtime.Value = 0;
    UpdateTimeIndex(hfig);
end

[gIX, numU] = SqueezeGroupIX(gIX);
M = getappdata(hfig,'M');
C = FindClustermeans(gIX,M);

[~,~,H] = GetTrialAvrLongTrace(hfig,C);

% if fishset == 1
%     [~,~,H] = GetTrialAvrLongTrace(hfig,C);
% else
%     [~,~,H_multistim] = GetTrialAvrLongTrace(hfig,C);
%     if isempty(H_multistim)
%         errordlg('chosen stimulus range not suitable for stim-lock analysis');
%         rankscore = 1:numU;
%         return;
%     end
%     H = zeros(size(H_multistim,1),1);
%     for i = 1:size(H_multistim,1)
%         H(i) = min(H_multistim(i,:));
%     end
%
%     %     % instead of algebraic average along 2nd dimension, use
%     %     % inverse of geometric average... large value~low variation. geometric
%     %     % mean biases towards large values, i.e. good stim-lock of any stimulus
%     %     % is emphasized.
%     %     H = zeros(size(H_raw,1),1);
%     %     for i = 1:size(H_raw,1),
%     %         temp = sum((1./H_raw(i,:)).^2);
%     %         H(i) = 1./sqrt(temp);
%     %     end
%
% end

[gIX,rankscore] = SortGroupIXbyScore(H,gIX,numU);
end


%kirotate 'Rotate Object by Arbitrary Angle '
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros irotate.pane file
%
% Parameters:
% InputFile: i 'Input ', required: 'Input data object'
% Toggle: resize 'Resize to fit', default: 0: 'resize to fit rotated image'
% Toggle: planes 'Process by planes', default: 0: 'Process data by WxH planes (fast, but only good for smaller objects) instead of small prisms down D (slow, but works for huge objects)'
% OutputFile: o 'Output', required: 'Output data object'
% Toggle: wctr 'W center', default: 0: 'center of rotation in center of W coordinate span'
% Toggle: hctr 'H center', default: 0: 'center of rotation in center of H coordinate span'
%
% Example: o = kirotate(i, {'i','';'resize',0;'planes',0;'o','';'wctr',0;'hctr',0})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% irotate - Rotate Object by Arbitrary Angle
%
%  DESCRIPTION
% .I irotate
% is used to apply a rotation operation to each WxH plane in the value segment
% of a data object. The is equivalent to rotating the data about an axis
% that lies parallel to the DEPTH axis.
%
% Interpolation of output data from the input data is bilinear.
%
% Each of the (DxE) WxH planes is rotated before being stored in the output
% object.
%
% If rotation about an axis parallel to an axis other than DEPTH is desired,
% it is necessary to use the kaxis
% program to reorient the data such that the desired axis becomes the DEPTH axis
% before applying irotate. This limitation is due to computational complexity and
% performance issues, particularly when dealing with large data sets.
%
% If the input object has a map, the data is pulled through the map prior to
% rotation, and the output object will have no map.
%
% The arbitrary (floating point) rotation angle is CW for a positive angle, and
% should be specified in degrees.
%
% The center of rotation is used to specify that WxH point in the input object
% about which the rotation is to take place. The -wctr and -hctr flags can be used
% to ask irotate to automatically place the center of rotation at the center
% of the WxH plane.
%
% The -resize flag will causes the output object dimensions to be increased or
% decreased sufficiently to just contain all of the input data even after
% rotation.
% When this flag is used, the W and H dimensions may increase or decrease by a
% factor of up to sqrt(2).  If -resize is selected and the center of rotation is
% not in the center of the WxH plane, the resulting rotated data will not
% be centered in the output data.
%
% The -planes flag is used to change the way the data is accessed for processing.
% If the data set is small enough that individual WxH planes of data
% will fit in memory, then use of the -planes
% flag will cause processing to be done on a plane-by-plane basis; this is
% generally "much faster\fR than the default method (orders of magnitude).
% If -planes is "not\fR specified, then
% processing will occur in prisms down the D axis, which is much slower but will
% work on data sets of any size. If you have lots of memory, you may be able to
% get away with using -planes even for quite large data sets, say around 2Kx2K
% or more points per plane.
%
% The value given to those data points in the output that do not map to
% data in the input object is controlled by the padding behavior. If padding by
% zero is selected, then those areas will be set to zero. If padding by a
% specific pad value is selected, then that value will appear in areas that
% don't map to the input data. If padding by the pad value attribute of the
% input object is selected, then the pad value from the input object
% attribute KPDS_VALUE_PAD_VALUE is retrieved and is placed in areas that
% don't map to the input data.
%
% If the input object has a mask, a new mask will be computed for the output
% object indicating which data points contain reliable data.
% If a mask is present, then the output mask will be computed
% with value 1 if there was a full set of valid data points in the input from
% which to compute the output value. If any of the needed input points was
% marked as invalid,
% then the output mask value is set to 0, marking that output point as invalid.
% For regions of the output that do not map to data in the input object, the
% mask can be set to valid or invalid, depending on the setting of the
% -padvalid argument.
%
% If the input object has a location data segment and the location data is
% anything other than KUNIFORM, then
% .I irotate
% will complain and ask you to use klocxform instead.
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
% igeowarp(1,) klocxform(1)
%
%  RESTRICTIONS
%
% If
% .I irotate
% is too slow for your application, be sure to read about the -planes option
% to see if it can be used. If so, processing may occur several orders of
% magnitude faster.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kirotate(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kirotate(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'resize', 0;'planes', 0;'o', '__output';'wctr', 0;'hctr', 0};
maxval={0,0,0,0,0,0};
minval={0,0,0,0,0,0};
istoggle=[0,1,1,0,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','Toggle','Toggle','OutputFile','Toggle','Toggle'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'irotate"  '],Inputs,narglist);


% very basic generic non-Cartesian recon using BART
% assumes raw data *.mat (or *.dat) fiels are stored together with the
% *.seq Pulseq files withe the identical(!) names
%
% optionally needs mapVBVD in the path (if raw data have not yet been converted to *.mat)
% requires BART, modify or remove the line below depending on your path settings
bart_path='~/pulseq_home/bart/bart-0.6.00';
addpath([bart_path '/matlab']);
cur_dir=pwd;
cd(bart_path);
setenv('TOOLBOX_PATH', pwd);
cd(cur_dir);
%% Load the latest file from a dir
path='../IceNIH_RawSend/'; % directory to be scanned for data files
%path='/data/zte_petra/';
%path='~/Dropbox/shared/data/siemens/';
%path='~/20211025-AMR/data';

pattern='*.seq';

if path(end)~=filesep, path=[path filesep]; end

D=dir([path pattern]);
[~,I]=sort([D(:).datenum]);
seq_file_path = [path D(I(end-0)).name]; % use end-1 to reconstruct the second-last data set, etc.

%% alternatively just provide the path to the .seq file

%seq_file_path = '../interpreters/siemens/data_example/gre_example.seq'
%seq_file_path = [path '2020-11-10-073628.seq'];

%% Load sequence from the file with the same name as the raw data
fprintf(['loading `' seq_file_path '´ ...\n']);
seq = mr.Sequence();              % Create a new sequence object
seq.read(seq_file_path,'detectRFuse');

%% keep basic filename without the extension
[p,n,e] = fileparts(seq_file_path);
basic_file_path=fullfile(p,n);

%% load the raw data file
data_file_path= [basic_file_path '.mat']; % try to load a matlab file with raw data...
try
    fprintf(['loading `' data_file_path '´ ...\n']);
    data_unsorted = load(data_file_path);
    if isstruct(data_unsorted)
        fn=fieldnames(data_unsorted);
        assert(length(fn)==1); % we only expect a single variable
        data_unsorted=data_unsorted.(fn{1});
    end
catch
    data_file_path= [basic_file_path '.dat']; % now try to load a raw data file...
    fprintf(['falling back to `' data_file_path '´ ...\n']);
    twix_obj = mapVBVD(data_file_path);
    if iscell(twix_obj)
        data_unsorted = twix_obj{end}.image.unsorted();
    else
        data_unsorted = twix_obj.image.unsorted();
    end
    seqHash_twix=twix_obj.hdr.Dicom.tSequenceVariant;
    if length(seqHash_twix)==32
        fprintf(['raw data contain pulseq-file signature ' seqHash_twix '\n']);
    end
    clear twix_obj
end

%% calculate k-space trajectory
traj_recon_delay=0*1e-6;%1.75e-6; % adjust this parameter to potentially improve resolution & geometric accuracy. It can be calibrated by inverting the spiral revolution dimension and making two images match. for our Prisma and a particular trajectory we found 1.75e-6
samples_to_mask=0; % each ADC may contain damaged samples in the begining
fprintf('calculating k-space trajectory ...');

%[ktraj_adc, t_adc, ktraj, t, t_excitation, t_refocusing]=seq.calculateKspacePP('trajectory_delay', traj_recon_delay);
%[ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace('trajectory_delay', traj_recon_delay);
[ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation] = seq.calculateKspacePP('trajectory_delay', traj_recon_delay);

% optionally transform or patch the trajectory
ktraj_adc(2,:)=-ktraj_adc(2,:); % seems to be needed for the correct orientaton
%ktraj_adc(3,:)=0;
fprintf(' done\n');

figure; plot3(ktraj_adc(1,:),ktraj_adc(2,:),ktraj_adc(3,:),'r.');
axis('equal');
drawnow;

fov=seq.getDefinition('FOV');
if isempty(fov)
    fov=[256 256 256]; % default FOV
    fprintf('WARNING: no FOV defined in the pulseq file, assuming FOV of %g x %g x %g m, matrix size will be wrong if these are incorrect\n', fov);
end

%fov=fov*1.5;
%% raw data preparation

adc_len=size(data_unsorted,1);
channels=size(data_unsorted,2);
readouts=size(data_unsorted,3);

%% average repetitions
na=numel(data_unsorted)/channels/numel(t_adc);
if (na>1)
    fprintf('averaging over %d acquisitions ...\n',na);
    data_unsorted=reshape(sum(reshape(data_unsorted,[adc_len,channels,readouts/na,na]),4),[adc_len,channels,readouts/na]);
    %data_unsorted=reshape(data_unsorted,[adc_len,channels,readouts/na,na]);
    %data_unsorted=reshape(data_unsorted(:,:,:,1),[adc_len,channels,readouts/na]);
    readouts=readouts/na;
end

%% mask data if requested
if samples_to_mask>0
    ktraj_adc=reshape(ktraj_adc,[3,adc_len,readouts]);
    ktraj_adc(:,1:samples_to_mask,:)=[];
    t_adc=reshape(t_adc,[adc_len,readouts]);
    t_adc(1:samples_to_mask,:)=[];
    data_unsorted(1:samples_to_mask,:,:)=[];
    adc_len=adc_len-samples_to_mask;
    ktraj_adc=reshape(ktraj_adc,[3,adc_len*readouts]);
    t_adc=reshape(t_adc,[1,adc_len*readouts]);
end

%% data preparation and petra/zte specific stuff
rawdata = permute(data_unsorted, [1,3,2]);

if strcmp('petra',seq.getDefinition('Name'))
    fprintf('performing special data handling for petra ...\n');
    SamplesPerShell=seq.getDefinition('SamplesPerShell');
    waspi_samples=1; % 1 16 32 tested; BART (nlinv) does not seem to like waspi (background noise goes up)
    if ~isempty(SamplesPerShell)
        SamplesSPI=sum(SamplesPerShell(2:end));
        data_mask=boolean([ones(SamplesPerShell(1),adc_len); ...
                          ones(SamplesSPI,waspi_samples), zeros(SamplesSPI,adc_len-waspi_samples)])';
    else
        data_mask=boolean(ones(adc_len,readouts));
    end
    ktraj_adc=ktraj_adc(:,data_mask(:));

    rdtmp=reshape(rawdata, [adc_len*readouts,channels]);
    rawdata=rdtmp(data_mask(:),:);
    clear rdtmp;
    % reshape to 4D (1 samples 1 channels) as BART needs it
    rawdata=reshape(rawdata,[1 size(rawdata,1) 1 channels]);

    kyz_plane=abs(ktraj_adc(1,:))<0.9; % *(1/fov(1)) -- for BART-like trajectory not needed
    figure; plot(ktraj_adc(2,kyz_plane),ktraj_adc(3,kyz_plane),'r.','MarkerSize',0.5); axis equal;title('Kyz plane');
    %figure; plot3(kspace(kxz_plane,1),kspace(kxz_plane,2),kspace(kxz_plane,3),'r.'); axis equal;
    drawnow;
elseif strcmp('ute_rs',seq.getDefinition('Name'))
    fprintf('performing special data handling for ute ...\n');
    % average every 2nd spoke because of the half-rf excitation
    ktraj_adc=reshape(ktraj_adc,[3,adc_len,readouts]);
    ktraj_adc=ktraj_adc(:,:,1:2:end-1);
    %rawdata=rawdata(:,1:2:end-1,:);
    rawdata=rawdata(:,1:2:end-1,:)+rawdata(:,2:2:end,:);
    readouts=readouts/2;
    ktraj_adc=reshape(ktraj_adc,[3,adc_len*readouts]);
    % make it 2D
    ktraj_adc(3,:)=0;
    % reshape raw data to 4D (1 samples 1 channels) as BART needs it
    rawdata=reshape(rawdata,[1 adc_len*readouts 1 channels]);
else
    % just a general reshape to 4D (1 samples 1 channels) as BART needs it
    rawdata = reshape(rawdata, [1 adc_len*readouts 1 channels]);
end

%% BART expects the trajectory in units of 1/FOV, Pulseq's trajectory is in inverse meters, need to rescale
for i=1:3, ktraj_adc(i,:)=ktraj_adc(i,:)*fov(i); end

% left-right swap (tested on TRIO)
ktraj_adc(1,:)=-ktraj_adc(1,:);

%% detect and clean-up 2D trajectory
kmax=max(abs(ktraj_adc'));
klim_almost_zero=1e-3; % why?
if any(kmax<klim_almost_zero)
    fprintf('2D trajectory detected\n');
    ktraj_adc(kmax<klim_almost_zero,:)=0;
end

%% calculate signal power in respective channels
datapwr=squeeze(sum(rawdata.*conj(rawdata),2));
[~,pwrsort]=sort(-datapwr);

%% call bart for the actual recon -- first try inverse gridding
igrid = bart('nufft -i -t -lowmem1', ktraj_adc, rawdata); % inverse interative gridding (-i) % -lowmem1 may be of help with large data
save([basic_file_path '_recon_igrid'],'igrid');
% display (needs imab for displaying multiple slices/partitions)
figure; imab(sum(abs(igrid).^2,4).^0.5); colormap gray;
title('BART recon, inverse gridding'); drawnow;

%% call bart for the actual recon -- now try iterative non-linear inversion which also uses multiple coils in a parallel imaging way
nlinv = bart('nlinv -n -d4 -m1 -i16 -t', ktraj_adc, rawdata); % there is a -R<n> key, but I did not notice any effect
%nlinv = bart('nlinv -n -d4 -m1 -i16 -t', ktraj_adc, rawdata(:,:,:,pwrsort(1:3))); % only include N most important channels (to save time and/or RAM)
save([basic_file_path '_recon_nlinv'],'nlinv');
figure; imab(abs(nlinv)/max(abs(nlinv(:)))); colormap gray;
title('BART recon, nonlinear inversion'); drawnow;

%% call bart for the pics algorithm with compressed sensing (with dummy coil sensitivities)
pics = bart('pics -S -l2 -r5 -t', ktraj_adc, rawdata, ones(size(igrid))); % inverse interative gridding (-i) % -lowmem1 may be of help with large data
%pics_l1 = bart('pics -S -l1 -i100 -s0.0002 -r0.003 -t', ktraj_adc, rawdata, ones(size(igrid))); % inverse interative gridding (-i) % -lowmem1 may be of help with large data
% tested r 0.002 0.004 and 0.01 (l1, l1s or ++, l1ss or +++)
save([basic_file_path '_recon_pics'],'pics');
% display (needs imab for displaying multiple slices/partitions)
figure; imab(sum(abs(pics).^2,4).^0.5); colormap gray;
title('BART recon, pics-l2'); drawnow;

% %%
% pics_l2 = bart('pics -S -l2 -r5 -t', ktraj_adc, rawdata, ones(size(igrid))); % inverse interative gridding (-i) % -lowmem1 may be of help with large data
% save([basic_file_path '_recon_picsL2'],'pics_l2');
% % display (needs imab for displaying multiple slices/partitions)
% figure; imab(sum(abs(pics_l2).^2,4).^0.5); colormap gray; drawnow;

%% 3D viewer
% img3d=abs(pics_l1(:,:,end:-1:1));
% %img3d=imag(pics);
% %img3d(img3d<0)=0;
% % img3d=sum(abs(igrid).^2,4).^0.5;
%
% s=size(img3d);
% [xx,yy,zz]=ndgrid(-s(1)/2:s(1)/2-1,-s(2)/2:s(2)/2-1,-s(3)/2:s(3)/2-1);
% img3d(vecnorm([xx(:),yy(:),zz(:)]')>min(s)/2)=0;
% figure; imab(permute(img3d(116:140,:,:),[2,3,1])); colormap gray;
% volumeViewer(img3d);