function models=train_svms_box(imnames, feat_dir, region_meta_info, cachedir, run_name, varargin)
%random seed
rng(3);

%input parser to allow additional inputs
p = inputParser;
addOptional(p, 'to_train', [1:20], @isnumeric);
addOptional(p, 'svm_C', 10^-3, @isscalar);
addOptional(p,'bias_mult',10,@isscalar);
addOptional(p, 'pos_loss_weight', 2, @isscalar);
addOptional(p, 'do_latent', 0, @isscalar);
addOptional(p, 'pos_ov_thresh', 0.5, @isscalar);
addOptional(p, 'neg_ov_thresh', 0.3, @isscalar);
addOptional(p, 'hard_neg_thresh', -1.0001, @isscalar);
addOptional(p, 'evict_thresh', -1.2, @isscalar);
addOptional(p, 'retrain_limit', 2000, @isscalar);
parse(p, varargin{:});
opts=p.Results;

to_train=opts.to_train;

%if cachedir does not exist, create it
if(~exist(cachedir, 'file')) mkdir(cachedir); end
cachedir=fullfile(cachedir, run_name);
if(~exist(cachedir, 'file')) mkdir(cachedir); end

%collect feature stats
fprintf('Collecting feature stats...\n');
stats=collect_feature_stats(imnames, feat_dir, cachedir, region_meta_info);

%initialize caches
for i=1:numel(to_train)
    caches(i) = init_cache;
    models(i).categid=to_train(i);
    models(i).w=[];
    models(i).b=[];
    models(i).meannrm=stats.meannrm;
end

%load GT positives
pos_file=fullfile(cachedir, 'pos.mat');
if(exist(pos_file, 'file'))
    tmp=load(pos_file);
    X_pos=tmp.X_pos;
else
    X_pos=get_positives(imnames, feat_dir, to_train, region_meta_info, false, models, opts.pos_ov_thresh);
    save(pos_file, 'X_pos');
end
for i=1:numel(to_train)
    caches(i).X_pos=X_pos{i};
    fprintf('Category %d has %d positives\n', to_train(i), size(X_pos{i},1));
end

%train
first_time=true;
force_update=false;
max_outer_iter=opts.do_latent+1;
for iter=1:max_outer_iter
    if(iter~=max_outer_iter)
        %subsample negatives
        negidx=randperm(numel(imnames));
        negidx=negidx(1:min(1000, numel(negidx)));
    else
        negidx=1:numel(imnames);
    end
    if(iter>1)
        %latent update
        X_pos=get_positives(imnames, feat_dir, to_train, region_meta_info, true, models, opts.pos_ov_thresh);
        for i=1:numel(to_train)
            caches(i).X_pos=X_pos{i};
            fprintf('Latent update: Category %d has %d positives\n', to_train(i), size(X_pos{i},1));

        end
        force_update=true;
    end
    %for every negative
    for i=1:numel(negidx)
        fprintf('Hard negatives iter: %d, image number: %d/%d\n',iter, i, numel(negidx));
        [X_neg, curr_keys]=get_hard_negatives(negidx(i), imnames, feat_dir, to_train,...
                             region_meta_info, models, {caches.keys}, opts.neg_ov_thresh, opts.hard_neg_thresh, first_time);
       very_last_iter=(iter==max_outer_iter) && (i==numel(negidx));
       for j=1:numel(to_train)
            caches(j).X_neg=cat(1, caches(j).X_neg,X_neg{j});
            caches(j).keys=cat(1, caches(j).keys, curr_keys{j});
            caches(j).num_added=caches(j).num_added+size(X_neg{j},1);
            if(caches(j).num_added>opts.retrain_limit || first_time ||very_last_iter || force_update)
                %time to retrain
                fprintf('Retraining category %d with %d positives and %d negatives\n', to_train(j), size(caches(j).X_pos,1), size(caches(j).X_neg,1));
                models(j)=update_model(models(j), caches(j), opts.svm_C, opts.bias_mult, opts.pos_loss_weight);
                [reg, posloss, negloss, neg_scores]=compute_obj_val(models(j),...
                                                   caches(j), opts.svm_C, opts.pos_loss_weight);
                caches(j).pos_loss(end+1)=posloss;
                caches(j).neg_loss(end+1)=negloss;
                caches(j).reg_loss(end+1)=reg;
                caches(j).tot_loss(end+1)=posloss+negloss+reg;
                for t=1:numel(caches(j).tot_loss)
                    fprintf('%d: %f = %f + %f + %f\n',t,caches(j).tot_loss(t),...
                    caches(j).reg_loss(t),caches(j).pos_loss(t), caches(j).neg_loss(t));
                end

                %evict
                keep=neg_scores>opts.evict_thresh;
                caches(j).X_neg=caches(j).X_neg(keep,:);
                caches(j).keys=caches(j).keys(keep,:);
                fprintf('Kept: %d negatives \n', sum(keep));
                caches(j).num_added=0;
            end

        end
        first_time=false;
        force_update=false;
    end
end

%--------------------------------
%   Feature stats
%--------------------------------
function stats = collect_feature_stats(imnames, feat_dir, cachedir, region_meta_info)
stat_file=fullfile(cachedir, 'stats.mat');
if(exist(stat_file, 'file'))
    tmp=load(stat_file);
    stats=tmp.stats; clear tmp;
else

    num_images=100;
    regs_per_img=100;
    ri=randperm(numel(imnames));
    ri=ri(1:num_images);
    totalnrm=0;
    totalcnt=0;
    for i=1:num_images
        fprintf('%d/%d\n', i, num_images);

        %load random image
        tmp=load(fullfile(feat_dir, [imnames{ri(i)} '.mat']));

        %pick random regions
        num=min(regs_per_img, region_meta_info.num_regs(ri(i)));
        ri2=randperm(region_meta_info.num_regs(ri(i)));
        ri2=ri2(1:num);

        %get norm
        nrm=sqrt(sum(tmp.feats(ri2,:).^2,2));
        totalnrm=totalnrm+sum(nrm);
        totalcnt=totalcnt+num;
    end
    stats.meannrm=totalnrm./totalcnt;
    save(stat_file, 'stats');
end
fprintf('\n\nMean norm=%f\n\n',stats.meannrm);

%------------------------------
%   Init cache
%------------------------------
function cache=init_cache
cache.X_pos = single([]);
cache.X_neg = single([]);
cache.keys = [];
cache.num_added = 0;
cache.retrain_limit = 2000;
cache.evict_thresh = -1.2;
cache.hard_thresh = -1.0001;
cache.pos_loss = [];
cache.neg_loss = [];
cache.reg_loss = [];
cache.tot_loss = [];

%------------------------------
%   Get positives from image
%------------------------------

function X_pos=get_positives(imnames, featdir, to_train, region_meta_info, islatent, models, pos_ov_thresh)
for k=1:numel(to_train)
    X_pos{k}=single([]);
end

for i=1:numel(imnames)
    fprintf('Positives: %d/%d\n',i, numel(imnames));
    tmp=load(fullfile(featdir, [imnames{i} '.mat']));
    feats=xform_feat(tmp.feats, models(1).meannrm);

    %for every category
    for j=1:numel(to_train)
        sel=[];
        if(islatent)
            %latent update: consider all features greater than thresh
            %find gt for this category and get corresponding overlaps
            gtidx=find(region_meta_info.gt{i}==to_train(j));
            nongt=find(region_meta_info.gt{i}==0);

            ovs=region_meta_info.box_overlaps{i}(gtidx,:);
            overlapping=find(max(ovs,[],1)>=pos_ov_thresh);
            ovs=ovs(:,overlapping);

            scores=feats*models(j).w+models(j).b;

            %for every gt, find the highest scoring gt that overlaps by >70
            for k=1:numel(gtidx)
                ovs_this=ovs(k,:);
                idx=find(ovs_this>pos_ov_thresh);
                if(isempty(idx)) continue; end
                [m, argmax]=max(scores(nongt(overlapping(idx))));
                sel=[sel nongt(overlapping(idx(argmax)))];
            end
        else
            sel=find(region_meta_info.gt{i}==to_train(j));
        end
        X_pos{j}=cat(1, X_pos{j},feats(sel,:));
    end
end

%------------------------------
%   Hard negatives
%------------------------------

function [X_neg, curr_keys]=get_hard_negatives(i, imnames, featdir, to_train, region_meta_info, models, keys, neg_ov_thresh, score_thresh, first_time)
tmp=load(fullfile(featdir, [imnames{i} '.mat']));
feats=xform_feat(tmp.feats, models(1).meannrm);

if(~first_time)
    %concatenate all models
    w=cat(2,models(:).w);
    b=cat(2,models(:).b);

    %compute all scores
    scores=bsxfun(@plus,feats*w, b);
else
    scores=(score_thresh+1)*ones(size(feats,1), numel(models));
end

%for every category
for j=1:numel(to_train)
    %get the groundtruths that don't belong to this category
    wronggtidx=find(region_meta_info.gt{i}~=to_train(j) & region_meta_info.gt{i}~=0);
    %get the non-groundtruths that overlap by less than threshold

    nongt=find(region_meta_info.gt{i}==0);
    gtidx=find(region_meta_info.gt{i}==to_train(j));
    if(~isempty(gtidx))
        idx=find(max(region_meta_info.box_overlaps{i}(gtidx,:),[],1)<neg_ov_thresh);
        nongtidx=nongt(idx);
    else
        nongtidx=nongt;
    end
    allidx=[wronggtidx(:); nongtidx(:)];
    allidx=allidx(scores(allidx,j)>=score_thresh);

    %get the keys for each of these
    tmp_keys=[i*ones(numel(allidx),1) allidx];

    %check that duplicate keys are not added
    keep=~ismember(tmp_keys, keys{j}, 'rows');

    %add these feats
    X_neg{j}=feats(allidx(keep),:);
    curr_keys{j}=tmp_keys(keep,:);
end

%------------------------------
%   Update model
%------------------------------
function model=update_model(model, cache, svm_c, bias_mult, pos_weight)
opts = sprintf('-w1 %.10f -c %.10f -s 3 -B %.10f', ...
                      pos_weight, svm_c, bias_mult);

X=cat(2, cache.X_pos', cache.X_neg');
y=[ones(size(cache.X_pos,1),1); zeros(size(cache.X_neg,1),1)];
fprintf('calling liblinear with:%s\n',opts);
llm = liblinear_train(y, sparse(double(X)), opts, 'col');
model.w = single(llm.w(1:end-1)');
model.b = single(llm.w(end)*bias_mult);

%---------------------------
% Compute objective value
%---------------------------
function [reg, posloss, negloss, neg_scores]=compute_obj_val(model,cache, svm_c, pos_weight)
pos_scores=cache.X_pos*model.w+model.b;
neg_scores=cache.X_neg*model.w+model.b;
posloss=sum(max(1-pos_scores,0))*svm_c*pos_weight;
negloss=sum(max(1+neg_scores,0))*svm_c;
reg=0.5*model.w'*model.w;

%---------------------------
% xform_feat
%---------------------------
function feats=xform_feat(feats, nrm)
feats=feats*20./nrm;


function bbox_reg = trans_bbox_reg(bbox_reg, use_gpu)
% translate bbox_reg model for fast compute
%
% AUTORIGHTS
% ---------------------------------------------------------
% Copyright (c) 2014, Shaoqing Ren
%
% This file is part of the SPP code and is available
% under the terms of the Simplified BSD License provided in
% LICENSE. Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

    for i = 1:length(bbox_reg.models)
        bbox_reg.models{i}.Beta = bbox_reg.models{i}.Beta';
        if use_gpu
            bbox_reg.models{i}.Beta_gpu = gpuArray(bbox_reg.models{i}.Beta);
        end
    end

    bbox_reg.combined_Beta = cell2mat(cellfun(@(x) x.Beta', bbox_reg.models', 'UniformOutput', false))';
    if use_gpu
        bbox_reg.combined_Beta_gpu = gpuArray(bbox_reg.combined_Beta);
    end
    bbox_reg.combined_T_inv = cell2mat(cellfun(@(x) x.T_inv, bbox_reg.models, 'UniformOutput', false));
end


function     [position,janelas,messages]= twavef_detector(heasig,samp,time,position,w,intervalo,messages)
% script which identifies T wave, as well as its onset and offset
% implementation of changes to improve FN and reduce mean error utilizando a escala 5
%
%
%Input Parameters:
%   heasig: struct vector with header information
%   samp: samples included in the current excerpt (borders excluded)
%   time:  QRS times in inedexes refering the interval included in the current excerpt (borders excluded)
%   position: struct vector with the detected points
%   w: matrix with WT scales 1 to 5
%   intervalo: numeration of the beats processed in this segment
%
%Output Parameters:
%   janelas: T wave seach windows
%   actualized parameters: position
%
% Rute Almeida
% based on twave.m by Juan Pablo Mart�nez Cort�s
% Last update: Rute Almeida  07FEB2012
%
% 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 2
% 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, write to the Free Software
% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
%
% Designed for MATLAB Version R12; tested with MATLAB Version R13
%
%%%%%% Constants and Thresholds  !!!!!!!!!!!!!!!!!!!!!!!!!
if ~isfield(messages.setup.wavedet,'Kton')
    messages.setup.wavedet.Kton = 4;      % 2 4!
end
if ~isfield(messages.setup.wavedet,'Ktoff')
    messages.setup.wavedet.Ktoff = 2.5;% 3.5 3!
end
if ~isfield(messages.setup.wavedet,'umbraldetT')
    messages.setup.wavedet.umbraldetT = 0.25;  % We use umbraldet*sqrt(mean(w(time(i):time(i+1),4).^2))
end
if ~isfield(messages.setup.wavedet,'umbralsig')
    messages.setup.wavedet.umbralsig  =  1/8;    % To decide if there are really 2, 1 or no peak
    %threshold to decide if there is or not a significative
    % T wave.3!
end
if ~isfield(messages.setup.wavedet,'rrant_mintol')
    messages.setup.wavedet.rrant_mintol  =  0.5;    % minimum time in sec admited for current RR to be used in T wave delineation or in Exponentially averaged RR
end
if ~isfield(messages.setup.wavedet,'rrant_maxtol')
    messages.setup.wavedet.rrant_maxtol  =  1.5;    % max time in sec admited for current RR to be used in Exponentially averaged RR
end
if ~isfield(messages.setup.wavedet,'rrant_average')
    messages.setup.wavedet.rrant_average  =  [0.8 0.2];    % Exponentially averaged RR weights
end
if ~isfield(messages.setup.wavedet,'inivent_tol')
    messages.setup.wavedet.inivent_tol  =  0.1;
end
if ~isfield(messages.setup.wavedet,'inivent_tol_S')
    messages.setup.wavedet.inivent_tol_S=0.05; % sec
end
if ~isfield(messages.setup.wavedet,'finvent_tol')
    messages.setup.wavedet.finvent_tol=0.240;% sec % it would be nioce to make it depend on round(rrmed/2)
end
if ~isfield(messages.setup.wavedet,'finvent_max')
    messages.setup.wavedet.finvent_max=0.6;% sec
end
if ~isfield(messages.setup.wavedet,'scale')
    messages.setup.wavedet.scale=4;
end
if ~isfield(messages.setup.wavedet,'scale2')
    messages.setup.wavedet.scale2=5;
end
if ~isfield(messages.setup.wavedet,'scalezerocros')
    messages.setup.wavedet.scalezerocros=3;
end
if ~isfield(messages.setup.wavedet,'scalezerocros2') %for twavetask5
    messages.setup.wavedet.scalezerocros2=4;
end

if ~isfield(messages.setup.wavedet,'min_vent')
    messages.setup.wavedet.min_vent=0.1;%sec
end
if ~isfield(messages.setup.wavedet,'Tmax_Tmin_time_min')
    messages.setup.wavedet.Tmax_Tmin_time_min=0.2;%0.15;%sec %10 ENE2012: 150 ms es poco
end
if ~isfield(messages.setup.wavedet,'Tmax_Tmin_bifasic')
    messages.setup.wavedet.Tmax_Tmin_bifasic=2.5;
end
if ~isfield(messages.setup.wavedet,'T_bound_tol')
    messages.setup.wavedet.T_bound_tol=0.12;%sec
end
Kton=messages.setup.wavedet.Kton;
Ktoff=messages.setup.wavedet.Ktoff;
umbraldetT = messages.setup.wavedet.umbraldetT;
umbralsig = messages.setup.wavedet.umbralsig;
rrant_mintol=messages.setup.wavedet.rrant_mintol;
rrant_maxtol=messages.setup.wavedet.rrant_maxtol;
rrant_average=messages.setup.wavedet.rrant_average;
inivent_tol= messages.setup.wavedet.inivent_tol;
inivent_tol_S=messages.setup.wavedet.inivent_tol_S;
finvent_tol=messages.setup.wavedet.finvent_tol;
finvent_max= messages.setup.wavedet.finvent_max;
scale=messages.setup.wavedet.scale;
scale2=messages.setup.wavedet.scale2;
scalezerocros=messages.setup.wavedet.scalezerocros;
scalezerocros2=messages.setup.wavedet.scalezerocros2; %#ok<NASGU>
min_vent=messages.setup.wavedet.min_vent;%sec
Tmax_Tmin_time_min= messages.setup.wavedet.Tmax_Tmin_time_min;%sec
Tmax_Tmin_bifasic=messages.setup.wavedet.Tmax_Tmin_bifasic;% extra criteria
T_bound_tol=messages.setup.wavedet.T_bound_tol;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isempty(heasig)

end
% janelas=[];
% endwinT=[];

% Initialization of auxiliary variables
if length(time)>1
    rrant = time(2)-time(1);
else
    aux=find(position.qrs~=0);
    if length(aux)>1
        rrant= diff(position.qrs(aux(end-1):aux(end))); %18MAR09
    else
        rrant=messages.setup.wavedet.freq;
    end
end

%18MAR09 % for the case in which the first RR is very wrong AGO2011
if rrant<rrant_mintol*messages.setup.wavedet.freq || rrant>rrant_maxtol*messages.setup.wavedet.freq;
    messages.warnings=[messages.warnings {['RR lower than' rrant_mintol*messages.setup.wavedet.freq ' ms or higher than' rrant_maxtol*messages.setup.wavedet.freq ' found in twavef']}];
    rrant=rrant_mintol*messages.setup.wavedet.freq;%Jul2011
end

T = []; Tprima=[]; picon=[]; picoff=[]; Ton=[]; Toff=[]; tipoT=[];
% minapos = []; minppos=[]; maxapos=[]; maxppos=[]; mina=[]; minp=[]; maxa=[];
% maxp=[];
picon_keep=[];%multileadchange
picoff_keep=[];%multileadchange
lead_keep=[];%multileadchange

%figure
%%%plot(messages.lixo)
% hold on
janelas=NaN*ones(length(time),3);
endwinT=NaN*ones(length(time),1);
for i = 1:length(time)   % For each beat

    if (i>1),
        if (rrant_maxtol*rrant>(time(i)-time(i-1)))&&(time(i)-time(i-1)>rrant_mintol*rrant),
            rrmed = rrant_average(1)*rrant + rrant_average(2)*(time(i)-time(i-1));% Exponentially averaged RR new value
        else
            rrmed = rrant;  % Exponentially averaged RR old value
        end
    else                  % Only for the first in each segment of the ECG
        rrmed = rrant;
    end
    rrant = rrmed;        % For next segment
    inivent = round(inivent_tol*messages.setup.wavedet.freq);   % Begining of window

    %Rute multilead 03.Dec.04

    %if ~isempty(pos.S(i)),              % If there is an S wave
    %         inivent = max(inivent, pos.S(i)-pos.qrs(i)+round(0.05*messages.setup.wavedet.freq));
    %     end

    if ~isempty(position.S(i+intervalo(1)-1));
        inivent = max(inivent, position.S(i+intervalo(1)-1)-samp(1)+1-time(i)+round(inivent_tol_S*messages.setup.wavedet.freq));
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% changed 13/06/02 Rute
    %if rrmed >= messages.setup.wavedet.freq,  %
    %if rrmed <= messages.setup.wavedet.freq,  %   21AGO09 % for rrmed < 1 seg finiven=finvent_max seg
    if rrmed <= messages.setup.wavedet.freq && i~=length(time) && (rrant_maxtol*rrant>(time(i+1)-time(i)))&&(time(i+1)-time(i)>rrant_mintol*rrant) %02FEB2011
        finvent = round(finvent_max*messages.setup.wavedet.freq);     % End of window
    else
        finvent = round(rrmed*finvent_max);
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %% changed 13/06/02 Rute
    if i~=length(time),                 % For last beat in the segment
        %finvent = min(finvent,time(i+1)-time(i)-round(finvent_tol*messages.setup.wavedet.freq));
        %finvent = min(finvent,time(i+1)-time(i)-min(round((time(i+1)-time(i))*1/3),round(finvent_tol*messages.setup.wavedet.freq)));
        finvent = min(finvent,time(i+1)-time(i)-min(round((time(i+1)-time(i))*0.4),round(finvent_tol*messages.setup.wavedet.freq)));
    elseif i~=1%Rute 18MAR09
        finvent = min(finvent,time(i)-time(i-1)-round(finvent_tol*messages.setup.wavedet.freq));
    elseif length(aux)>1
        finvent = min(finvent,diff(position.qrs(aux(end-1):aux(end)))-round(finvent_tol*messages.setup.wavedet.freq));%Rute 18MAR09
    end

    % We work at scale 4 (in general).
    if isempty(position.QRSoff(i+intervalo(1)-1)),   % Should never happen, but...
        begwin = min(inivent + time(i),length(w)); %Rute 4Jul2011
    else
        begwin = min(max(inivent + time(i), position.QRSoff(i+intervalo(1)-1)-samp(1)+1+1),length(w));%Rute 4Jul2011
        % if QRSoff is bad anoated it can miss T wave...
    end

    endwin = max(1,min(finvent + time(i),length(w))); %% Rute 20/05/02
    janelas(i,:)=[i begwin endwin];
    endwinT(i)=endwin;
    %%%%plot([begwin endwin ], messages.lixo([begwin endwin]),'*g')

    % Positive maxima and negative minima in the window
    maxpos = begwin + modmax(w(begwin+1:endwin,scale),2,0,+1);
    minpos = begwin + modmax(w(begwin+1:endwin,scale),2,0,-1);
    [maxim ind] = max(w(maxpos,scale));   % The biggest of the positive
    maxpos = maxpos(ind);
    [minim ind] = min(w(minpos,scale));   % The biggest of the negative
    minpos = minpos(ind);

    if isempty(maxpos),               % If no local positive maximum
        % The maximum will be the first
        % or the last sample
        if (w(begwin,scale)>=w(endwin,scale)) && w(begwin,scale)>0,
            maxpos = begwin; maxim = w(maxpos,scale);
        elseif (w(endwin,scale)>=w(begwin,scale)) && w(endwin,scale)>0,
            maxpos = endwin; maxim = w(maxpos,scale);
        end
    end
    if isempty(minpos),               % if no local negative minimum
        % the minimum will be the first
        % or the last sample
        if (w(begwin,scale)<=w(endwin,scale)) && w(begwin,scale)<0,
            minpos = begwin; minim = w(minpos,scale);
        elseif (w(endwin,scale)<=w(begwin,scale)) && w(endwin,scale)<0,
            minpos = endwin; minim = w(minpos,scale);
        end
    end

    absmax = abs(maxim);
    absmin = abs(minim);

    if i<length(time),
        veficaz = sqrt(mean(w(time(i):time(i+1),scale).^2));
    else                        % if last beat of the segment
        veficaz = sqrt(mean(w(time(i):end,scale).^2));
    end

    hay_onda = ((absmax>umbraldetT*veficaz)|(absmin>umbraldetT*veficaz));
    if isempty(hay_onda)
        hay_onda = 0;
    end
    % Rute 18/06/02

    if endwin-begwin<(min_vent*messages.setup.wavedet.freq) || isnan(hay_onda)% se a janela tem amplitude menor do que min_vent seg enato nao existe onda T
        hay_onda =0;
    end

    % Is there a wave?
    if hay_onda,
        if absmax >= absmin,    % the greatest modulus maximum is the maximum
            % Now we search the two minima nearest to maxpos, one before and one after
            minapos = max(modmax(w(begwin+1:maxpos-1,scale),2,0,-1));
            minapos = begwin + minapos; % Position of the negative minimum before the maximum
            if isempty(minapos) && (maxpos ~= begwin) && (w(begwin,scale)<0),
                minapos = begwin;% If no local minimum before the maximum, take the first sample
            end
            minppos = min(modmax(w(maxpos+1:endwin,scale),2,0,-1));
            minppos = maxpos + minppos; % Position of the positive maximum after the minimum
            if isempty(minppos) && (maxpos ~= endwin) && (w(endwin,scale)<0),
                minppos = endwin;        % If no local minimum after the maximum, take the last sample
            end

            mina = abs(w(minapos,scale));     % Amplitude of minimum before maximum
            minp = abs(w(minppos,scale));     % Amplitude of minimum after maximum

            if (mina < umbralsig*absmax), % If mina is not big enough
                mina =[];                  % forget it

            elseif (maxpos-minapos>Tmax_Tmin_time_min*messages.setup.wavedet.freq), %or if ther are more than Tmax_Tmin_time_min sec to maxpos
                mina = [];
            end
            if (minp < umbralsig*absmax), % If minp is not big enough
                minp =[];                  % forget it
            elseif (minppos-maxpos>Tmax_Tmin_time_min*messages.setup.wavedet.freq), % and also if there are more than Tmax_Tmin_time_min sec to maxpos
                minp =[];
            end

            if ~isnan(mina)&~isnan(minp), %#ok<AND2>     %%% NUEVO JP
                if (mina >= minp)&&(minp < umbralsig*absmax*Tmax_Tmin_bifasic),
                    minp = [];
                elseif (minp> mina)&&(mina < umbralsig*absmax*Tmax_Tmin_bifasic),
                    mina = [];
                end
            end

            % Test which modulus maxima are significative and find zero crossings
            if isempty(mina),
                if isempty(minp),
                    tipoT = 2;      % only upwards T wave
                    if maxpos - minapos > 2,         % if not !!!!!!!!!!!
                        ind = zerocros(flipud(w(minapos:maxpos,scalezerocros)));   %Scale scalezerocros !!!
                        T = maxpos - ind +1;                           % Zero crossing = T wave position
                        picoff = maxpos;                               %wavelet  peak to detect offset
                    elseif isempty(minapos);  % If there were no minimum, there is no zero crossing
                        T = picant (w(begwin:maxpos,scale),maxpos);       % Take the minimum at scale scale
                        if ~isempty(T) %%%%%%%%%%%% 14/06/02 Rute
                            picoff = maxpos;
                        else
                            picoff = []; % if did not exist a peak in scale scale there is no T
                        end %%%%%%%%%%%% 14/06/02 Rute
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave' }];
                    end
                else      % minp exists (is significative) but mina not
                    tipoT = 0;  		%normal T wave
                    if minppos -maxpos >2,       % if not!!!!!!???
                        ind = zerocros(w(maxpos:minppos,scalezerocros));  %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(w(maxpos:minppos,scale));
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = maxpos + ind -1;
                        picon = maxpos;		% For determining onset and offset
                        picoff = minppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            else
                if isempty(minp),   %  mina exists (is significative) but minp not
                    tipoT = 1;  	%inverted T wave
                    if maxpos -minapos >2,  % if not !!!!!!!!!
                        ind = zerocros(w(minapos:maxpos,scalezerocros));  % wavelet zero crossing is T wave peak  %% 07/06/02 Rute
                        if isempty(ind)%%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(w(minapos:maxpos,scale));  % wavelet zero crossing is T wave peak  %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = minapos + ind -1;
                        picon = minapos;
                        picoff = maxpos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                else    % both mina and minp are significative.  Biphasic wave.
                    tipoT = 5;	% biphasic neg-pos T wave
                    if maxpos - minapos > 2,    %!!!!!!!!!!!!
                        ind = zerocros(flipud(w(minapos:maxpos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(flipud(w(minapos:maxpos,scale))); %% 03/09/02 Rute

                        end%%%%%%%%%%%%%%Rute 03/09/02
                        T = maxpos - ind +1;
                        picon = minapos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                    if minppos - maxpos > 2,    %!!!!!!!!!!!!
                        ind = zerocros(flipud(w(maxpos:minppos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind)%%%%%%%%%%%%%%Rute 03/09/02
                            %ind = zerocros(flipud(w(minapos:maxpos,scale))); %% 03/09/02 Rute %18.11.05 DUVIDA
                            %DUVIDA 18Nov05
                            ind = zerocros(flipud(w(maxpos:minppos,scale))); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        %Tprima = maxpos + ind -1;
                        %DUVIDA 18Nov05
                        Tprima = minppos- ind +1;
                        picoff = minppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            end
        else        % If the greatest modulus maximum is the minimum
            % Search two maxima, one before and one after the minimum
            maxapos = max(modmax(w(begwin+1:minpos-1,scale),2,0,1));
            maxapos = begwin + maxapos;
            if isempty(maxapos) && (minpos ~= begwin) && (w(begwin,scale)>0),
                maxapos = begwin;
            end
            maxppos = min(modmax(w(minpos+1:endwin,scale),2,0,1));
            maxppos = minpos + maxppos;
            if isempty(maxppos) && (minpos ~= endwin) && (w(endwin,scale)>0),
                maxppos = endwin;
            end
            maxa = abs(w(maxapos,scale));
            maxp = abs(w(maxppos,scale)) ;                                   % See if they are significative
            if (maxa < umbralsig*absmin)
                maxa =[];
            elseif (minpos-maxapos>Tmax_Tmin_time_min*messages.setup.wavedet.freq),
                maxa = [];
            end
            if (maxp < umbralsig*absmin),
                maxp =[];
            elseif (maxppos-minpos>Tmax_Tmin_time_min*messages.setup.wavedet.freq),
                maxp = [];
            end
            if ~isnan(maxa)&~isnan(maxp), %#ok<AND2>     %%% NUEVO JP
                if (maxa >= maxp)&&(maxp < umbralsig*absmin*Tmax_Tmin_bifasic),
                    maxp = [];
                elseif (maxp> maxa)&&(maxa < umbralsig*absmin*Tmax_Tmin_bifasic),
                    maxa = [];
                end
            end
            % Test which modulus maxima are significative and find zero crossings
            if isempty(maxa),
                if isempty(maxp),
                    tipoT = 3;      % only downwards T wave
                    if minpos - maxapos > 2, %!!!!!!!!
                        ind = zerocros(flipud(w(maxapos:minpos,scalezerocros)));   %Scale scalezerocros
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(flipud(w(maxapos:minpos,scale)));
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = minpos - ind +1;
                        picoff = minpos;
                    elseif isempty(maxapos);  % If there were no maximum, there is no zero crossing.
                        T = picant (w(begwin:minpos,scale),minpos);  % minimo en escala scale.
                        if ~isempty(T) %%%%%%%%%%%% 14/06/02 Rute
                            picoff = minpos;
                        else
                            picoff = []; % if did not exist a peak in scale scale there is no T
                        end %%%%%%%%%%%% 14/06/02 Rute
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                else      % maxp is signficative, but not maxa
                    tipoT = 1;  %inverted T wave
                    if maxppos -minpos >2,  % !!!!!!
                        ind = zerocros(w(minpos:maxppos,scalezerocros));  %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(w(minpos:maxppos,scale));  %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = minpos + ind -1;
                        picon = minpos;		% For calculating onset and offset
                        picoff = maxppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            else
                if isempty(maxp),   % maxa is significative, but not maxp
                    tipoT = 0;       %normal T wave
                    if minpos -maxapos >2,
                        ind = zerocros(w(maxapos:minpos,scalezerocros)); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                            ind = zerocros(w(maxapos:minpos,scale)); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                        T = maxapos + ind -1;
                        picon = maxapos;
                        picoff = minpos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                else    % both maxa and maxp are significative.  Biphasic wave.
                    tipoT = 4;	% biphasic pos-neg T wave
                    if minpos - maxapos > 2,  %!!!!!!!!!!!
                        ind = zerocros(flipud(w(maxapos:minpos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                            ind = zerocros(flipud(w(maxapos:minpos,scale))); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                        T = minpos - ind +1;
                        picon = maxapos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                    if maxppos - minpos > 2,    %!!!!!!!!!!!!
                        ind = zerocros(flipud(w(minpos:maxppos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                            %ind = zerocros(flipud(w(minapos:maxpos,scale))); %% 03/09/02 Rute %18.11.05 DUVIDA
                            %DUVIDA 18Nov05
                            ind = zerocros(flipud(w(minpos:maxppos,scale))); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                        %Tprima = minpos + ind -1; %18.11.05 DUVIDA
                        %DUVIDA 18Nov05
                        Tprima = maxppos- ind +1;
                        picoff = maxppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            end
        end

        % T wave onset and offset detection
        %if isempty(T), picon=[]; picoff=[]; end

        picon_keep=[picon_keep picon]; %#ok<AGROW> %multileadchange
        picoff_keep=[picoff_keep picoff];  %#ok<AGROW> %multileadchange
        lead_keep=[lead_keep scale];  %#ok<AGROW> %multileadchange

        if ~isempty(picon),
            Ton = searchon (picon, w(max(begwin,picon-round(T_bound_tol*messages.setup.wavedet.freq)):picon,scale), Kton);
        end
        if ~isempty(picoff),
            Toff=searchoff(picoff, w(picoff:min([size(w,1) picoff+round(T_bound_tol*messages.setup.wavedet.freq) ]) ,scale) , Ktoff);
            if (Toff > endwin),
                Toff = endwin;
            end
        end
    else
        %% using scale 5 07/06/02 Rute %%%%%%%%%%%%%%%%%%%%%%%%%%%
        %% Neste momento usa-se a mesma janela e os mesmos limiares que para a escala scale

        maxpos = begwin + modmax(w(begwin+1:endwin,scale2),2,0,+1);
        minpos = begwin + modmax(w(begwin+1:endwin,scale2),2,0,-1);
        [maxim ind] = max(w(maxpos,scale2));   % The biggest of the positive
        maxpos = maxpos(ind);
        [minim ind] = min(w(minpos,scale2));   % The biggest of the negative
        minpos = minpos(ind);
        if isempty(maxpos),               % If no local positive maximum
            % The maximum will be the first
            % or the last sample
            if (w(begwin,scale2)>=w(endwin,scale2)) && w(begwin,scale2)>0,
                maxpos = begwin; maxim = w(maxpos,scale2);
            elseif (w(endwin,scale2)>=w(begwin,scale2)) && w(endwin,scale2)>0,
                maxpos = endwin; maxim = w(maxpos,scale2);
            end
        end
        if isempty(minpos),               % if no local negative minimum
            % the minimum will be the first
            % or the last sample
            if (w(begwin,scale2)<=w(endwin,scale2)) && w(begwin,scale2)<0,
                minpos = begwin; minim = w(minpos,scale2);
            elseif (w(endwin,scale2)<=w(begwin,scale2)) && w(endwin,scale2)<0,
                minpos = endwin; minim = w(minpos,scale2);
            end
        end

        absmax = abs(maxim);
        absmin = abs(minim);
        if i<length(time),
            veficaz = sqrt(mean(w(time(i):time(i+1),scale2).^2));
        else                        % if last beat of the segment
            veficaz = sqrt(mean(w(time(i):end,scale2).^2));
        end
        hay_ondascale2 = ((absmax>umbraldetT*veficaz)|(absmin>umbraldetT*veficaz));

        if endwin-begwin<(min_vent*messages.setup.wavedet.freq) % se a janela tem amplitude menor do que min_vent seg enato nao existe onda T
            hay_ondascale2 =0;
        end
        if hay_ondascale2,
            t5; % procurar na escala scale2 10/07/02 Rute
            picon_keep=[picon_keep picon];%#ok<AGROW> %multileadchange
            picoff_keep=[picoff_keep picoff];%#ok<AGROW>%multileadchange
            lead_keep=[lead_keep scale2];%#ok<AGROW>%multileadchange
        end
    end
    % Is there a wave
    %% utilizar a escala scale2 07/06/02 Rute %%%%%%%%%%%%%%%%%%%%%%%%%%%

    % Filling the structure with positions
    if isempty(Ton), Ton = NaN; end;
    if isempty(Toff), Toff = NaN;  end;
    if isempty(T), T=NaN; end;
    if isnan(T),
        picon_keep=[picon_keep NaN]; %#ok<AGROW>%multileadchange
        picoff_keep=[picoff_keep NaN]; %#ok<AGROW>%multileadchange
        lead_keep=[lead_keep NaN]; %#ok<AGROW>%multileadchange
    end
    if isempty(Tprima), Tprima=NaN; end;
    if isempty(tipoT), tipoT=NaN; end;
    pos.Tscale(i)=scale;
    pos.Ton(i) = Ton;
    pos.Toff(i) = Toff;
    pos.T(i) = T;
    pos.Tprima(i) = Tprima;
    pos.Ttipo(i) = tipoT;
    %%%plot([Toff(~isnan(Toff))], messages.lixo([Toff(~isnan(Toff))]),'*r')
    T = []; Tprima=[]; picon=[]; picoff=[]; Ton=[]; Toff=[]; tipoT=[];
%     minapos = []; minppos=[]; maxapos=[]; maxppos=[]; mina=[]; minp=[]; maxa=[];
%     maxp=[];

end
position.Tscale(intervalo(1):intervalo(2)) = pos.Tscale;
position.Ton(intervalo(1):intervalo(2)) = pos.Ton+samp(1)-1;
position.Toff(intervalo(1):intervalo(2))= pos.Toff+samp(1)-1;
position.T(intervalo(1):intervalo(2)) = pos.T+samp(1)-1;
position.Tprima(intervalo(1):intervalo(2)) = pos.Tprima+samp(1)-1;
position.Ttipo(intervalo(1):intervalo(2)) = pos.Ttipo;


function script_faster_rcnn_demo()
close all;
clc;
clear mex;
clear is_valid_handle; % to clear init_key
run(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'startup'));
%% -------------------- CONFIG --------------------
opts.caffe_version          = 'caffe_faster_rcnn';
opts.gpu_id                 = auto_select_gpu;
active_caffe_mex(opts.gpu_id, opts.caffe_version);

opts.per_nms_topN           = 6000;
opts.nms_overlap_thres      = 0.7;
opts.after_nms_topN         = 300;
opts.use_gpu                = true;

opts.test_scales            = 600;

%% -------------------- INIT_MODEL --------------------
model_dir                   = fullfile(pwd, 'output', 'faster_rcnn_final', 'faster_rcnn_VOC0712_vgg_16layers'); %% VGG-16
%model_dir                   = fullfile(pwd, 'output', 'faster_rcnn_final', 'faster_rcnn_VOC0712_ZF'); %% ZF
proposal_detection_model    = load_proposal_detection_model(model_dir);

proposal_detection_model.conf_proposal.test_scales = opts.test_scales;
proposal_detection_model.conf_detection.test_scales = opts.test_scales;
if opts.use_gpu
    proposal_detection_model.conf_proposal.image_means = gpuArray(proposal_detection_model.conf_proposal.image_means);
    proposal_detection_model.conf_detection.image_means = gpuArray(proposal_detection_model.conf_detection.image_means);
end

% caffe.init_log(fullfile(pwd, 'caffe_log'));
% proposal net
rpn_net = caffe.Net(proposal_detection_model.proposal_net_def, 'test');
rpn_net.copy_from(proposal_detection_model.proposal_net);
% fast rcnn net
fast_rcnn_net = caffe.Net(proposal_detection_model.detection_net_def, 'test');
fast_rcnn_net.copy_from(proposal_detection_model.detection_net);

% set gpu/cpu
if opts.use_gpu
    caffe.set_mode_gpu();
else
    caffe.set_mode_cpu();
end

%% -------------------- WARM UP --------------------
% the first run will be slower; use an empty image to warm up

for j = 1:2 % we warm up 2 times
    im = uint8(ones(375, 500, 3)*128);
    if opts.use_gpu
        im = gpuArray(im);
    end
    [boxes, scores]             = proposal_im_detect(proposal_detection_model.conf_proposal, rpn_net, im);
    aboxes                      = boxes_filter([boxes, scores], opts.per_nms_topN, opts.nms_overlap_thres, opts.after_nms_topN, opts.use_gpu);
    if proposal_detection_model.is_share_feature
        [boxes, scores]             = fast_rcnn_conv_feat_detect(proposal_detection_model.conf_detection, fast_rcnn_net, im, ...
            rpn_net.blobs(proposal_detection_model.last_shared_output_blob_name), ...
            aboxes(:, 1:4), opts.after_nms_topN);
    else
        [boxes, scores]             = fast_rcnn_im_detect(proposal_detection_model.conf_detection, fast_rcnn_net, im, ...
            aboxes(:, 1:4), opts.after_nms_topN);
    end
end

%% -------------------- TESTING --------------------
im_names = {'001763.jpg', '004545.jpg', '000542.jpg', '000456.jpg', '001150.jpg'};
% these images can be downloaded with fetch_faster_rcnn_final_model.m

running_time = [];
for j = 1:length(im_names)

    im = imread(fullfile(pwd, im_names{j}));

    if opts.use_gpu
        im = gpuArray(im);
    end

    % test proposal
    th = tic();
    [boxes, scores]             = proposal_im_detect(proposal_detection_model.conf_proposal, rpn_net, im);
    t_proposal = toc(th);
    th = tic();
    aboxes                      = boxes_filter([boxes, scores], opts.per_nms_topN, opts.nms_overlap_thres, opts.after_nms_topN, opts.use_gpu);
    t_nms = toc(th);

    % test detection
    th = tic();
    if proposal_detection_model.is_share_feature
        [boxes, scores]             = fast_rcnn_conv_feat_detect(proposal_detection_model.conf_detection, fast_rcnn_net, im, ...
            rpn_net.blobs(proposal_detection_model.last_shared_output_blob_name), ...
            aboxes(:, 1:4), opts.after_nms_topN);
    else
        [boxes, scores]             = fast_rcnn_im_detect(proposal_detection_model.conf_detection, fast_rcnn_net, im, ...
            aboxes(:, 1:4), opts.after_nms_topN);
    end
    t_detection = toc(th);

    fprintf('%s (%dx%d): time %.3fs (resize+conv+proposal: %.3fs, nms+regionwise: %.3fs)\n', im_names{j}, ...
        size(im, 2), size(im, 1), t_proposal + t_nms + t_detection, t_proposal, t_nms+t_detection);
    running_time(end+1) = t_proposal + t_nms + t_detection;

    % visualize
    classes = proposal_detection_model.classes;
    boxes_cell = cell(length(classes), 1);
    thres = 0.6;
    for i = 1:length(boxes_cell)
        boxes_cell{i} = [boxes(:, (1+(i-1)*4):(i*4)), scores(:, i)];
        boxes_cell{i} = boxes_cell{i}(nms(boxes_cell{i}, 0.3), :);

        I = boxes_cell{i}(:, 5) >= thres;
        boxes_cell{i} = boxes_cell{i}(I, :);
    end
    figure(j);
    showboxes(im, boxes_cell, classes, 'voc');
    pause(0.1);
end
fprintf('mean time: %.3fs\n', mean(running_time));

caffe.reset_all();
clear mex;

end

function proposal_detection_model = load_proposal_detection_model(model_dir)
    ld                          = load(fullfile(model_dir, 'model'));
    proposal_detection_model    = ld.proposal_detection_model;
    clear ld;

    proposal_detection_model.proposal_net_def ...
                                = fullfile(model_dir, proposal_detection_model.proposal_net_def);
    proposal_detection_model.proposal_net ...
                                = fullfile(model_dir, proposal_detection_model.proposal_net);
    proposal_detection_model.detection_net_def ...
                                = fullfile(model_dir, proposal_detection_model.detection_net_def);
    proposal_detection_model.detection_net ...
                                = fullfile(model_dir, proposal_detection_model.detection_net);

end

function aboxes = boxes_filter(aboxes, per_nms_topN, nms_overlap_thres, after_nms_topN, use_gpu)
    % to speed up nms
    if per_nms_topN > 0
        aboxes = aboxes(1:min(length(aboxes), per_nms_topN), :);
    end
    % do nms
    if nms_overlap_thres > 0 && nms_overlap_thres < 1
        aboxes = aboxes(nms(aboxes, nms_overlap_thres, use_gpu), :);
    end
    if after_nms_topN > 0
        aboxes = aboxes(1:min(length(aboxes), after_nms_topN), :);
    end
end


function varargout= bbci_acquire_cognionics(varargin)
%BBCI_ACQUIRE_cognionics - Online data acquisition from Cognionics headset
%
%Synopsis:
%  STATE= bbci_acquire_cognionics('init', <PARAM>)
%  [CNTX, MRKTIME, MRKDESC, STATE]= bbci_acquire_cognionics(STATE)
%  bbci_acquire_cognionics('close')
%  bbci_acquire_cognionics('close', STATE)
%
%Arguments:
%  PARAM - Optional arguments
%    'clab', 'fs', 'filtHd', 'blocksize', 'port', 'timeout', 'verbose'
%
%Output:
%  STATE - Structure characterizing the incoming signals; fields:
%     'fs', 'clab', and intern stuff
%  CNTX - 'acquired' signals [Time x Channels]
%    The following variables hold the markers that have been 'acquired' within
%    the current block (if any).
%  MRKTIME - DOUBLE: [1 nMarkers] position [msec] within data block.
%      A marker occurrence within the first sample would give
%      MRKTIME= 1/STATE.fs.
%  MRKDESC - DOUBLE [1 nMarkers] values of markers

% benjamin.blankertz

if isequal(varargin{1}, 'init'),
  state= opt_proplistToStruct(varargin{2:end});
  default_clab= ...
    {'AF5' 'AF3' 'AF1' 'AFz' 'AF2' 'AF4' 'AF6' ...
     'F5' 'F3', 'F1' 'Fz' 'F2' 'F4' 'F6' ...
     'FC7' 'FC5' 'FC3' 'FC1' 'FCz' 'FC2' 'FC4' 'FC6' 'FC8' ...
     'T7' 'C5' 'C3' 'C1' 'Cz' 'C2' 'C4' 'C6' 'T8' ...
     'CP7' 'CP5' 'CP3' 'CP1' 'CPz' 'CP2' 'CP4' 'CP6' 'CP8' ...
     'P7' 'P5' 'P3' 'P1' 'Pz' 'P2' 'P4' 'P6' 'P8' ...
     'PO5' 'PO3' 'PO1' 'POz' 'PO2' 'PO4' 'PO6' ...
     'O5' 'O3' 'O1' 'Oz' 'O2' 'O4' 'O6' ...
    };
  props= {'fs'            250            '!DOUBLE[1]'
          'clab'          default_clab   'CELL{CHAR}'
          'blocksize'     40             '!DOUBLE[1]'
          'port'          'COM11'        '!CHAR'
          'timeout'       3              '!DOUBLE[1]'
          'filtHd'        []             'STRUCT'
          'verbose'       true           '!BOOL'
         };
  [state, isdefault]= opt_setDefaults(state, props, 1);
  if state.fs~=250,
    error('Only fs=250 allowed');
  end
  if isdefault.filtHd,
    % Fs/4
    filt1.b= [0.85 0 0.85]
    filt1.a= [1 0 0.7]
    %Fs/2
    filt2.b= [0.8 0.8]
    filt2.a= [1 0.6]
    state.filtHd= procutil_catFilters(filt1, filt2);
    state.filtHd.PersistentMemory= true;
  end
  state.nChans= length(state.clab);
  state.nBytesPerPacket= 2+3*state.nChans+4;
  nPacketsPerPoll= ceil(state.blocksize/1000*state.fs);
  state.nBytesPerPoll= nPacketsPerPoll*state.nBytesPerPacket;

  % try to find connected cognionics port
  cog_ports= instrfind('Tag', 'Cognionics');
  if isempty(cog_ports),
    state.sp= serial(state.port, 'BaudRate',115200, ...
                                 'Timeout',state.timeout, ...
                                 'Tag', 'Cognionics');
  elseif length(cog_ports)==1,
    state.sp= cog_ports;
    if strcmp(state.sp.Status, 'open'),
      fclose(state.sp);
    end
  else
    error('multiple ports connected to Cognioncs found');
  end
  state.sp.InputBufferSize= 5*state.nBytesPerPoll;

  fopen(state.sp);
  % Poll data several times to get into a more stable state
  for q= 1:3,
    fread(state.sp, state.sp.InputBufferSize, 'uint8');
  end
  state.packetNo=[];
  state.buffer= [];
  state.lastx= zeros(1, state.nChans);
  state.lastMrkDesc= 256;
  state.scale= 1000000/2^24;
  if isempty(state.filtHd),
    reset(state.filtHd);
  end
  output= {state};

elseif isequal(varargin{1}, 'close'),
  cog_ports= instrfind('Tag', 'Cognionics');
  if ~isempty(cog_ports),
    fclose(cog_ports);
    %delete(cog_ports);  % shutdown port completely?
  end
  output= {};
elseif length(varargin)~=1,
  error('Except for INIT/CLOSE case, only one input argument expected');

else
  if ~isstruct(varargin{1}),
    error('First input argument must be ''init'', ''close'', or a struct');
  end
  state= varargin{1};
  %while state.sp.BytesAvailable<state.nBytesPerPoll,
  %  pause(0.01);
  %end

  % Receive data packet from COM port and check packet IDs
  chunk= fread(state.sp, state.nBytesPerPoll, 'uint8');
  chunk= cat(1, state.buffer, chunk);
  chunk= bbciutil_cognionicsRemoveIncompletePackets(chunk, state);
  idx= find(chunk==255);
  % IDs at the very end are not considered, because packet # is not available
  if ~isempty(idx) && idx(end)==length(chunk),
    idx(end)= [];
  end

  % if last packet is incomplete, put it to the buffer
  % and exclude that packet from current acquisition
  if length(chunk)-idx(end)+1 < state.nBytesPerPacket,
    state.buffer= chunk(idx(end):end);
    idx(end)= [];
  else
    state.buffer= [];
  end
  if isempty(idx),
    if state.verbose,
      fprintf('[ACQ-COG] Did not receive any complete data block.\n');
    end
    state.buffer= chunk;
    output= {[], [], [], state};
    varargout= output(1:nargout);
    return;
  end

  % Extract packet numbers
  if isempty(state.packetNo),   % init case
    state.packetNo= chunk(idx(1)+1)-1;
  end
  packet_counter= [state.packetNo; chunk(idx+1)];
%  fprintf('buffer: #%d, packets: %s\n', state.packetNo, ...
%          str_vec2str(packet_counter(2:end)));
  packet_counter= mod(packet_counter,128);
  unwarp_correction= 128 * (floor(state.packetNo/128) + ...
                            [0; cumsum(diff(packet_counter)<0)]);
  packet_counter= packet_counter + unwarp_correction;
  state.packetNo= packet_counter(end);
%  fprintf('packets unwrapped: %s\n', str_vec2str(packet_counter));

  % Obtain signals from data packets
  np= length(idx);
  idxData= repmat(2+[1:3:3*state.nChans]', [1 np]) + ...
           repmat([0:np-1] * state.nBytesPerPacket, [state.nChans 1]);
  idxData= reshape(idxData, [1 np*state.nChans]);
  msb= chunk(idxData);
  x= bitshift(uint32(msb), 16) + ...
     bitshift(uint32(chunk(idxData+1)), 9) + ...
     bitshift(uint32(chunk(idxData+2)), 2);
  x= x + uint32(bitget(msb, 8)) * bitshift(uint32(255), 24);
  cntx= state.scale * double( typecast(x, 'int32') );
  cntx= reshape(cntx, [state.nChans np])';

  % Obtain markers from data packets
  idxTrg= idx(1) + 2 + 3*state.nChans + 1 + state.nBytesPerPacket*[0:np-1];
  mrkDesc= double( bitshift(uint16(chunk(idxTrg)), 8) + ...
                            uint16(chunk(idxTrg+1)) )';
  iNonvoid= find(mrkDesc & diff([state.lastMrkDesc mrkDesc]));
  state.lastMrkDesc= mrkDesc(end);
  mrkTime= 1/state.fs * iNonvoid;
  if state.verbose && ~isempty(iNonvoid),
    fprintf('[ACQ-COG] Markers: %s\n', str_vec2str(mrkDesc(iNonvoid)));
%     for m= 1:length(iNonvoid),
%       fprintf('[ACQ-COG] %6.3f: %s\n', mrkTime(m), mrkDesc(iNonvoid(m)));
%		 end
  end

  % Interpolate lost packets
  dpc= diff(packet_counter);
  ilost= find(dpc>1);
  if ~isempty(ilost),
    if state.verbose,
      fprintf('[ACQ-COG] interpolating %d lost packets after packet #%d\n', ...
              [dpc(ilost)-1, packet_counter(ilost)]');
    end
    [cntx, mapping]= ...
        bbciutil_cognionicsInterpolate(cntx, packet_counter, state);
    mrkTime= 1/state.fs * mapping(iNonvoid)';
  end
  state.lastx= cntx(end,:);

  % Apply filter if requested (dfilt.filter automatically saves the state)
  if ~isempty(state.filtHd),
    cntx= filter(state.filtHd, cntx, 1);
  end

  output= {cntx, mrkTime, mrkDesc(iNonvoid), state};
end
varargout= output(1:nargout);


function [msi] = read_bti_m4d(filename);

% READ_BTI_M4D
%
% Use as
%   msi = read_bti_m4d(filename)

% Copyright (C) 2007, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
% 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: read_bti_m4d.m 945 2010-04-21 17:41:20Z roboos $

[p, f, x] = fileparts(filename);
if ~strcmp(x, '.m4d')
  % add the extension of the header
  filename = [filename '.m4d'];
end

fid = fopen(filename, 'r');
if fid==-1
  error(sprintf('could not open file %s', filename));
end

% start with an empty header structure
msi = struct;

% these header elements contain strings and should be converted in a cell-array
strlist = {
  'MSI.ChannelOrder'
  };

% these header elements contain numbers and should be converted in a numeric array
%   'MSI.ChannelScale'
%   'MSI.ChannelGain'
%   'MSI.FileType'
%   'MSI.TotalChannels'
%   'MSI.TotalEpochs'
%   'MSI.SamplePeriod'
%   'MSI.SampleFrequency'
%   'MSI.FirstLatency'
%   'MSI.SlicesPerEpoch'
% the conversion to numeric arrays is implemented in a general fashion
% and all the fields above are automatically converted
numlist = {};

line = '';

msi.grad.label = {};
msi.grad.pnt   = zeros(0,3);
msi.grad.ori   = zeros(0,3);
while ischar(line)
  line = cleanline(fgetl(fid));
  if isempty(line) || (length(line)==1 && all(line==-1))
    continue
  end

  sep = strfind(line, ':');
  if length(sep)==1
    key = line(1:(sep-1));
    val = line((sep+1):end);
  elseif length(sep)>1
    % assume that the first separator is the relevant one, and that the
    % next ones are part of the value string (e.g. a channel with a ':' in
    % its name
    sep = sep(1);
    key = line(1:(sep-1));
    val = line((sep+1):end);
  elseif length(sep)<1
    % this is not what I would expect
    error('unexpected content in m4d file');
  end

  if ~isempty(strfind(line, 'Begin'))
    sep = strfind(key, '.');
    sep = sep(end);
    key = key(1:(sep-1));

    % if the key ends with begin and there is no value, then there is a block
    % of numbers following that relates to the magnetometer/gradiometer information.
    % All lines in that Begin-End block should be treated seperately
    val = {};
    lab = {};
    num = {};
    ind = 0;
    while isempty(strfind(line, 'End'))
      line = cleanline(fgetl(fid));
      if isempty(line) || (length(line)==1 && all(line==-1)) || ~isempty(strfind(line, 'End'))
        continue
      end
      ind = ind+1;
      % remember the line itself, and also cut it into pieces
      val{ind} = line;
      % the line is tab-separated and looks like this
      % A68 0.0873437   -0.075789   0.0891512   0.471135    -0.815532   0.336098
      sep = find(line==9); % the ascii value of a tab is 9
      sep = sep(1);
      lab{ind} = line(1:(sep-1));
      num{ind} = str2num(line((sep+1):end));

    end % parsing Begin-End block
    val = val(:);
    lab = lab(:);
    num = num(:);
    num = cell2mat(num);
    % the following is FieldTrip specific
    if size(num,2)==6
      msi.grad.label = [msi.grad.label; lab(:)];
      % the numbers represent position and orientation of each magnetometer coil
      msi.grad.pnt   = [msi.grad.pnt; num(:,1:3)];
      msi.grad.ori   = [msi.grad.ori; num(:,4:6)];
    else
      error('unknown gradiometer design')
    end
  end

  % the key looks like 'MSI.fieldname.subfieldname'
  fieldname = key(5:end);

  % remove spaces from the begin and end of the string
  val = strtrim(val);

  % try to convert the value string into something more usefull
  if ~iscell(val)
    % the value can contain a variety of elements, only some of which are decoded here
    if ~isempty(strfind(key, 'Index')) || ~isempty(strfind(key, 'Count')) || any(strcmp(key, numlist))
      % this contains a single number or a comma-separated list of numbers
      val = str2num(val);
    elseif ~isempty(strfind(key, 'Names')) || any(strcmp(key, strlist))
      % this contains a comma-separated list of strings
      val = tokenize(val, ',');
    else
      tmp = str2num(val);
      if ~isempty(tmp)
        val = tmp;
      end
    end
  end

  % assign this header element to the structure
  msi = setsubfield(msi, fieldname, val);

end % while ischar(line)

fclose(fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION to remove spaces from the begin and end
% and to remove comments from the lines
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function line = cleanline(line)
if isempty(line) || (length(line)==1 && all(line==-1))
  return
end
comment = findstr(line, '//');
if ~isempty(comment)
  line(min(comment):end) = ' ';
end
line = strtrim(line);


%%%%%%%%%%%%%%%
%%
if true,
    clear all;close all;
    varList = {'CR_dtr','numcell_full','CellXYZ','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn'};
else
    % or without 'CR_dtr', keeping it from previous step:
    clearvars -except 'CR_dtr'; % 'CR_z_dtr';
    varList = {'numcell_full','CellXYZ','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','frame_turn'};
end

%%
M_dir = GetFishDirectories();
M_stimset = [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2];

save_dir = GetCurrentDataDir();

dshift = 2; % differential shift between fictive and fluo, manually chosen

%% MANUAL
for i_fish = 1,
    disp(['i_fish = ', num2str(i_fish)]);
    tic
    %% load data
    load(fullfile(M_dir{i_fish},['Fish' num2str(i_fish) '_direct_load_full_v6.mat']),varList{:}); % '_direct_load_nodiscard.mat'
%     load(fullfile(datadir,'frame_turn.mat'),'frame_turn');

    if size(frame_turn,1)<size(frame_turn,2),
        frame_turn = frame_turn';
    end

    %% Manual occasional frame corrections: ONLY RUN ONCE!!!!!!!!!

    if false,
        % add 1 frame at start: ------------- for which fish again??
        CR_dtr = horzcat(CR_dtr(:,1),CR_dtr);
        frame_turn = vertcat(frame_turn(1,:),frame_turn);
    end

    % correction of an error in Fish #1
    if i_fish == 1,
        CR_dtr = horzcat(CR_dtr(:,1:20),CR_dtr);
%         CR_z_dtr = horzcat(CR_z_dtr(:,1:20),CR_z_dtr);
    end

    %% index processing
    if M_stimset(i_fish)==1, % fish 1-7
        M_period = {480,160,160,320,480,140,150}; %,{120,150,360}};
        periods = M_period{i_fish};
        period = periods;

        nrep = floor(size(CR_dtr,2)/period)-1;

        shift = period; % (fixed 9/23/15; before = period+dshift)

        IX_all = 1+shift:period*nrep+shift;
        CellResp = CR_dtr(:,IX_all);
%         CellRespZ = CR_z_dtr(:,IX_all); % z-scored

        nCells = size(CR_dtr,1);
        CellRespAvr = mean(reshape(CR_dtr(:,IX_all+dshift),nCells,period,[]),3);
%         CellRespAvrZ = mean(reshape(CR_z_dtr(:,IX_all+dshift),nCells,period,[]),3);

        if i_fish<6,
            stimrangenames = {'16 permut: B/W/phototaxis*2'};
        else % i_fish = 6 or 7
            stimrangenames = {'phototaxis'};
        end
        %         stimrangenames = {'rep average','all reps','rep #1','rep #2','last rep'};
        ix_avr = 1:period;
        ix_all = 1:period*nrep;
        tlists = {ix_avr, ix_all};
        for i = [1,2,nrep],
            ix = 1+period*(i-1):period*i;
            tlists = [tlists, ix];
        end

        stim_full = frame_turn(IX_all,17)';
        stim_full = round(stim_full);

        stimAvr = stim_full(1:period);

    else % multiple protocols
        % process raw stimulus code
        [stimset,stim_full_raw] = StimulusKeyPreprocessing(frame_turn,i_fish); % StimulusKeyPreprocessing(frame_turn,i_fish,'isplotting') to show plot

        %% variables to save later in struct
        periods = [stimset.period];
        stimrangenames = {stimset.name};

        %% find time-lists = list of time-frames numbers for a certain stimulus
        nTypes = length(stimset);
        tlists_raw = cell(1,nTypes+1); % time-lists, i.e. frame indices
        IX_all = [];
        for i_type = 1:nTypes,
            nSets = length(stimset(i_type).starts);
            IX = [];
            for i_set = 1:nSets,
                IX = horzcat(IX,stimset(i_type).starts(i_set):stimset(i_type).stops(i_set));
            end
            tlists_raw{i_type} = IX;
            IX_all = horzcat(IX_all,IX);
        end
        tlists_raw{nTypes+1} = IX_all;
%         tlists_raw{nTypes+2} = sort(IX_all);

        % This is the main data to store and load to GUI
        CellResp = CR_dtr(:,IX_all);
%         CellRespZ = CR_z_dtr(:,IX_all);
        stim_full = stim_full_raw(IX_all);

        %% get tlists corresponding to IX_all (tlists_raw ~ 1:totalframe#)
        tlists = cell(1,nTypes+1); % time-lists, i.e. frame indices
        for i_type = 1:nTypes+1,
            [~,IX] = ismember(tlists_raw{i_type},IX_all);
            tlists{i_type} = IX(find(IX));
        end

        %% find average
        numcell = size(CR_dtr,1);
        shift = -dshift; % circshift fluo left by 2 frames

        CellRespAvr = [];
        CellRespAvrZ = [];
        stimAvr = [];
        for i = 1:nTypes,
            M = circshift(CellResp(:,tlists{i}),shift,2);
            CellRespAvr = horzcat(CellRespAvr,mean(reshape(M,numcell,periods(i),[]),3));
%             CellRespAvrZ = horzcat(CellRespAvrZ,mean(reshape(M,numcell,periods(i),[]),3));
            % stim from stim_full
            m = stim_full(tlists{i});
            stimAvr = horzcat(stimAvr,m(1:periods(i)));
        end

    end

    %% prepare fictive data
    rows = [7,8,9,13,14];
    F = frame_turn(:,rows)';

    F(2,:) = -F(2,:);
    Fictive = F(:,IX_all);

    % find averages
    if M_stimset(i_fish)==1, % fish 1-7
        FictiveAvr = mean(reshape(Fictive,length(rows),period,[]),3);
    else
        FictiveAvr = [];
        for i = 1:nTypes,
            avr = mean(reshape(F(:,tlists_raw{i}),length(rows),periods(i),[]),3);
            FictiveAvr = horzcat(FictiveAvr,avr);
        end
    end

    % normalizations
    for i = 1:3,
        m = FictiveAvr(i,:);
        FictiveAvr(i,:) = (m-min(m))/(max(m)-min(m));
        m = Fictive(i,:);
        Fictive(i,:) = (m-min(m))/(max(m)-min(m));
    end
    m = FictiveAvr(4:5,:);
    FictiveAvr(4:5,:) =  (m-min(min(m)))/(max(max(m))-min(min(m)));
    m = Fictive(4:5,:);
    Fictive(4:5,:) =  (m-min(min(m)))/(max(max(m))-min(min(m)));

    %% compile CONST
    CellRespAvrZ = zscore(CellRespAvr')';

    data = [];
    names = {'numcell_full','CellXYZ','anat_yx','anat_yz','anat_zx','ave_stack','fpsec','periods','shift','dshift',...
        'CellRespAvr','CellRespAvrZ','Fictive','FictiveAvr','stim_full','stimAvr',... %'CellResp',
        'tlists','stimrangenames'};
    if M_stimset(i_fish) > 1,
        names = [names,{'tlists_raw','stimset'}];
    end

    for i = 1:length(names), % use loop to save variables into fields of CONST
        eval(['data.',names{i},'=', names{i},';']);
    end

    %% and save

    %%% new method with partitioning of main data
    newfishdir = fullfile(save_dir,['Data_F' num2str(i_fish) '_full.mat']);
    dimCR = size(CellResp);
    save(newfishdir,'data','dimCR','-v6');
    % custom function:
    SaveFileInPartsAppendv6(newfishdir,CellResp,'CellResp');
%     SaveFileInPartsAppendv6(newfishdir,CellRespZ,'CellRespZ');

    toc; beep

end

%% compute z-score
% disp('compute z-score')
% cell_resp_z_full = zscore(cell_resp_full')';


function obj = viewMovieCreateSideBySide(obj)
	% Align signal to a stimulus and display images.
	% Biafra Ahanonu
	% branched from controllerAnalysis: 2014.08.01 [16:09:16]
	% inputs
		%
	% outputs
		%

	% changelog
		% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
	% TODO
		%

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

	% =====================
	% fileFilterRegexp = obj.fileFilterRegexp;
	FRAMES_PER_SECOND = obj.FRAMES_PER_SECOND;
	% DOWNSAMPLE_FACTOR = obj.DOWNSAMPLE_FACTOR;
	% =====================
	% if strcmp(obj.analysisType,'group')
	% 	nFiles = length(obj.rawSignals);
	% else
	% 	nFiles = 1;
	% end
	% =====================
	movieSettings = inputdlg({...
		'start:end frames (leave blank for all)',...
		'behavior:movie sample rate (downsample factor): ',...
		'imaging movie regexp:',...
		'video folder(s), separate multiple folders by a comma:',...
		'side-by-side save folder:',...
		'rotate second movie?',...
		'save movie type (hdf5 or tiff)',...
		'division factor behavior movie',...
		'normalize before combining? 0=no, 1=yes'},...
		'downsample settings',1,{...
		'1:500',...
		num2str(obj.DOWNSAMPLE_FACTOR),...
		obj.fileFilterRegexp,...
		obj.videoDir{1},....
		obj.videoSaveDir,...
		'0',...
		'hdf5',...
		'10',...
		'0'});

	frameList = str2num(movieSettings{1});
	DOWNSAMPLE_FACTOR = str2num(movieSettings{2});
	obj.fileFilterRegexp = movieSettings{3}; fileFilterRegexp = obj.fileFilterRegexp;
	obj.videoDir = movieSettings{4}; videoDir = obj.videoDir;
	obj.videoSaveDir = movieSettings{5}; videoSaveDir = obj.videoSaveDir;
	rotateSecondMovie = str2num(movieSettings{6});
	saveMovieType = movieSettings{7};
	behaviorMovieDivisionFactor = str2num(movieSettings{8});
	normalizeMovies = str2num(movieSettings{9});
	% =====================
	videoTrialRegExpList = {'yyyy_mm_dd_pNNN_mNNN_assayNN','yymmdd-mNNN-assayNN','yymmdd_mNNN_assayNN','subject_assay'};
	scnsize = get(0,'ScreenSize');
	[videoTrialRegExpIdx, ok] = listdlg('ListString',videoTrialRegExpList,'ListSize',[scnsize(3)*0.2 scnsize(4)*0.25],'Name','video string type (N = number)');
	% % =====================
	[fileIdxArray, idNumIdxArray, nFilesToAnalyze, nFiles] = obj.getAnalysisSubsetsToAnalyze();
	for thisFileNumIdx = 1:nFilesToAnalyze
		try
			fileNum = fileIdxArray(thisFileNumIdx);
			obj.fileNum = fileNum;
			display(repmat('=',1,21))
			% display([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ': ' obj.fileIDNameArray{obj.fileNum}]);
			display([num2str(thisFileNumIdx) '/' num2str(nFilesToAnalyze) ' (' num2str(fileNum) '/' num2str(nFiles) '): ' obj.fileIDNameArray{obj.fileNum}]);
			% =====================
			% for backwards compatibility, will be removed in the future.
			% subject = obj.subjectNum{obj.fileNum};
			% assay = obj.assay{obj.fileNum};
			% =====================
			% frameList
			movieList = getFileList(obj.inputFolders{obj.fileNum}, fileFilterRegexp);
			[imagingMovie] = loadMovieList(movieList,'convertToDouble',0,'frameList',frameList(:));
			if ~isempty(videoDir)
				if isempty(frameList)
					frameListTmp = 1:size(imagingMovie,3);
					% frameListTmp = 1:min(cellfun(@(x) size(x,3), primaryMovie));
				else
					frameListTmp = frameList;
				end
				videoTrialRegExp = local_getVideoRegexp();
				% videoTrialRegExp
				vidList = getFileList(videoDir,videoTrialRegExp);
				if ~isempty(vidList)
					% get the movie
					% vidList
					behaviorMovie = loadMovieList(vidList,'convertToDouble',0,'frameList',frameListTmp(:)*DOWNSAMPLE_FACTOR,'treatMoviesAsContinuous',1);
					% behaviorMovie = createMovieMontage(behaviorMovie,nAlignPts,timeVector,postOffset,preOffset,options.montageSuffix,savePathName,0);
					% [behaviorMovie] = createSideBySide(behaviorMovie,imagingMovie,'pxToCrop',[],'rotateSecondMovie',rotateSecondMovie);
					[behaviorMovie] = normalizeVector(single(behaviorMovie),'normRange','zeroToOne');
					[behaviorMovie] = normalizeMovie(behaviorMovie,'normalizationType','meanSubtraction');
					behaviorMovie = behaviorMovie/behaviorMovieDivisionFactor;

					[behaviorMovie] = createSideBySide(imagingMovie,behaviorMovie,'pxToCrop',[],'rotateSecondMovie',rotateSecondMovie,'normalizeMovies',normalizeMovies);
					switch saveMovieType
						case 'hdf5'
							savePathName = [videoSaveDir filesep obj.date{obj.fileNum} '_' obj.protocol{obj.fileNum}  '_' obj.fileIDArray{obj.fileNum} '_sideBySide.h5'];
							[output] = writeHDF5Data(behaviorMovie,savePathName);
						case 'tiff'
							savePathName = [videoSaveDir filesep obj.date{obj.fileNum} '_' obj.protocol{obj.fileNum}  '_' obj.fileIDArray{obj.fileNum} '_sideBySide.tif'];
							tiffOptions.comp = 'no';
							saveastiff(behaviorMovie, savePathName, tiffOptions);
						otherwise
							% body
					end
				end
			end
		catch err
			display(repmat('@',1,7))
			disp(getReport(err,'extended','hyperlinks','on'));
			display(repmat('@',1,7))
		end
	end
	function videoTrialRegExp = local_getVideoRegexp()
		switch videoTrialRegExpIdx
			case 1
				videoTrialRegExp = [obj.date{obj.fileNum} '_' obj.protocol{obj.fileNum} '_' obj.fileIDArray{obj.fileNum}];
			case 2
				dateTmp = strsplit(obj.date{obj.fileNum},'_');
				videoTrialRegExp = strcat(dateTmp{1}(end-1:end),dateTmp{2},dateTmp{3},'-',obj.subjectStr{obj.fileNum},'-',obj.assay{obj.fileNum});
			case 3
				videoTrialRegExp = [obj.subjectStr{obj.fileNum} '_' obj.assay{obj.fileNum}]
			otherwise
				videoTrialRegExp = fileFilterRegexp
		end
	end
end


clearvars;
close all;
% Add path to stlTools
% You can download the package for free from:
% https://es.mathworks.com/matlabcentral/fileexchange/51200-stltools
addpath('./stlTools');
% Set the name of the mat file containing all the info of the 3D model
MatFileName = '../../3d_models/a320_3d_model.mat';
% Define the list of parts which will be part of the rigid aircraft body
% First part should always be main aircraft body
rigid_body_list   = { 'Body_A320.stl','Engine_L_A320.stl','Engine_R_A320.stl'};
% Define the color of each part
rigid_body_colors = {0.8 * [1, 1, 1], 0.1 * [1, 1, 1], 0.1 * [1, 1, 1]};
% Define the transparency of each part
alphas            = [              1,               1,               1];
% Define the model ofset to center the A/C Center of Gravity
offset   = [0,0,0];
% Define the control surfaces
ControlsFieldNames = {...
'model'                 'label',             'color',                'rot_point',           'rot_vect', 'max_deflection'};
Controls = {
'Aileron_L_A320.stl',     'A_L',   0.3*[0.8, 0.8, 1], [515.1,-1434,80.5]-offset, [ 0.2932,-0.9523, 0.0843], [-30, +30];
'Aileron_R_A320.stl',     'A_R',   0.3*[0.8, 0.8, 1], [515.1,+1434,80.5]-offset, [ 0.2932, 0.9523, 0.0843], [-30, +30];
'Rudder_A320.stl',        'RUD',   0.3*[0.8, 0.8, 1], [ 1954,     0,550.1]-offset,-[ 0.3064, 0, 0.9519], [-30, +30];
'Elevator_L_A320.stl', 'STAB_L',   0.3*[0.8, 0.8, 1], [ 2037,-352.9,168.5]-offset, [-0.2952, 0.9481,-0.1179], [-30, +30];
'Elevator_R_A320.stl', 'STAB_R',   0.3*[0.8, 0.8, 1], [ 2037,+352.9,168.5]-offset, [ 0.2952, 0.9481, 0.1179], [-30, +30];
};
% Definition of the Model3D data structure
% Rigid body parts
for i = 1:length(rigid_body_list)
    Model3D.Aircraft(i).model = rigid_body_list{i};
    Model3D.Aircraft(i).color = rigid_body_colors{i};
    Model3D.Aircraft(i).alpha = alphas(i);
    % Read the *.stl file
   [Model3D.Aircraft(i).stl_data.vertices, Model3D.Aircraft(i).stl_data.faces, ~, Model3D.Aircraft(i).label] = stlRead(rigid_body_list{i});
    Model3D.Aircraft(i).stl_data.vertices  = Model3D.Aircraft(i).stl_data.vertices - offset;
end
% Controls parts
for i = 1:size(Controls, 1)
    for j = 1:size(Controls, 2)
        Model3D.Control(i).(ControlsFieldNames{j}) = Controls{i, j};
    end
    % Read the *.stl file
    [Model3D.Control(i).stl_data.vertices, Model3D.Control(i).stl_data.faces, ~, ~] = stlRead( Model3D.Control(i).model);
    Model3D.Control(i).stl_data.vertices = Model3D.Control(i).stl_data.vertices - offset;
end

%% Save mat file
save(MatFileName, 'Model3D');

%% Check the results
plot3Dmodel(MatFileName)


function prepData = prepINITModel(origRefModel, taskStruct, spontRxnNames, convertGenes, customRxnsToIgnore, extComp)
% prepINITModel
%
% The purpose of this function is to run time-consuming calculation steps that are not
% dependent on the RNA-Seq data.
%
% origRefModel      The model to use. Expected to be something such as Human-GEM,
%                   Mouse-GEM, etc.
% taskStruct        The essential tasks. Can be loaded with for example
%                   taskStruct = parseTaskList('../data/metabolicTasks_Essential.txt');
% spontRxnNames     The spontaneous rxns. (opt, default {})
% convertGenes      If true the genes are converted to gene names (from
%                   ENSEMBL) (opt, default false)
% customRxnsToIgnore These reactions can be ignored in the ignore mask
%                   (specifying b7=1) (opt, default = {})
% extComp           Name of the external compartment, typically 's' or 'e'. This
%                   is used for identifying exch and import rxns (opt, default = 'e')
% prepData          The resulting prepData structure which is used as input to ftINIT
%
% Usage: prepData = prepINITModel(origRefModel, taskStruct, spontRxnNames, convertGenes, customRxnsToIgnore, extComp)

if nargin < 3
    spontRxnNames = {};
end

if nargin < 4
    convertGenes = false;
end

if nargin < 5
    customRxnsToIgnore = {};
end

if nargin < 6
    extComp = 'e';
end

disp('Step 1: Gene rules')
[origRefModel.grRules, origRefModel.rxnGeneMat] = standardizeGrRules(origRefModel, true);

if convertGenes %For mouse we might want to translate in the opposite direction - this has to be done before calling this function in that case.
    [origRefModel.grRules, origRefModel.genes, origRefModel.rxnGeneMat] = translateGrRules(origRefModel.grRules, 'Name');
end

% Get list of all dead-end/inacessible/constrained reactions in the model.
% We don't merge linear dependent reactions here, that will happen later.
% We also don't remove reversibility here, we don't want that in the final
% models produced. Therefore, this is done as a separate step.
% This takes quite some time to run
disp('Step 2: First simplification')
[~,deletedDeadEndRxns] = simplifyModel(origRefModel,true,false,true,true,true);

% get reduced model
cModel = removeReactions(origRefModel,deletedDeadEndRxns,false,true);

%Then, run the tasks to find out which reactions are essential. These rxns
%will be forced to carry flux in the optimization later

% determine reactions essential for tasks - takes ~10 min
% need to add boundary mets first - but only to a temporary model, we don't
% want those for further processing
disp('Step 3: Check tasks (~10 min)')
if ~isempty(taskStruct)
    bModel = closeModel(cModel);
    [taskReport, essentialRxnMat, ~, essentialFluxes] = checkTasks(bModel,[],true,false,true,taskStruct);

    %extract the essential rxns:
    sel = sum(essentialRxnMat,2) > 0;
    selInd = find(sel);
    essentialRxns = bModel.rxns(selInd);%382 rxns for human-GEM

    %Find metabolites present in taskStruct. We want to avoid removing
    %these metabolites from the final model (even though they may no longer
    %participate in any reacitons) so that the final model is still able to
    %complete all of the tasks without any errors.
    taskMets = union(vertcat(taskStruct.inputs),vertcat(taskStruct.outputs));
    taskMets = union(taskMets, parseRxnEqu(vertcat(taskStruct.equations)));
    modelMets = strcat(cModel.metNames,'[',cModel.comps(cModel.metComps),']');
    [inModel,metInd] = ismember(taskMets,modelMets);
    essentialMetsForTasks = cModel.mets(metInd(inModel));%118 mets

    %Remove tasks that cannot be performed
    taskStruct(taskReport.ok == false) = [];

else
    essentialRxns = {};
    essentialMetsForTasks = {};
    taskReport = {};
end

% remove metabolites separately to avoid removing those needed for tasks
unusedMets = cModel.mets(all(cModel.S == 0,2));%1248 in Human-GEM
cModel = removeMets(cModel, setdiff(unusedMets,essentialMetsForTasks));

if ~isempty(taskStruct)

    %Here, we decide on a direction in which the essential reactions should be forced.
    essentialRevDir = false(length(essentialRxns),1);
    pp = zeros(length(essentialRxns),1);
    nn = zeros(length(essentialRxns),1);
    for i = 1:length(selInd)
        pos = sum(essentialFluxes(selInd(i),essentialRxnMat(selInd(i),:)) > 0);
        neg = sum(essentialFluxes(selInd(i),essentialRxnMat(selInd(i),:)) < 0);
        essentialRevDir(i) = pos < neg;
        pp(i) = pos;
        nn(i) = neg;
    end

    %sum(pp>0 & nn>0) %0, so it is not a big problem that different tasks share essential
    %reactions but in different directions, at least no such cases exist now, so we ignore this for now.

    %Now create a minimal model for the MILP - we want to make this as small as we can, but we
    %don't want to use this later when we create the final models - just for the first MILP.
    %We don't want boundary metabolites in here either.

    %We do a trick with the essential rxns here:
    %What we want is to get rid of the reversibility of the reactions, because reversible reactions require
    %an integer each in the MILP, and we can also get the case that it may be cheaper to force flux in the wrong direction, which
    %will probably not give the gap-filling of reactions that we want.
    %So, we simply change the reversible reactions to irreversible, and flip their direction if the direction
    %to force flux in is negative. We don't need to do anything else, runINIT will then handle all essential rxns
    %as irreversible (and will not add any integers etc. for them).

    minModel1 = cModel;
    %for the essential rxns where we should force flux in the positive direction, just make them irreversible
    %for the negative direction, we do the same, but also flip the direction of the reaction

    %first flip the reactions with negative direction
    %constructEquations(minModel1,minModel1.rxns(selInd(essentialRevDir))) %looks good
    minModel1 = reverseRxns(minModel1, minModel1.rxns(selInd(essentialRevDir)));
    %constructEquations(minModel2,minModel2.rxns(selInd(essentialRevDir))) %looks good

    %Then make them irreversible
    minModel1.rev(selInd) = 0;
    minModel1.lb(selInd) = 0;
else
    minModel1 = cModel;
end
%Now, remove reversibility on any reactions that can only be run in one direction - that simplifies the problem
%This takes a long time to run (at least an hour or so).
%This is an important step, we go from 5533 to 3734 reversible rxns, which reduces the size of the MILP,
%since irreversible rxns are easier to handle.
disp('Step 4: Second simplification (~1 hour)')
minModel2 = simplifyModel(minModel1,false,false,false,false,false,false,true);

disp('Step 5: Final work')

%Now, merge all linear dependent rxns - this reduces the size of the model a lot.
%Size goes from 11888 rxns to 7922 rxns for human-GEM, so this is an important step
[minModel3,origRxnIds,groupIds]=mergeLinear(minModel2, {});

%we now need to figure out what happened to the essential rxns in the merge step above.
%Note that it would be ideal to run checkTasks on the result from mergeLinear, but that doesn't work.
%The exchange rxns have in many cases been merged with other reactions, making it difficult to run
%the tasks. So, instead we run checkTasks before, and figure out which merged reactions the
%essential reactions belong to now

%It may be possible to create a minModel2b, run mergeLinear on that (which is quick), and
%then run checkTasks on the minimized model. Would be good to check if this gives the
%same result, I'm not sure. The code would get less complicated.

if ~isempty(taskStruct)

    %find all potential rxns
    rxnIndOrig = ismember(origRxnIds, essentialRxns);
    ids = unique(groupIds(rxnIndOrig));
    ids = ids(ids > 0);%0 means it has not been merged
    rxnCandidates = unique([essentialRxns;origRxnIds(ismember(groupIds, ids))]);%388, essentialRxns is 382
    newEssentialRxns = minModel3.rxns(ismember(minModel3.rxns,rxnCandidates));%196 in Human-GEM
    %setdiff(newEssentialRxns, essentialRxns)%only 2, that is very few
    %setdiff(essentialRxns, newEssentialRxns)%188, that is very many
    %It is probably the case that many essential reactions are linearly dependent on one another, which makes sense.
else
    newEssentialRxns = {};
end
%What is left now is to remove some rxns from the problem, such as exchange rxns and spontaneous rxns.
%We set the rxn scores of these to exactly 0, and make sure no other scores are zero.
%runINIT will then handle this, we do >0 and <0 to get positive and negative scores, the rest
%are not included in the problem.
%But, this has to happen in the scoring - we identify the reactions in the origrxns here, and then fix this
%in the scoring for each sample (the function groupRxnScores handles this and is run for each sample).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%identify the reactions to ignore in the minModel2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Identify reactions that should be ignored by tINIT, i.e. tINIT will not remove these regardless of score

[~,exchRxnInd] = getExchangeRxns(minModel2);
toIgnoreExch = false(numel(minModel2.rxns),1);
toIgnoreExch(exchRxnInd) = true;
toIgnoreImportRxns = false(numel(minModel2.rxns),1);
toIgnoreSimpleTransp = false(numel(minModel2.rxns),1);
toIgnoreAdvTransp = false(numel(minModel2.rxns),1);
toIgnoreS = false(numel(minModel2.rxns),1);

%sum(toIgnore) %1500 for Human-GEM
%find simple transport reactions with no GPRs:
%We really only skip the reactions with two metabolites
%where they are the same but different compartments - only skip the transport into the cell for now
numMets = sum(minModel2.S ~= 0, 1);
scomp = find(strcmp(minModel2.comps,extComp));
for i = 1:length(minModel2.rxns)
    %check that it has no GPR and that the rxn has two metabolites
    if strcmp(minModel2.grRules(i),'') && (numMets(i) == 2)
      metsComps = minModel2.metComps(minModel2.S(:,i) ~= 0);
      metNames = minModel2.metNames(minModel2.S(:,i) ~= 0);
      if metsComps(1) ~= metsComps(2) && (metsComps(1) == scomp || metsComps(2) == scomp) %different compartments, one is the extComp
          if strcmp(metNames{1}, metNames{2}) %the same metabolite, it is a transport reaction
              toIgnoreImportRxns(i) = true;
          end
      elseif metsComps(1) ~= metsComps(2) %different compartments, no check for extComp
          if strcmp(metNames{1}, metNames{2}) %the same metabolite, it is a transport reaction
              toIgnoreSimpleTransp(i) = true;
          end
      end
    else %check for advanced
        metsInd = find(minModel2.S(:,i) ~= 0);
        %check that we have an even number of mets that is larger than 2 and that we don't have a GPR
        if rem(length(metsInd),2) == 0 && length(metsInd) > 2 && isempty(minModel2.grRules{i})
            %Now check that we have pairs of metabolites that are transported
            SVals = full(minModel2.S(metsInd,i));
            metNames = minModel2.metNames(metsInd);
            comps = minModel2.metComps(metsInd);
            success = true;
            while ~isempty(metNames)
                if length(metNames) < 2 %this should not happen
                   error('We should never arrive here!');
                end
                metMatch = find(strcmp(metNames(2:length(metNames)),metNames{1})) + 1;
                if length(metMatch) ~= 1 %we want a match, and only one (if there is some other complex transport rxn, we skip that)
                   success = false;
                   break;
                end
                if (SVals(1) + SVals(metMatch)) ~= 0 %the stoichiometry is not right
                   success = false;
                   break;
                end
                if comps(1) == comps(metMatch) %they must be in different compartments (maybe this need not to be checked)
                   success = false;
                   break;
                end
                %now remove the pair and continue with the next
                SVals([1;metMatch]) = [];
                metNames([1;metMatch]) = [];
                comps([1;metMatch]) = [];
            end
            toIgnoreAdvTransp(i) = success;
        end
    end
end

%Spontaneous reactions:
toIgnoreSpont = ismember(minModel2.rxns, spontRxnNames);%only 10 rxns in human-GEM

%rxns without GPRs in the s compartment (outside the cell)
sCompInd = find(strcmp(minModel2.comps, extComp));
for i = 1:length(minModel2.rxns)
    metsInd = find(minModel2.S(:,i) ~= 0);
    %check that it has more than 1 metabolite (not exch rxn) and no GPR
    if length(metsInd) > 1 && isempty(minModel2.grRules{i})
        if  all(minModel2.metComps(metsInd) == sCompInd)
            toIgnoreS(i) = true;
        end
    end
end

toIgnoreAllWithoutGPRs = cellfun(@isempty,minModel2.grRules);

%sum(toIgnore)%in total 2375 rxns in human-GEM
%sum(toIgnoreAllTransp)%in total 3337 rxns in human-GEM, so 1,000 more

%Now, try to scale the model to become more favorable for the solver.
%In general, we try to make all fluxes as similar as possible.
%      There is room for improvement here, this function is pretty simple.
%      It only rescales reactions, while it would be possible to rescale
%      metabolites as well (ROS => kROS, albumin => millialbumin, etc., but
%      scaled freely with a mathematical method). It could potentially open up
%      for using less strict margins in the MILP, and make it run faster.
scaledMinModel = rescaleModelForINIT(minModel3);
scaledMinModel.ub(scaledMinModel.ub > 0) = 1000;
scaledMinModel.lb(scaledMinModel.lb < 0) = -1000;

% create data structure with pre-processing results
prepData.taskReport = taskReport;
prepData.essentialRxns = newEssentialRxns; %essential rxns in the minModel
prepData.taskStruct = taskStruct;
prepData.refModel = cModel;
prepData.minModel = scaledMinModel;
prepData.refModelWithBM = closeModel(cModel); %do this here so we don't have to do this for each sample
prepData.groupIds = groupIds;
prepData.essentialMetsForTasks = essentialMetsForTasks;

prepData.toIgnoreExch = toIgnoreExch;
prepData.toIgnoreImportRxns = toIgnoreImportRxns;
prepData.toIgnoreSimpleTransp = toIgnoreSimpleTransp;
prepData.toIgnoreAdvTransp = toIgnoreAdvTransp;
prepData.toIgnoreSpont = toIgnoreSpont;
prepData.toIgnoreS = toIgnoreS;
prepData.toIgnoreCustomRxns = ismember(minModel2.rxns, customRxnsToIgnore);
prepData.toIgnoreAllWithoutGPRs = toIgnoreAllWithoutGPRs;

end


function eeg_write_brainstorm(fileprefix,data)

% eeg_write_brainstorm - Write EEG data into brainstorm format
%
% Useage: eeg_write_brainstorm(fileprefix, data)
%
% file  - the path + filename, eg "c:\subj_data.mat"
%         It is actually a fileprefix, as the _data.mat are
%         appended by this function (they are mandatory)
%
% data  - a matlab structure with brainstorm data fields.
%         There are 2 essential fields (others are initialised):
%
%         F       a matrix of voltage values (in Volts) with
%                 electrodes in rows and data points in columns.
%         Time    Time is a row vector with time points (in sec)
%                 for each column of the voltage data.
%
% This script does not convert the voltage data from uV to Volts
% or the Time from msec to sec.  Also, it assumes that the associated
% Channel struct for this EEG data has the last Channel.Type = 'EEG REF'
% so the ChannelFlag array output from this function contains a -1 at
% index = size(data.F,1) + 1.
%
% Comment:  See the brainstorm website for more info, at
%           http://neuroimage.usc.edu/.
%

% $Revision: 1.1 $ $Date: 2009-04-28 22:13:54 $

% Licence:  GNU GPL, no express or implied warranties
% Author:   07/2001, Darren.Weber_at_radiology.ucsf.edu
%           10/2002, Darren.Weber_at_radiology.ucsf.edu
%                    modified warnings and channelflag
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

fprintf('EEG_WRITE_BRAINSTORM...\n'); tic;

if isfield(data,'F'),
    F = data.F;
else
    msg = strcat('data structure must contain an ''F'' field.\n',...
                 'F is a matrix of voltage values (in Volts)\n',...
                 'with electrodes in rows and data points in columns.\n');
    error(sprintf(msg));
end
if isfield(data,'Time'),
    Time = data.Time;
else
    msg = strcat('data structure must contain a ''Time'' field.\n',...
                 'Time is a row vector with time points (in sec)\n',...
                 'for each column of the voltage data.\n');
    error(sprintf(msg));
end
if isfield(data,'ChannelFlag'),
    ChannelFlag = data.ChannelFlag;
else
%     msg = strcat('data structure can contain a ''ChannelFlag'' field.\n',...
%                  'ChannelFlag is a row vector indicating the status\n',...
%                  'of the electrodes (-1 is dead, 0 is ignored, 1 is good).\n',...
%                  'Creating this vector = ones(1,size(data.F,1)).\n');
%     fprintf(msg);

    ChannelFlag = [ones(1,size(F,1)), -1];
    fprintf('...assume data Nchannels + 1 is ref, so ChannelFlag(Nchannels + 1) = -1\n');
end
if isfield(data,'NoiseCov'),
    NoiseCov = data.NoiseCov;
else
%     msg = strcat('data structure can contain a ''NoiseCov'' field.\n',...
%                  'This is a square matrix the size of the electrodes\n',...
%                  'Creating this vector = ones(size(data.F,1)).\n');
%     fprintf(msg);
    NoiseCov = [];
end
if isfield(data,'SourceCov'),
    SourceCov = data.SourceCov;
else
%     msg = strcat('data structure can contain a ''SourceCov'' field.\n',...
%                  'This is a square matrix the size of ImageGridLoc(?)\n',...
%                  'Creating this matrix = [].\n');
%     fprintf(msg);
    SourceCov = [];
end
if isfield(data,'Project'),
    Projector = data.Project;
else
%     msg = strcat('data structure can contain a ''Project'' field.\n',...
%                  'Not sure what it is, but it is created for the ascii\n',...
%                  'tutorial data.  Creating this matrix = [].\n');
%     fprintf(msg);
    Project = [];
end
if isfield(data,'Projector'),
    Projector = data.Projector;
else
%     msg = strcat('data structure can contain a ''Projector'' field.\n',...
%                  'This is a matrix of size length of electrodes by rank,\n',...
%                  'not necessarily orthogonal due to Good-Channel selections.\n',...
%                  'Orthogonalized as ''U'', then the data are to be projected away\n',...
%                  'from the projector as F'' = F - U*(U^t * F).\n',...
%                  'Creating this matrix = [].\n');
%     fprintf(msg);
    Projector = [];
end
if isfield(data,'Comment'),
    Comment = data.Comment;
else
%     msg = strcat('data structure can contain a ''Comment'' field.\n',...
%                  'This is a character string describing the data.\n',...
%                  'Creating this field = file.\n');
%     fprintf(msg);
    Comment = fileprefix;
end
if isfield(data,'Device'),
    Device = data.Device;
else
%     msg = strcat('data structure can contain a ''Device'' field.\n',...
%                  'This is a character string describing the device\n',...
%                  'used to acquire the data (eg, SYNAMPS)\n',...
%                  'Creating an empty field.\n');
%     fprintf(msg);
    Device = 'Neuromag_Planar_122'; % the only supported device!
end

[path,file,ext] = fileparts(fileprefix);
ext = '_data.mat';

file = fullfile(path,[file ext]);

save(file,'F','Time','ChannelFlag','NoiseCov','SourceCov',...
          'Project','Projector','Comment','Device');

fprintf('...wrote brainstorm data to:\n\t%s\n', file);

t = toc; fprintf('...done (%6.2f sec).\n\n',t);

return