%% Example of using KST class for interfacing with KUKA iiwa robots

% Using the gamepad for controlling the end-effector of the KUKA
% iiwa 7 R 800 robot.

% 1- Left joystick of the game pad is used to control the X-Y motion of the end effector.
%   a- Move the left joystick right and left to move the end-effector along the X axes.
%   b- Move the left joystick up or down to move the end-effector along the Y axes.
%   c- Combination is used to move the end-effector along X and Y
%   simultaneously
% -------------------------------------
% 2- Right joystick of the game pad is used to control the orientation of the end effector around the X and the Y axes.
%   a- Move the right joystick right and left to rotate the end-effector around the X axes.
%   b- Move the right joystick up or down to rotate the end-effector around the Y axes.
%   c- Combination is used to rotate the end-effector around X and Y
%   simultaneously
% -------------------------------------

% Warning: keep away from joint limits, and singularities they are not
% yet taken into consideratio.
% The Toolbox is a work in progress, other functionalities are going to be
% added with time.

% This example works with Sunrise application version KST_1.7  and higher.
% Copyright: Mohammad SAFEEA, 25th of June 2018

close all;clear;clc;
warning('off')
% Start the joystick, make sure that the joystick is connected
instrreset;
ID=1;
joy=vrjoystick(ID);
%% Add path of KST class to work space
cDir = pwd;
cDir=getTheKSTDirectory(cDir);
addpath(cDir);

%% Instantiate the KST object
ip='172.31.1.147'; % The IP of the controller
arg1=KST.LBR7R800; % choose the robot iiwa7R800 or iiwa14R820
arg2=KST.Medien_Flansch_elektrisch; % choose the type of flange
Tef_flange=eye(4); % transofrm matrix of EEF with respect to flange
iiwa=KST(ip,arg1,arg2,Tef_flange); % create the object

%% Start a connection with the server
flag=iiwa.net_establishConnection();
if flag==0
	fprintf('Can not connect to KST \n');
    fprintf('Program terminated \n');
    return;
end

% Initial configuration
jPos={0, pi / 180 * 30, 0, -pi / 180 * 60, 0,...
                        pi / 180 * 90, 0};
% Move robot to initial configuration
relVel=0.15;
iiwa.movePTPJointSpace(jPos, relVel); % move to initial configuration

% Start direct servo in joint space
iiwa.realTime_startDirectServoJoints();

% IK solver parameters
numberOfIterations=10;
lambda=0.1;
TefTool=eye(4);

% Initial configuration vector
qin=zeros(7,1);
for i=1:7
    qin(i)=jPos{i};
end

% Ttransformation matrix at initial configurarion
[Tt,j]=iiwa.directKinematics(qin);

% max angular velocity, at which the end-effector can rotate.
w=5*pi/180; % rad/sec
% max linear velocity, at which the end-effector can move.
v=0.05; % m/sec
% Joint space control
firstExecution=0;

pause(0.1);

% filtering buffer, value
c=0.9;
joyStatus=read(joy);
filter=zeros(size(joyStatus));

% Control loop
while true
    joyStatus=read(joy);
    % Remove the bias in the input analog signal
    analogPrecission=1/20;
    for i=1:4
        if abs( joyStatus(i))<analogPrecission
            joyStatus(i)=0;
        end
    end
    filter=c*filter+(1-c)*joyStatus;
    joyStatus=filter;

    % About the variable (joyStatus)
    % joyStatus: is a 4x1 vector, the first and the second elements of this
    % vector correspond to the analog values of the left analog-stick
    % psoition. the third and the fourth elements of this
    % vector correspond to the analog values of the right analog-stick
    % psoition.
    %
    % All foour elemets of the analog signal of the vector  (joyStatus) are used for
    % controlling the end-effector.
    %

    % Calculate the elapsed time between updates
    if(firstExecution==0)
        firstExecution=1;
        a=datevec(now);
        timeNow=a(6)+a(5)*60+a(4)*60*60; % calculate time at this instant
        dt=0; % elapsed is zero at first excution
        time0=timeNow;
    else
        a=datevec(now);
        timeNow=a(6)+a(5)*60+a(4)*60*60; % calculate time at this instant
        dt=timeNow-time0; % calculate elapsed time interval
        time0=timeNow;
    end
 % Construct motion control command
     n=[joyStatus(4);
         joyStatus(3);
         0];
     k=[0;0;1];
     w_control=w*cross(n,k);
     v_control=v*[joyStatus(2);
         joyStatus(1);
         0];
% Calculate target transform using command, and current transform
     Tt=updateTransform(Tt,w_control,v_control,dt);

     for i=1:7
         qin(i)=jPos{i};
     end

      qt=kukaDLSSolver( qin, Tt, TefTool,numberOfIterations,lambda );

      if(size(qt)==[7,1])
          errorFlag=false;
          for i=1:7
              if abs(qt(i)-qin(i))>pi*0.5/180
                  errorFlag=true;
              end
          end
      % check joints limits
     qt=iiwa.gen_InverseKinematics( qin, Tt,numberOfIterations,lambda );
     flag=jointLimitReached(qt);
     % if the motion causes joint limit violation, stay in the current configuration
     if flag==true;
         qt=qin;
     end

          if errorFlag==false
              for i=1:7
                  jPos{i}=qt(i);
              end
          end
      end
      iiwa.sendJointsPositions(jPos);

     % A condition to break the loop, when x button of the gamepad is
     % pressed
     b=button(joy,1);
     if(b==1)
         break;
     end

end
% turn off the server
% close connection
iiwa.net_turnOffServer();


% Codes for CVPR-15 work `Face Alignment by Coarse-to-Fine Shape Searching'
% Any question please contact Shizhan Zhu: zhshzhutah2@gmail.com
% Released on July 25, 2015

if exist('config','var'), error('Please remove variable config mannally!'); end;

addpath(genpath('./codes_release'));
addpath(genpath('./external'));

config.stageTot = 3;
config.win_size = 250;

% From Pr to sub-region center
regsInfo.iterTot = [3 3 3];
regsInfo.samplingOffset = [NaN,1500,100];
regsInfo.trainSampleTot = [10 10 10];
regsInfo.testSampleTot = [10 10 10];
regsInfo.aug_eyes_id = [37 46];
regsInfo.win_size = config.win_size;
regsInfo.SIFTscale = [12 6 6 6];
regsInfo.mirror = [17:-1:1 27:-1:18 28:31 36:-1:32 ...
    46 45 44 43 48 47 40 39 38 37 42 41 ...
    55:-1:49 60:-1:56 65:-1:61 68:-1:66];
regsInfo.mirror = [regsInfo.mirror regsInfo.mirror+68];
regsInfo.regressorInfo.trainMethod = @getLR_lcScale;
regsInfo.regressorInfo.lambda = [50*25 50*30 50*20 50*20];
regsInfo.regressorInfo.times = 20;

regsInfo.dominantIterTot = [100 100 100];

config.regs = regsInfo;

% From sub-region center to Pr
probsInfo.semantic_id = [18 22 23 27 37 40 43 46 49 55 51 52 53 58 9];
probsInfo.fix_id = [3:7 11:15];
probsInfo.sigmaCutoff = [0.25 0.25];

probsInfo.SVCradius = [20 10];
probsInfo.SVCthre = {[0.3 0.6],[0.3 0.6]};
probsInfo.probSamplings = [5 5];
probsInfo.pyramidScale = [6 12 18];
probsInfo.representativeNum1 = [12 12];
probsInfo.representativeNum2 = [8 8];

probsInfo.acceptThre = 0.9;
probsInfo.gamma_current = 1/50;
probsInfo.gamma_search = 1/50;
probsInfo.gamma_fix = 1/50;
probsInfo.gamma_prior = [1/50,1];

config.probs = probsInfo;

% Prior model
priorsInfo.augTimes = 10;
priorsInfo.maxRoll = 45;
priorsInfo.nose_id = 34;
priorsInfo.noseUpLength = 15;
priorsInfo.rotationCenter = [0.5 0.35]; %(x,y)
priorsInfo.win_size = config.win_size;
priorsInfo.rotatorLength = 15;
priorsInfo.extractWindow = [0.2 0.8 0.1 0.7]; %(xX,xD,yX,yD)
priorsInfo.extractCellNum = 3;
priorsInfo.BaggerNum = 100;

config.priors = priorsInfo;

clear regsInfo probsInfo priorsInfo;

%% Generate testConf
testConf.stageTot = config.stageTot;
testConf.win_size = config.win_size;
testConf.priors.win_size = config.win_size;
testConf.priors.rotationCenter = config.priors.rotationCenter;
testConf.priors.rotatorLength = config.priors.rotatorLength;
testConf.priors.extractWindow = config.priors.extractWindow;
testConf.priors.extractCellNum = config.priors.extractCellNum;
testConf.priors.predictedVoteThre = 10;
testConf.regs.samplingTot = [10 10 10];
testConf.regs.dominantIterTot = config.regs.dominantIterTot;
testConf.regs.SIFTscale = config.regs.SIFTscale;
testConf.regs.iterTot = config.regs.iterTot;
testConf.probs.probSamplings = config.probs.probSamplings;
testConf.probs.semantic_id = config.probs.semantic_id;
testConf.probs.fix_id = config.probs.fix_id;
testConf.probs.pyramidScale = config.probs.pyramidScale;
testConf.probs.acceptThre = config.probs.acceptThre;
testConf.probs.gamma_current = config.probs.gamma_current;
testConf.probs.gamma_search = config.probs.gamma_search;
testConf.probs.gamma_fix = config.probs.gamma_fix;
testConf.probs.gamma_prior = config.probs.gamma_prior;


%%Ex. 2 The meaning of "a = b"

%In Matlab and in any programming language, the statement "a = b" does not mean
%"a equals b". Instead, it prompts the action of replacing the content of a by the
%content of b.
a = 3;
b = a;
b

%Output:3


function ExecFlag=DoSARWatchExec(handles)

global VObj;
global VMag;
global VCoi;
global VCtl;
global VSig;
global VSeq;
global VVar;

%preserve VObj
VTmpObj=VObj;

handles.Simuh=guidata(handles.Simuh.SimuPanel_figure);
%% Do spin execution
try
    % Read tab parameters
    fieldname=fieldnames(handles.Attrh1);
    for i=1:length(fieldname)/2
        try
            eval(['SW.' fieldname{i*2} '=[' get(handles.Attrh1.(fieldname{i*2}),'String') '];']);
        catch me
            TAttr=get(handles.Attrh1.(fieldname{i*2}),'String');
            eval(['SW.' fieldname{i*2} '=''' TAttr{get(handles.Attrh1.(fieldname{i*2}),'Value')}  ''';']);
        end
    end

    if SW.N_Second<VCtl.TR
        error('''N_Second'' needs to be no less than one TR.');
    end

    % Prescan config
    DoPreScan(handles.Simuh);
    DoUpdateBar(handles.TimeBar_axes,10,40);

    if ~isequal(size(VCoi.TxE1x(:,:,:,1)),[max(max(sum(VMag.FRange,1))),max(max(sum(VMag.FRange,2))),max(max(sum(VMag.FRange,3)))])
        error('E1+ map is not available or invalid.');
    end

    % Create Executing Virtual Structure VOsw
    VOsw=VObj;
    VOsw.MassDen(VMag.FRange==0)=[];
    VOsw.ECon(repmat(VMag.FRange,[1,1,1,3])==0)=[];

    % Kernel uses Mz to determine SpinMx size
    VOsw.MassDen=reshape(VOsw.MassDen,[max(max(sum(VMag.FRange,1))),max(max(sum(VMag.FRange,2))),max(max(sum(VMag.FRange,3)))]);
    VOsw.ECon=reshape(VOsw.ECon,[max(max(sum(VMag.FRange,1))),max(max(sum(VMag.FRange,2))),max(max(sum(VMag.FRange,3))),3]);

    % Spin execution
    VObj=VOsw;

    if sum(VObj.MassDen(VObj.MassDen~=0))*(VObj.XDimRes*VObj.YDimRes*VObj.ZDimRes)*0.01<SW.N_Gram/1000
        error(['''N_Gram'' needs to be less than ' num2str(sum(VObj.MassDen(VObj.MassDen~=0))*(VObj.XDimRes*VObj.YDimRes*VObj.ZDimRes)*10) 'g (1% of the total mass).']);
    end

    % Generate Pulse line
    DoPulseGen(handles.Simuh);
    DoUpdateBar(handles.TimeBar_axes,20,40);

    % Initialize SAR, Muts in VSig & VVar
    seq=VSeq.utsLine;
    dseq=diff(seq);
    dseq(dseq<0)=0;
    tSeqEnd=sum(dseq); % sequence end time point
    if isinf(SW.N_Second)
        tStart=0;
        tEnd=tSeqEnd;
        tSample=unique([tStart, tEnd]);
    else
        tEnd=max(0,SW.tStart):SW.dt:min(tSeqEnd,SW.tEnd); % prepare SAR sampling time point
        tStart=tEnd-SW.N_Second;
        tStart(tStart<0)=0;
        tStart(tEnd==0)=[];
        tEnd(tEnd==0)=[];
        if isempty(tEnd) | isempty(tStart)
            error('Input sample time is invalid.');
        end
        tSample=unique([tStart, tEnd]);
    end

    [row,col,layer]=size(VObj.MassDen);
    VSig.tSample=tSample;
    VSig.tRealSample=zeros(size(tSample));
    VSig.SAR=zeros([[row,col,layer] length(tSample)]);
    VSig.Muts=zeros(max(size(VSeq.utsLine)),1);
    VVar.SAR=zeros([[row,col,layer] 3]);

    % Simulation Process
    fprintf('Calculating unaveraged local SAR...\n');
    try
        % convert data type
        DoDataTypeConv(handles.Simuh);
        VObj.MassDen=single(VObj.MassDen);
        VObj.ECon=single(VObj.ECon);
        VCoi.TxE1x=single(VCoi.TxE1x);
        VCoi.TxE1y=single(VCoi.TxE1y);
        VCoi.TxE1z=single(VCoi.TxE1z);
        VSig.SAR=single(VSig.SAR);
        VVar.SAR=single(VVar.SAR);

        % calculate dissipated power
        VCtl.MaxThreadNum=int32(handles.Simuh.CPUInfo.NumThreads);
        VVar.SARi=int32(0);
        DoCalSARAtCPU;

        % calculate unaveraged SAR
        SAR=zeros([size(VObj.MassDen) length(tEnd)]);
        Power=zeros([size(VObj.MassDen) length(tEnd)]);
        tSARSample=zeros(size(tEnd));
        tSARSecond=zeros(size(tEnd));
        for i=1:length(tEnd)
            if tEnd(i)>max(VSig.tRealSample)
                SAR(:,:,:,i:end)=[];
                tSARSample(i:end)=[];
                break;
            end
            idx1 = find(tSample==tStart(i));
            idx2 = find(tSample==tEnd(i));
            tSARSecond(i) = VSig.tRealSample(idx2)-VSig.tRealSample(idx1);
            % Collins et.al. MRM 2001 and Tang et.al. PIERS 2007
            SAR(:,:,:,i)=((VSig.SAR(:,:,:,idx2) - VSig.SAR(:,:,:,idx1))/(VSig.tRealSample(idx2)-VSig.tRealSample(idx1)))./(2*VObj.MassDen);
            Power(:,:,:,i)=((VSig.SAR(:,:,:,idx2) - VSig.SAR(:,:,:,idx1))/(VSig.tRealSample(idx2)-VSig.tRealSample(idx1)))*(VObj.XDimRes*VObj.YDimRes*VObj.ZDimRes/2);
            tSARSample(i)=VSig.tRealSample(idx2);
        end
        SAR(isnan(SAR))=0;
        fprintf('Calculating unaveraged local SAR completed.\n');
        DoUpdateBar(handles.TimeBar_axes,30,40);

    catch me
        error_msg{1,1}='ERROR!!! SAR calculation process aborted.';
        error_msg{2,1}=me.message;
        errordlg(error_msg);
        ExecFlag=0;

        %recover VObj
        VObj=VTmpObj;
        return;
    end

    %N-gram average SAR
    if SW.N_Gram~=0
        fprintf('Calculating %f-gram local SAR...\n', SW.N_Gram);
%         aveSAR=zeros(size(SAR));
%         avePower=zeros(size(Power));
%         aveKGram=zeros(size(SAR));
        % Carluccio et.al. TBME 2013
        [aveSAR,avePower]=DoSARAverageAtCPU(SAR,Power,double(VObj.MassDen),VObj.XDimRes,VObj.YDimRes,VObj.ZDimRes,SW.N_Gram);
%         for i=1:size(SAR,4)
%             [aveSAR(:,:,:,i),avePower(:,:,:,i),aveKGram(:,:,:,i)]=DoSARAverage(SAR(:,:,:,i),Power(:,:,:,i),VObj.MassDen*(VObj.XDimRes*VObj.YDimRes*VObj.ZDimRes),SW.N_Gram/1000);
%             fprintf('(%d/%d) %f-gram local SAR at time point %fs completed.\n', i, size(SAR,4), SW.N_Gram, tSARSample(i));
%             DoUpdateBar(handles.TimeBar_axes,i,size(SAR,4));
%             pause(0.001);
%         end
    else
        aveSAR=SAR;
        avePower=Power;
%         aveKGram=zeros(size(SAR));
    end

    handles.SW = SW;
    handles.aveSAR = aveSAR;
    handles.avePower = avePower;
%     handles.aveKGram = aveKGram;
    handles.tSAR = tEnd; % prescribed sample point
    handles.tSARSample = tSARSample; % actual sample point
    handles.tSARSecond = tSARSecond; % actual N_Second
    handles.MassDen = VObj.MassDen;

    DoUpdateBar(handles.TimeBar_axes,40,40);
catch me
    error_msg{1,1}='ERROR!!! SAR calculation process aborted.';
    error_msg{2,1}=me.message;
    errordlg(error_msg);
    ExecFlag=0;

    %recover VObj
    VObj=VTmpObj;
    return;
end

%recover VObj
VObj=VTmpObj;

guidata(handles.SARWatcherPanel_figure, handles);
ExecFlag=1;

end


function [net, info] = wsddn_train(varargin)
% @author: Hakan Bilen
% wsddn_train: training script for WSDDN

opts.dataDir = fullfile(vl_rootnn, 'data') ;
opts.expDir = fullfile(vl_rootnn, 'exp') ;
opts.imdbPath = fullfile(vl_rootnn, 'data', 'imdbs', 'imdb-eb.mat');
opts.modelPath = fullfile(vl_rootnn, 'models', 'imagenet-vgg-f.mat') ;
opts.proposalType = 'eb' ;
opts.proposalDir = fullfile(vl_rootnn, 'data', 'EdgeBoxes') ;

opts.addBiasSamples = 1; % add Box Scores
opts.addLossSmooth  = 1; % add Spatial Regulariser
opts.softmaxTempCls = 1; % softmax temp for cls
opts.softmaxTempDet = 2; % softmax temp for det
opts.maxScale = 2000 ;

% if you have limited gpu memory (<6gb), you can change the next 2 params
opts.maxNumProposals = inf; % limit number (eg 1500)
opts.imageScales = [480,576,688,864,1200]; % scales
opts.minBoxSize = 20; % minimum bounding box size
opts.train.gpus = [] ;
opts.train.continue = true ;
opts.train.prefetch = true ;
opts.train.learningRate = 1e-5 * [ones(1,10) 0.1*ones(1,10)] ;
opts.train.weightDecay = 0.0005;
opts.train.numEpochs = 20;
opts.train.derOutputs = {'objective', 1} ;

opts.numFetchThreads = 1 ;
opts = vl_argparse(opts, varargin) ;

display(opts);

opts.train.batchSize = 1 ;
opts.train.expDir = opts.expDir ;
opts.train.numEpochs = numel(opts.train.learningRate) ;
%% -------------------------------------------------------------------------
%                                                   Database initialization
% -------------------------------------------------------------------------
fprintf('loading imdb...');
if exist(opts.imdbPath,'file')==2
  imdb = load(opts.imdbPath) ;
else
  if strcmp(opts.proposalType,'ssw')
    imdb = setup_voc07_ssw('dataDir',opts.dataDir, ...
      'proposalDir',opts.proposalDir,'loadTest',1);
  elseif strcmp(opts.proposalType,'eb')
    imdb = setup_voc07_eb('dataDir',opts.dataDir, ...
      'proposalDir',opts.proposalDir,'loadTest',1);
  else
    error('undefined proposal type %s\n',opts.proposalType)
  end

  imdbFolder = fileparts(opts.imdbPath);

  if ~exist(imdbFolder,'dir')
    mkdir(imdbFolder);
  end
  save(opts.imdbPath,'-struct', 'imdb', '-v7.3');
end

fprintf('done\n');

imdb = fixBBoxes(imdb, opts.minBoxSize, opts.maxNumProposals);

% use train + val for training
imdb.images.set(imdb.images.set == 2) = 1;
trainIdx = find(imdb.images.set == 1);

%% Compute image statistics (mean, RGB covariances, etc.)
imageStatsPath = fullfile(opts.dataDir, 'imageStats.mat') ;
if exist(imageStatsPath,'file')
  load(imageStatsPath, 'averageImage', 'rgbMean', 'rgbCovariance') ;
else

  images = imdb.images.name(imdb.images.set == 1) ;
  images = strcat([imdb.imageDir filesep],images) ;

  [averageImage, rgbMean, rgbCovariance] = getImageStats(images, ...
    'imageSize', [256 256], ...
    'numThreads', opts.numFetchThreads, ...
    'gpus', opts.train.gpus) ;
  save(imageStatsPath, 'averageImage', 'rgbMean', 'rgbCovariance') ;
end
[v,d] = eig(rgbCovariance) ;
rgbDeviation = v*sqrt(d) ;
clear v d ;

%% ------------------------------------------------------------------------
%                                                    Network initialization
% -------------------------------------------------------------------------
nopts.addBiasSamples = opts.addBiasSamples; % add Box Scores (only with Edge Boxes)
nopts.addLossSmooth  = opts.addLossSmooth; % add Spatial Regulariser
nopts.softmaxTempCls = opts.softmaxTempCls; % softmax temp for cls
nopts.softmaxTempDet = opts.softmaxTempDet; % softmax temp for det

nopts.averageImage = reshape(rgbMean,[1 1 3]) ;
% nopts.rgbVariance = 0.1 * rgbDeviation ;
nopts.rgbVariance = [] ;
nopts.numClasses = numel(imdb.classes.name) ;
nopts.classNames = imdb.classes.name ;

if ~exist(opts.modelPath,'file')
  [pname,fname,ext]  = fileparts(opts.modelPath) ;
  if ~exist(pname,'dir')
    mkdir(pname) ;
  end
  fprintf('Downloading %s to %s\n', [fname ext], pname) ;
  urlwrite(sprintf('http://www.vlfeat.org/matconvnet/models/%s',[fname ext]),...
    opts.modelPath) ;
end

net = load(opts.modelPath);
net = wsddn_init(net,nopts);

if nopts.addLossSmooth
  opts.train.derOutputs = {'objective', 1, 'lossTopB', 1e-4} ;
end

if ~exist(opts.expDir,'dir')
  mkdir(opts.expDir) ;
end

%% -------------------------------------------------------------------------
%                                                   Database stats
% -------------------------------------------------------------------------
bopts = net.meta.normalization;
net.meta.augmentation.jitterBrightness = 0 ;
% bopts.interpolation = 'bilinear';
bopts.jitterBrightness = net.meta.augmentation.jitterBrightness ;
bopts.imageScales = opts.imageScales;
bopts.numThreads = opts.numFetchThreads;
bopts.addLossSmooth = opts.addLossSmooth;
bopts.addBiasSamples = opts.addBiasSamples;
bopts.maxScale = opts.maxScale ;
bopts.vgg16 = any(arrayfun(@(a) strcmp(a.name, 'relu5_1'), net.layers)==1) ;
%% -------------------------------------------------------------------
%                                                                Train
% --------------------------------------------------------------------
% avoid test data
valIdx = find(imdb.images.set == 3);
valIdx = valIdx(1:5:end) ;
% valIdx = [];

%%
bopts.useGpu = numel(opts.train.gpus) >  0 ;
bopts.prefetch = opts.train.prefetch;

info = cnn_train_dag(net, imdb, @(i,b) ...
  getBatch(bopts,i,b), ...
  opts.train, 'train', trainIdx, ...
  'val', valIdx) ;

%% -------------------------------------------------------------------
%                                                       Deploy network
% --------------------------------------------------------------------
if ~exist(fullfile(opts.expDir,'net.mat'),'file')
  removeLoss = {'dagnn.Loss','dagnn.DropOut'};
  for i=1:numel(removeLoss)
    dagRemoveLayersOfType(net,removeLoss{i}) ;
  end

  net.mode = 'test' ;
  net_ = net ;
  net = net_.saveobj() ;
  save(fullfile(opts.expDir,'net.mat'), '-struct','net');
end
% --------------------------------------------------------------------
function inputs = getBatch(opts, imdb, batch)
% --------------------------------------------------------------------
if isempty(batch)
  inputs = {'input', [], 'label', [], 'rois', [], 'ids', []};
  return;
end

opts.scale = opts.imageScales(randi(numel(opts.imageScales)));
opts.flip = randi(2,numel(batch),1)-1; % random flip
is_vgg16 = opts.vgg16 ;
opts = rmfield(opts,'vgg16') ;

images = strcat([imdb.imageDir filesep], imdb.images.name(batch)) ;
opts.prefetch = (nargout == 0);

[im,rois] = wsddn_get_batch(images, imdb, batch, opts);

if nargout>0
  rois = single(rois') ;
  labels = imdb.images.label(:,batch) ;
  labels = reshape(labels,[1 1 size(labels,1) numel(batch)]);

  if opts.useGpu > 0
    im = gpuArray(im) ;
    rois = gpuArray(rois) ;
  end

  if ~isempty(rois)
   rois = rois([1 3 2 5 4],:) ;
  end

  ss = [16 16] ;

  if is_vgg16
    o0 = 8.5 ;
    o1 = 9.5 ;
  else
    o0 = 18 ;
    o1 = 9.5 ;
  end

  rois = [ rois(1,:); ...
    floor((rois(2,:) - o0 + o1) / ss(1) + 0.5) + 1;
    floor((rois(3,:) - o0 + o1) / ss(2) + 0.5) + 1;
    ceil((rois(4,:) - o0 - o1) / ss(1) - 0.5) + 1;
    ceil((rois(5,:) - o0 - o1) / ss(2) - 0.5) + 1];

  inputs = {'input', im, 'label', labels, 'rois', rois, 'ids', batch} ;

  if opts.addLossSmooth
    inputs{end+1} = 'boxes' ;
    inputs{end+1} = imdb.images.boxes{batch} ;
  end

  if opts.addBiasSamples==1
    boxScore = reshape(imdb.images.boxScores{batch},[1 1 1 numel(imdb.images.boxScores{batch})]);
    inputs{end+1} = 'boxScore';
    inputs{end+1} = boxScore ;
  end
end

% -------------------------------------------------------------------------
function imdb = fixBBoxes(imdb, minSize, maxNum)
% -------------------------------------------------------------------------
for i=1:numel(imdb.images.name)
  bbox = imdb.images.boxes{i};
  % remove small bbox
  isGood = (bbox(:,3)>=bbox(:,1)+minSize) & (bbox(:,4)>=bbox(:,2)+minSize);
  bbox = bbox(isGood,:);
  % remove duplicate ones
  [dummy, uniqueIdx] = unique(bbox, 'rows', 'first');
  uniqueIdx = sort(uniqueIdx);
  bbox = bbox(uniqueIdx,:);
  % limit number for training
  if imdb.images.set(i)~=3
    nB = min(size(bbox,1),maxNum);
  else
    nB = size(bbox,1);
  end

  if isfield(imdb.images,'boxScores')
    imdb.images.boxScores{i} = imdb.images.boxScores{i}(isGood);
    imdb.images.boxScores{i} = imdb.images.boxScores{i}(uniqueIdx);
    imdb.images.boxScores{i} = imdb.images.boxScores{i}(1:nB);
  end
  imdb.images.boxes{i} = bbox(1:nB,:);
  %   [h,w,~] = size(imdb.images.data{i});
  %   imdb.images.boxes{i} = [1 1 h w];

end

% -------------------------------------------------------------------------
function layers = dagFindLayersOfType(net, type)
% -------------------------------------------------------------------------
layers = [] ;
for l = 1:numel(net.layers)
  if isa(net.layers(l).block, type)
    layers{1,end+1} = net.layers(l).name ;
  end
end
% -------------------------------------------------------------------------
function dagRemoveLayersOfType(net, type)
% -------------------------------------------------------------------------
names = dagFindLayersOfType(net, type) ;
for i = 1:numel(names)
  layer = net.layers(net.getLayerIndex(names{i})) ;
  net.removeLayer(names{i}) ;
  net.renameVar(layer.outputs{1}, layer.inputs{1}, 'quiet', true) ;
end


function colors = getColorSet(numObjs)
% get rgb [0,1] values from id
colors=[
        128 255 255;    %
        255 0 0;        % red           1
        0 255 0;        % green         2
        0 0 255;        % blue          3
        0 255 255;      % cyan          4
        255 0 255;      % magenta       5
        212 212 0;      % yellow        6
        25 25 25;       % black         7
        34,139,34;      % forestgreen   8
        0,191,255;      % deepskyblue   9
        139,0,0 ;       % darkred       10
        218,112,214;    % orchid        11
        244,164,96 ;]/255;  % sandybrown    12
colors = colors / 255;
if(numObjs >= 13)
    load colors.mat
    %morecolors = rand(10000, 3);
    %colors = cat(1,colors,morecolors);
end


% Load far talk, close talk, and frame label.
function [Data, para, vocab] = LoadParallelWavLabel_Reverb(para, step, dataset, datatype, distance)
nCh = para.topology.useChannel;
wavlist = []; wavlistClean = [];
% note that training data do not contain real recordings.
% real dev and eval data do not have clean version
for type_i = 1:length(datatype)
    if strcmpi(datatype, 'real') || strcmpi(dataset, 'train')
        nRoom = 1;    else; nRoom = 3; end
    for dist_i = 1:length(distance)
        for roomID = 1:nRoom
            [tasklist, taskfile] = LoadTaskFile_Reverb([para.local.wavroot '/taskFiles'], dataset, datatype{type_i}, distance{dist_i}, nCh, roomID);
            wavlist = [wavlist Tasklist2wavlist(para.local.wavroot, tasklist, dataset, datatype{type_i})];

            if strcmpi(dataset, 'train')
                wavlistClean = findFiles([para.local.wsjcam0root '/data_wav/primary_microphone/si_tr'], para.local.wsjcam0ext);
            elseif ~strcmpi(datatype, 'real')
                [tasklistClean, taskfileClean] = LoadTaskFile_Reverb([para.local.wavroot '/taskFiles'], dataset, datatype{type_i}, 'cln', nCh, roomID);
                wavlistClean = [wavlistClean Tasklist2wavlist(para.local.wavroot, tasklistClean, dataset, datatype{type_i})];
            end
        end
        if strcmpi(dataset, 'train'); break; end    % For training data, far and near distances are in one list, so we can stop here.
    end
end
for i=1:length(wavlistClean)   % build an index of clean files so we can find them quickly by utterance ID
    [~,curr_uttID] = fileparts(wavlistClean{i});
    clean_struct.(['U_' curr_uttID]) = wavlistClean{i};
end

% Currently we don't support loading of frame label yet.
% switch lower(dataset)
%     case {'eval_simu'}
%         ali_file = [para.local.aliDir '_' dataset '/ali.txt'];
%     case 'tr05'
%         ali_file = [para.local.aliDir '/ali.txt'];
% end
% [ali, vocab] = LoadKaldiFrameLabel(ali_file);
vocab = [];

wavlist = wavlist(:,step:step:end);
wavlistClean = wavlistClean(:,step:step:end);

wavreader.name = 'wavfile';
wavreader.array = 1;
wavreader.multiArrayFiles = 1;

fs = 16000;
frame_size = fs*0.025;
frame_shift = fs*0.01;
if para.local.useFileName==0
    seglen = para.local.seglen;
    segshift = para.local.segshift;
end

% Load data file list
wav_noisy = {};  label = {};  wav_clean = {};
nUtt = size(wavlist,2);
for si = 1:nUtt
    words = ExtractWordsFromString_v2(wavlist{1,si}, '/');
    curr_uttID = words{end}(1:end-4);
    PrintProgress(si, nUtt, 100, curr_uttID);

    words2 = ExtractWordsFromString_v2(curr_uttID, '_');
    clean_uttID = words2{1};

    [wav] = InputReader(wavlist(:,si), wavreader);
    wav = StoreWavInt16(wav);
    if strcmpi(datatype, 'real')
        wav_c = wav; % note that for real data, we don't have clean version. so just use the noisy version. This line will only be run at test time.
    elseif strcmpi(dataset, 'train')
        wavfileClean = clean_struct.(['U_' clean_uttID]);
        wav_c = audioread(wavfileClean)';
    else
        [wav_c] = InputReader(wavlistClean(:,si), wavreader);
    end
    wav_c = StoreWavInt16(wav_c);

    % synchronize the length of label and wav
    nFr_feat = enframe_decide_frame_number(size(wav,2), frame_size, frame_shift);
    nFr_feat = min(nFr_feat, enframe_decide_frame_number(size(wav_c,2), frame_size, frame_shift));
    nFr_label = 10000;
    nFr = min([nFr_feat nFr_label]);
    requiredLen = DecideWavLen4XFrames(nFr, frame_size, frame_shift);
    wav(:,requiredLen+1:end) = [];
    wav_c(:,requiredLen+1:end) = [];

    if para.local.useFileName
        for i=1:nCh
            wavfileArray{i} = sprintf('%s 0 %2.3f', wavlist{i,si}, size(wav,2)/fs);
        end
        wav_noisy{end+1} = wavfileArray;
        if strcmpi(datatype, 'real')
            wav_clean{end+1} = wavfileArray{1};
        elseif strcmpi(dataset, 'train')
            wav_clean{end+1} = sprintf('%s 0 %2.3f', clean_struct.(['U_' clean_uttID]), size(wav,2)/fs);
        else
            wav_clean{end+1} = sprintf('%s 0 %2.3f', wavlistClean{1,si}, size(wav,2)/fs);
        end
    else
        if 1    % we use small segments rather than whole sentences as training examples
            wav_c_seg = DivideSent2Segments(wav_c, (seglen-1)*frame_shift+frame_size, segshift*frame_shift, 1);
            wav_seg = DivideSent2Segments(wav, (seglen-1)*frame_shift+frame_size, segshift*frame_shift, 1);
            wav_noisy = [wav_noisy; wav_seg];
            wav_clean = [wav_clean; wav_c_seg];
        else
            wav_noisy{end+1} = wav;
            wav_clean{end+1} = wav_c;
        end
    end
    if para.local.loadLabel
        for j = length(label)+1:length(wav_noisy)
            label{j} = [];
        end
    end
end

Data(1).data = wav_noisy;
Data(2).data = wav_clean;

para.IO.inputFeature = [1 1];
para.IO.DataSyncSet{1} = [];
para.IO.frame_rate = [16000 16000];
para.IO.isTensor = [1 1];
if para.local.useFileName
    para.IO.inputFeature([1 2]) = 0;
    wavreader.precision = 'int16';
    if isfield(para.IO, 'fileReader')
        para.IO = rmfield(para.IO, 'fileReader');
    end
    para.IO.fileReader(1) = wavreader;
    para.IO.fileReader(2) = wavreader;
    para.IO.fileReader(2).array = 0;
    para.IO.fileReader(2).multiArrayFiles = 0;
end
if para.local.loadLabel
    Data(3).data = label;
    para.IO.inputFeature(3) = 1;
    para.IO.frame_rate(3) = 100;
    para.IO.isTensor(3) = 1;
    para.IO.fileReader(3).name = '';
end
end


function events = in_events_kdf(sFile, EventFile)
% IN_EVENTS_KDF: Read the events descriptions from a KRISS .trg file.
%
% USAGE:  events = in_events_kdf(sFile, EventFile)

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

% Initialize returned variable
events = repmat(db_template('event'), 0);
% Load trigger file
EventDat = load(EventFile, '-ASCII');
if isempty(EventDat)
    return;
end

% Consider both the stim and responses as events of the same level
AllEvt = [EventDat(:,1:2); EventDat(EventDat(:,4)~=0, 3:4)];
% Get all the unique events
uniqueEvt = unique(AllEvt(:,2));
% Create events structures: one per category of event
for iEvt = 1:length(uniqueEvt)
    % Find all the occurrences of event #iEvt
    iMrk = find(AllEvt(:,2) == uniqueEvt(iEvt));
    % Add event structure
    events(iEvt).label      = num2str(uniqueEvt(iEvt));
    events(iEvt).epochs     = ones(1, length(iMrk));
    events(iEvt).times      = AllEvt(iMrk,1)' ./ sFile.prop.sfreq;
    events(iEvt).reactTimes = [];
    events(iEvt).select     = 1;
    events(iEvt).channels   = [];
    events(iEvt).notes      = [];
end


function rezToPhy(rez, savePath, varargin)
% pull out results from kilosort's rez to either return to workspace or to
% save in the appropriate format for the phy GUI to run on. If you provide
% a savePath it should be a folder, and you will need to have npy-matlab
% available (https://github.com/kwikteam/npy-matlab)

[~, Nfilt, Nrank] = size(rez.W);
rez.Wphy = cat(1, zeros(1+rez.ops.nt0min, Nfilt, Nrank), rez.W); % for Phy, we need to pad the spikes with zeros so the spikes are aligned to the center of the window

% spikeTimes will be in samples, not seconds
rez.W = gather(single(rez.Wphy));
rez.U = gather(single(rez.U));
rez.mu = gather(single(rez.mu));

if size(rez.st3,2)>4
    rez.st3 = rez.st3(:,1:4);
end

[~, isort]   = sort(rez.st3(:,1), 'ascend');
rez.st3      = rez.st3(isort, :);
if ~isempty(rez.cProj)
    rez.cProj    = rez.cProj(isort, :);
    rez.cProjPC  = rez.cProjPC(isort, :, :);
end

% ix = rez.st3(:,4)>12;
% rez.st3 = rez.st3(ix, :);
% rez.cProj = rez.cProj(ix, :);
% rez.cProjPC = rez.cProjPC(ix, :,:);

fs = dir(fullfile(savePath, '*.npy'));
for i = 1:length(fs)
   delete(fullfile(savePath, fs(i).name));
end
if exist(fullfile(savePath, '.phy'), 'dir')
    rmdir(fullfile(savePath, '.phy'), 's');
end

spikeTimes = uint64(rez.st3(:,1));
% account for ops.trange(1) to accomodate real time
spikeTimes = spikeTimes - rez.ops.trange(1)*rez.ops.fs;
% [spikeTimes, ii] = sort(spikeTimes);
spikeTemplates = uint32(rez.st3(:,2));
if size(rez.st3,2)>4
    spikeClusters = uint32(1+rez.st3(:,5));
end
amplitudes = rez.st3(:,3);

Nchan = rez.ops.Nchan;

xcoords     = rez.xcoords(:);
ycoords     = rez.ycoords(:);
chanMap     = rez.ops.chanMap(:);
chanMap0ind = chanMap - 1;

nt0 = size(rez.W,1);
U = rez.U;
W = rez.W;

Nfilt = size(W,2);

templates = zeros(Nchan, nt0, Nfilt, 'single');
for iNN = 1:size(templates,3)
   templates(:,:,iNN) = rez.mu(iNN,1) * squeeze(U(:,iNN,:)) * squeeze(W(:,iNN,:))';
end
templates = permute(templates, [3 2 1]); % now it's nTemplates x nSamples x nChannels
templatesInds = repmat([0:size(templates,3)-1], size(templates,1), 1); % we include all channels so this is trivial

templateFeatures = rez.cProj;
templateFeatureInds = uint32(rez.iNeigh);
pcFeatures = rez.cProjPC;
pcFeatureInds = uint32(rez.iNeighPC);

% whiteningMatrix = rez.Wrot/rez.ops.scaleproc;
whiteningMatrix = eye(size(rez.Wrot)) / rez.ops.scaleproc;
whiteningMatrixInv = whiteningMatrix^-1;

% here we compute the amplitude of every template...

% unwhiten all the templates
tempsUnW = zeros(size(templates));
for t = 1:size(templates,1)
    tempsUnW(t,:,:) = squeeze(templates(t,:,:))*whiteningMatrixInv;
end

% The amplitude on each channel is the positive peak minus the negative
tempChanAmps = squeeze(max(tempsUnW,[],2))-squeeze(min(tempsUnW,[],2));

% The template amplitude is the amplitude of its largest channel
tempAmpsUnscaled = max(tempChanAmps,[],2);

% assign all spikes the amplitude of their template multiplied by their
% scaling amplitudes
spikeAmps = tempAmpsUnscaled(spikeTemplates).*amplitudes;

% take the average of all spike amps to get actual template amps (since
% tempScalingAmps are equal mean for all templates)
ta = clusterAverage(spikeTemplates, spikeAmps);
tids = unique(spikeTemplates);
tempAmps = zeros(numel(rez.mu),1);
tempAmps(tids) = ta; % because ta only has entries for templates that had at least one spike
gain = getOr(rez.ops, 'gain', 1);
tempAmps = gain*tempAmps'; % for consistency, make first dimension template number

templateFeatures = [];
if ~isempty(savePath)
    fileID = fopen(fullfile(savePath, 'cluster_KSLabel.tsv'),'w');
    fprintf(fileID, 'cluster_id%sKSLabel', char(9));
    fprintf(fileID, char([13 10]));

    fileIDCP = fopen(fullfile(savePath, 'cluster_ContamPct.tsv'),'w');
    fprintf(fileIDCP, 'cluster_id%sContamPct', char(9));
    fprintf(fileIDCP, char([13 10]));

    fileIDA = fopen(fullfile(savePath, 'cluster_Amplitude.tsv'),'w');
    fprintf(fileIDA, 'cluster_id%sAmplitude', char(9));
    fprintf(fileIDA, char([13 10]));

    rez.est_contam_rate(isnan(rez.est_contam_rate)) = 1;
    for j = 1:length(rez.good)
        if rez.good(j)
            fprintf(fileID, '%d%sgood', j-1, char(9));
        else
            fprintf(fileID, '%d%smua', j-1, char(9));
        end
        fprintf(fileID, char([13 10]));

        fprintf(fileIDCP, '%d%s%.1f', j-1, char(9), rez.est_contam_rate(j)*100);
        fprintf(fileIDCP, char([13 10]));

        fprintf(fileIDA, '%d%s%.1f', j-1, char(9), tempAmps(j));
        fprintf(fileIDA, char([13 10]));

    end
    fclose(fileID);
    fclose(fileIDCP);
    fclose(fileIDA);

    writeNPY(spikeTimes, fullfile(savePath, 'spike_times.npy'));
    writeNPY(uint32(spikeTemplates-1), fullfile(savePath, 'spike_templates.npy')); % -1 for zero indexing
    if size(rez.st3,2)>4
        writeNPY(uint32(spikeClusters-1), fullfile(savePath, 'spike_clusters.npy')); % -1 for zero indexing
    else
        writeNPY(uint32(spikeTemplates-1), fullfile(savePath, 'spike_clusters.npy')); % -1 for zero indexing
    end
    writeNPY(amplitudes, fullfile(savePath, 'amplitudes.npy'));
    writeNPY(templates, fullfile(savePath, 'templates.npy'));
    writeNPY(templatesInds, fullfile(savePath, 'templates_ind.npy'));

    %chanMap0ind = int32(chanMap0ind);
    chanMap0ind = int32([1:rez.ops.Nchan]-1);
    writeNPY(chanMap0ind, fullfile(savePath, 'channel_map.npy'));
    writeNPY([xcoords ycoords], fullfile(savePath, 'channel_positions.npy'));

    if ~isempty(templateFeatures)
        writeNPY(templateFeatures, fullfile(savePath, 'template_features.npy'));
        writeNPY(templateFeatureInds'-1, fullfile(savePath, 'template_feature_ind.npy'));% -1 for zero indexing
        writeNPY(pcFeatures, fullfile(savePath, 'pc_features.npy'));
        writeNPY(pcFeatureInds'-1, fullfile(savePath, 'pc_feature_ind.npy'));% -1 for zero indexing
    end

    writeNPY(whiteningMatrix, fullfile(savePath, 'whitening_mat.npy'));
    writeNPY(whiteningMatrixInv, fullfile(savePath, 'whitening_mat_inv.npy'));

    if isfield(rez, 'simScore')
        similarTemplates = rez.simScore;
        writeNPY(similarTemplates, fullfile(savePath, 'similar_templates.npy'));
    end

    % save a list of "good" clusters for Phy
%     fileID = fopen(fullfile(savePath, 'channel_names.tsv'), 'w');
%     fprintf(fileID, 'cluster_id%sKSLabel', char(9));
%     for j = 1:Nchan
%         fprintf(fileID, '%d%s%d', j-1,char(9),chanMap0ind(j));
%         fprintf(fileID, char([13 10]));
%     end
%     fclose(fileID);

    % Duplicate "KSLabel" as "group", a special metadata ID for Phy, so that
    % filtering works as expected in the cluster view
    KSLabelFilename = fullfile(savePath, 'cluster_KSLabel.tsv');
    copyfile(KSLabelFilename, fullfile(savePath, 'cluster_group.tsv'));

     %make params file
    if ~exist(fullfile(savePath,'params.py'),'file')
        fid = fopen(fullfile(savePath,'params.py'), 'w');

%        [~, fname, ext] = fileparts(rez.ops.fbinary);
%         fprintf(fid,['dat_path = ''',fname ext '''\n']);
%         fprintf(fid,'n_channels_dat = %i\n',rez.ops.NchanTOT);
        if ~isempty(varargin)
            [root, fname, ext] = fileparts(rez.ops.fbinary);
        else
            [root, fname, ext] = fileparts(rez.ops.fproc);
        end
%         fprintf(fid,['dat_path = ''',fname ext '''\n']);
        fprintf(fid,['dat_path = ''', strrep(rez.ops.fproc, '\', '/') '''\n']);

        fprintf(fid,'n_channels_dat = %i\n',rez.ops.Nchan);

        fprintf(fid,'dtype = ''int16''\n');
        fprintf(fid,'offset = 0\n');
        if mod(rez.ops.fs,1)
            fprintf(fid,'sample_rate = %i\n',rez.ops.fs);
        else
            fprintf(fid,'sample_rate = %i.\n',rez.ops.fs);
        end
%         fprintf(fid,'hp_filtered = False');
        fprintf(fid,'hp_filtered = True');

        fclose(fid);
    end
end


function val = get(w,prop_name)
   %GET Get waveform properties
   %   val = get(waveform,prop_name)
   %
   %   Valid property names:
   %       CHANNELTAG, NETWORK, STATION, LOCATION, CHANNEL,
   %       CHANNELINFO : (gets the info as a string 'Net.Sta.Loc.Cha')
   %       FREQ, START_STR,
   %       END_STR, DATA, NYQ, PERIOD
   %     also:
   %       START, END : return datenum format (MatLab's format)
   %       START_EPOCH, END_EPOCH : return epoch format.
   %
   %       DATA_LENGTH : return number of elements in data
   %
   %       The following return time duration of data
   %       DURATION_STR :  text format
   %       DURATION: matlab (datenum) format
   %       DURATION_EPOCH : antelope (epoch) format (just # of seconds)
   %
   %       TIMEVECTOR : return a vector of same length as data with
   %                    matlab-formatted times.
   %
   %       MISC_FIELDS: gets a list of fields that were added to this waveform
   %       UNITS : Find out what units the data is in (ex. counts, nm/s)
   %
   %   If waveform is N-dimensional, then VAL will be a cell of the same
   %   dimensions.  If GET would return single-values for the property, then
   %   VAL will be a matrix of type DOUBLE, arranged in the same dimensions.
   %
   %       If additional fields were added to waveform using ADDFIELD, then
   %       values from these can be retrieved using the fieldname
   %
   %       Example: Create a waveform, add a field, then get the field
   %           W = waveform;
   %           W = addfield(W,'P-pick', datenum('1/5/2007 02:12:15'));
   %           misc = get(W,'MISC_FIELDS'); %returns a cell with 'P-pick'
   %           PickTime = get(W,'P-pick'); % (same as) PickTime = get(W,misc);
   %
   %
   %   See also WAVEFORM/GETM, WAVEFORM/SET, WAVEFORM/ADDFIELD,
   %   WAVEFORM/DELFIELD

   %  11/28/2008 added WAVEFORM_OBJECT_VERSION, which returns a single version
   %  field from the waveform object.  This is set with the constructor, and
   %  is used in load_obj.

   % AUTHOR: Celso Reyes, Geophysical Institute, Univ. of Alaska Fairbanks
   % $Date$
   % $Revision$

   %global mep2dep

   %val_CELL = cell(size(w));
   %val = val_CELL;
   usedcell = false;
   singleWave = isscalar(w);
   switch upper(prop_name)

      % IDENTIFICATION PROPERTIES
      case {'STATION','CHANNEL','NETWORK','LOCATION'} %type:CELL
         val = get([w.cha_tag],prop_name);
         usedcell = true;

      case {'COMPONENT'} %type:CELL   GRANDFATHERED IN...
         val = get([w.cha_tag],'channel');
         usedcell = true;

         % DATA DESCRIBING PROPERTIES

      case {'FS', 'FREQ'} %type:DOUBLE
         val = [w.Fs];

      case 'DATA' %type:DOUBLE
         if singleWave
            val = w.data;
         else
            val = {w.data}; %cells of data values
            usedcell = true;
         end

      case 'NYQ' %type:DOUBLE
         val = [w.Fs] ./ 2;

      case 'PERIOD' %type:DOUBLE
         val = 1 ./ [w.Fs];

      case 'DATA_LENGTH' %type:DOUBLE
         val = zeros(size(w));
         for N = 1: numel(w)
            val(N) = numel(w(N).data);
         end

         % TIME PROPERTIES

      case {'START_STR'} %type:CELL
         val = stringDate([w.start]);
         usedcell = true;

      case {'START_EPOCH'} %type:DOUBLE
         val = mep2dep([w.start]);

      case {'START_MATLAB', 'START'} %type:DOUBLE
         val = [w.start];

      case {'END_STR'} %type:CELL
         val = stringDate(grabEndTime(w));
         usedcell = true;

      case {'END_EPOCH'} %type:DOUBLE
         val = mep2dep(grabEndTime(w));

      case {'END_MATLAB', 'END'} %type:DOUBLE
         val = grabEndTime(w);

      case {'DURATION_STR'} %type:CELL
         endvec = grabEndTime(w(:)) - [w(:).start];
         if numel(w) == 0
            val = durationDate(nan);
         elseif singleWave
            val = durationDate(endvec);
         else
            val = cell(size(w));
            for N = 1 : numel(w)
               val(N) = {durationDate(endvec(N))};
            end
            usedcell = true;
         end
      case {'DURATION_EPOCH'} %type:DOUBLE
         val = (grabEndTime(w) - [w.start]) * 86400;
         %epoch time is # of seconds.

      case {'DURATION_MATLAB', 'DURATION'} %type:DOUBLE
         val = grabEndTime(w) - [w.start];

      case {'TIMEVECTOR'} %type:CELL
         val = cell(size(w));
         for N = 1 : numel(w)
            Xvalues = linspace(w(N).start,get(w(N),'end'),length(w(N).data)+1);
            val{N} = Xvalues(1:end-1)';
         end
         usedcell = true;

      case {'MISC_FIELDS'} %type:CELL
         val = {w.misc_fields};
         usedcell = true;

      case {'UNITS'} %type : CELL
         val = {w.units};
         usedcell = true;

      case {'WAVEFORM_OBJECT_VERSION'}
         val = w(1).version;

      case {'SCNLOBJECT'} % legacy only
         % I'm making this whiny for now so it is obvious when things need to
         % be changed.  This warning should probably be pulled out once we
         % rerelease this.
         warning('waveform:get:scnlobjectsAreDepricated',...
            ['any usage of scnlobject should be replaced by ', ...
            '''channelinfo'' which uses the new ChannelTag class']);
         val = scnlobject([w.cha_tag]);
         %must add network & location, too.

      case {'CHANNELTAG'}
         val = [w.cha_tag];

      case {'CHANNELINFO'}
         val = string([w.cha_tag]);

      case {'HISTORY'}
         val = cell(size(w));
         for n=1:numel(w)
            val(n) = {w(n).history};
         end
         usedcell = true;

      otherwise
         %perhaps we're trying to get at one of the miscelleneous fields?
         val = cell(size(w));
         for n = 1 : numel(w)
            %loc is the position...
            %w(n).misc_fields should ALWAYS already be in uppercase
            mask = strcmp(upper(prop_name),w(n).misc_fields);
            %fieldwasfound = any(mask);
            %[fieldwasfound, loc] = ismember(prop_name, w(n).misc_fields);
            if any(mask)

               val{n} = w(n).misc_values{mask};
               %val{n} = w(n).misc_values{m};
            else
               error('Waveform:get:unrecognizedProperty',...
                  'Unrecognized property name : %s',  prop_name);
            end
         end
         %check to see if value can be returned as a numeric value instead
         %of cell.  Only if all values are numeric AND scalar
         numberize = true;
         for n=1:numel(val)
            if ~(isnumeric(val{n}) && isscalar(val{n}))
               numberize = false;
               usedcell = true;
               break
            end
         end
         if numberize,
            Z = val;
            val = nan(size(Z));
            for n=1:numel(Z)
               val(n) = Z{n};
            end
         end

   end;
   if (numel(val) == numel(w)) %THIS TEST CONDITIONALIZED 4/16/2008
      val = reshape(val,size(w)); %return values in proper shape
   end
   if usedcell && singleWave
      if isa(val,'cell')
         val = val{1}; % return the actual value, not a cell array
      end
   end;
end
%%%%%%%%%%%%%%%%
function val = grabEndTime(w)
   if isempty(w)
      %val = [];
      val = w.start;
      return
   end
   dlens = get(w,'data_length');
   dlens = dlens(:);

   myfrq = [w.Fs];
   myfrq = myfrq(:);

   seclen = dlens ./ myfrq;
   to_add = datenum([zeros(numel(w),5) seclen])';
   svals = [w(:).start];
   val = svals +  to_add;

   %endvec = datevec([w.start]) + [0 0 0 0 0 length(w.data)/w.Fs];
   %endvec = datevec([w.start]) + [0 0 0 0 0 get(w,'data_length') ./ get(w,'Freq')];
   %val = datenum(endvec);

end
%%%%%%%%%%%%%%%%
function val = stringDate(myDate)
   if isnan(myDate)
      warning('waveform:get:undefinedDate','Undefined date');
      val = 0;
      return
   end
   val = datestr(myDate,'yyyy-mm-dd HH:MM:SS.FFF');
end
%%%%%%%%%%%%%%%%
function val = durationDate(myDate)

   if isnan(myDate)
      %this likely happened because the frequency was undefined (NaN)
      val = '';
      return;
   end

   myDate = myDate(:);
   [yr, mo, da] = datevec(myDate);
   val = '';
   %if yr, val = [val sprintf('%2d years ',yr)]; end
   %if mo, val = [val sprintf('%2d months ',mo)]; end
   if (yr+mo+da)>1,
      val = [val sprintf('%4d days ',fix(myDate))];
   end
   val = [val datestr(myDate,'HH:MM:SS.FFF')];
end


function events = in_events_oebin(sFile, EventFile)
% IN_EVENTS_OEBIN: Import events from a Open Ephys flat binary event file (timestamps.npy)
%
% USAGE:  events = in_events_oebin(sFile, EventFile)

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

% Read time stamps
evtTime = reshape(readNPY(EventFile), 1, []);
if isempty(evtTime)
    events = [];
    return
end

% Find other files, indicating the type of event
[evtDir, fName] = bst_fileparts(EventFile);
TextFile = bst_fullfile(evtDir, 'text.npy');
% ChanFile = bst_fullfile(evtDir, 'channels.npy');
ChanStateFile = bst_fullfile(evtDir, 'channel_states.npy');

% Get event labels
if file_exist(TextFile)
    disp('EOBIN> ERROR: Text events are not supported yet...');
    evtGroupLabel = {'TEXT'};
    evtGroupInd = {1:length(evtTime)};
    evtChan = [];

%     evtLabels = readNPY(TextFile);
%     % Create event groups
%     evtGroupLabel = unique(evtLabels);
%     evtGroupInd = cell(1, length(evtGroupLabel));
%     for iUnique = 1:length(evtGroupLabel)
%         evtGroupInd{iUnique} = find(strcmpi(evtLabels, evtGroupLabel{iUnique}));
%     end
%     % Read event channel
%     if file_exist(ChanFile)
%         evtChan = readNPY(ChanFile);
%     else
%         evtChan = [];
%     end
elseif file_exist(ChanStateFile)
    evtVal = readNPY(ChanStateFile);
    % Create event groups
    uniqueVal = unique(evtVal);
    evtGroupLabel = cell(1, length(uniqueVal));
    evtGroupInd = cell(1, length(uniqueVal));
    for iUnique = 1:length(uniqueVal)
        evtGroupLabel{iUnique} = num2str(uniqueVal(iUnique));
        evtGroupInd{iUnique} = find(evtVal == uniqueVal(iUnique));
    end
    % Do not get read channels
    evtChan = [];
else
    evtGroupLabel = {'Unknown'};
    evtGroupInd = {1:length(evtTime)};
    evtChan = [];
end

% Initialize list of events
events = repmat(db_template('event'), [1, length(evtGroupLabel)]);
% Get occurrences for each event
for iEvt = 1:length(evtGroupLabel)
    events(iEvt).label      = evtGroupLabel{iEvt};
    events(iEvt).times      = double(evtTime(evtGroupInd{iEvt})) ./ sFile.prop.sfreq;
    events(iEvt).epochs     = ones(1, length(events(iEvt).times));  % Epoch: set as 1 for all the occurrences
    events(iEvt).reactTimes = [];
    events(iEvt).select     = 1;
    events(iEvt).notes      = [];
    events(iEvt).channels   = [];
end


function ni = niftiSet(ni,param,val,varargin)
% Set data for various nifti data structures
%
%     ni = niftiSet(ni,param,val,varargin)
%
% Parameter values
%
%  'pixdim'    - Voxel dimensions in millimeters (though settable)
%  'dim'       - Number of pixels in each dimension (row,col, depth, time/orientation)
%  'data'      - The data
%  'voxelsize' - What is this?  Why isn't it pixdim?
%
%  'checkqto'
%
% Example:
%
%   ni = niftiCreate;
%   ni = niftiSet(ni,'voxel size',[0.375, 0.375, 2]);
%
%
% EKA/BW Vistasoft Team, 2015

%% Check the parameters
if notDefined('ni'), error('Nifti data structure variable required'); end
if notDefined('param'), error('Parameter field required.'); end
if ~exist('val','var'), error('Val required'); end

%% Squeeze spaces out, force lower case
param = mrvParamFormat(param);

%TODO: Add a nifti paramaterMapField
switch param
    case 'checkqto'

        sz = size(niftiGet(ni,'data'));
        dim = niftiGet(ni,'dim');

        if(any(dim(1:3)~=sz(1:3)))
            warning('[%s] NIFTI volume dim wrong- setting it to the actual data size.\n',mfilename);
            dim(1:3) = sz(1:3);
            ni = niftiSet(ni,'dim',dim);
        end

        if(ni.qform_code==0 && ni.sform_code~=0)
            warning('[%s] ni.qform_code is zero and sform_code ~=0. Setting ni.qto_* from ni.sto_*...\n',mfilename);
            %ni = niftiSetQto(ni, ni.sto_xyz);
            ni = niftiSet(ni,'qto',niftiGet(ni,'sto_xyz'));
        end

        dim = niftiGet(ni,'dim');
        qto_ijk = niftiGet(ni,'qto_ijk');
        origin = [qto_ijk(1:3,:)*[0 0 0 1]']';
        if(any(origin<2)||any(origin>dim(1:3)-2))
            [~,r,s,k] = affineDecompose(niftiGet(ni, 'qto_ijk'));
            t = ni.dim/2;
            warning_string = [sprintf('[%s] Qto matrix defines an origin very far away from the isocenter.\n',mfilename),...
                              sprintf('This implies that the Qto matrix may be bad - please check qto_ijk. An automatic fix will be attempted.\n'),...
                              sprintf('Origin to the image center is at [%2.3f,%2.3f,%2.3f] pix.\n',t(1),t(2),t(3))];
            warning(warning_string);
            %ni = niftiSetQto(ni, inv(affineBuild(t,r,s,k)));
            ni = niftiSet(ni,'qto',inv(affineBuild(t,r,s,k)));

        end

    case 'data'
        ni.data = val;

    case 'dim'
        ni.dim = val;

    case 'filepath'
        ni.fname = val;

    case 'freqdim'
        ni.freq_dim = val;

    case 'nifti'
        ni = val; %This means that we are passing in an entire Nifti!

    case 'phasedim'
        ni.phase_dim = val;

    case 'pixdim'
        ni.pixdim = val;

    case 'qfac'
        ni.qfac= val;

    case 'qform_code'
        ni.qform_code = val;

    case 'qoffset_x'
        ni.qoffset_x = val;

    case 'qoffset_y'
        ni.qoffset_y = val;

    case 'qoffset_z'
        ni.qoffset_z = val;

    case 'qto'
        xformXyz = val;
        q = matToQuat(xformXyz);
        ni = niftiSet(ni,'qform_code',2);
        ni = niftiSet(ni,'qto_xyz',xformXyz);
        ni = niftiSet(ni,'qto_ijk',inv(xformXyz));
        ni = niftiSet(ni,'quatern_b',q.quatern_b);
        ni = niftiSet(ni,'quatern_c ',q.quatern_c);
        ni = niftiSet(ni,'quatern_d',q.quatern_d);
        ni = niftiSet(ni,'qoffset_x',q.quatern_x);
        ni = niftiSet(ni,'qoffset_y',q.quatern_y);
        ni = niftiSet(ni,'qoffset_z',q.quatern_z);
        ni = niftiSet(ni,'qfac',q.qfac);

        if length(varargin) < 1
            setStoToo = '';
        else
            setStoToo = varargin{1};
        end

        if(~isempty(setStoToo)&&setStoToo)
            ni = niftiSet(ni,'sto_xyz',niftiGet(ni,'qto_xyz'));
            ni = niftiSet(ni,'sto_ijk',niftiGet(ni,'qto_ijk'));
        end

    case 'qto_ijk'
        ni.qto_ijk = val;

    case 'qto_xyz'
        ni.qto_xyz = val;

    case 'quatern_b'
        ni.quatern_b = val;

    case 'quatern_c'
        ni.quatern_c = val;

    case 'quatern_d'
        ni.quatern_d = val;

    case 'slicedim'
        ni.slice_dim = val;

    case 'sto_ijk'
        ni.sto_ijk = val;

    case 'sto_xyz'
        ni.sto_xyz = val;

    case 'voxelsize'
        ni.voxelSize = val;

    otherwise
        error('Unknown parameter %s\n',param);

end %switch

return


function events = in_events_array(sFile, EventFile, format, EventName, isInteractive)
% IN_EVENTS_ARRAY: Read events information from a .mat or text file
%
% USAGE:  events = in_events_array(sFile, EventFile, 'times',   EventName=[ask], isInteractive=1)
%         events = in_events_array(sFile, EventFile, 'samples', EventName=[ask], isInteractive=1)
%         events = in_events_array(sFile, EventMat, ...)

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

% Parse inputs
if (nargin < 5) || isempty(isInteractive)
    isInteractive = 1;
end
if (nargin < 4) || isempty(EventName)
    EventName = [];
end

% ===== READ FILE =====
if ischar(EventFile)
    % Can read ASCII or .mat
    EventsMat = load(EventFile);
    if isstruct(EventsMat)
        fields = fieldnames(EventsMat);
        EventsMat = EventsMat.(fields{1});
        if isstruct(EventsMat)
            fields = fieldnames(EventsMat);
            EventsMat = EventsMat.(fields{1});
        end
    end
else
    EventsMat = EventFile;
end
% Force to be double
EventsMat = double(EventsMat);
% Check orientation
if (size(EventsMat,2) < size(EventsMat,1))
    EventsMat = EventsMat';
end

% ===== FORMAT EVENTS =====
% Create [samples; times] array
switch (format)
    case 'times'
        evtTimes = round(EventsMat .* sFile.prop.sfreq) ./ sFile.prop.sfreq;
    case 'samples'
        evtTimes = round(EventsMat) ./ sFile.prop.sfreq;
end

% ===== TIME OFFSET =====
if isInteractive
    % Check for offset (typical of FIF files)
    isAddOffset = 0;
    if (sFile.prop.times(1) ~= 0)
        res = java_dialog('question', ['The raw data file starts at ' num2str(sFile.prop.times(1)) ' sec.' 10 10 ...
                                      'Is this offset already added to these events?' 10 10],...
                                      'Import events', [], {'Yes', 'Add Offset','Cancel'},'Yes');
        if isempty(res) || strcmpi(res, 'Cancel')
            bst_progress('stop');
            return;
        elseif strcmpi(res, 'Add Offset')
            isAddOffset = 1;
        end
    end
    % Add a column for time
    if isAddOffset
        evtTimes = evtTimes + sFile.prop.times(1);
    end
end

% ===== CONVERT TO BRAINSTORM STRUCTURE =====
% Initialize list of events
events = db_template('event');
% Ask for a label
if ~isempty(EventName)
    events.label = EventName;
else
    res = java_dialog('input', 'Please enter a label for this event:', 'Event Label');
    if isempty(res)
        events.label = '1';
    else
        events.label = res;
    end
end
events.color      = [];
events.reactTimes = [];
events.select     = 1;
events.times      = evtTimes;
events.epochs     = ones(1, length(evtTimes));  % Epoch: set as 1 for all the occurrences
events.channels   = [];
events.notes      = [];


function plotData(x, y)
%PLOTDATA Plots the data points x and y into a new figure
%   PLOTDATA(x,y) plots the data points and gives the figure axes labels of
%   population and profit.

% ====================== YOUR CODE HERE ======================
% Instructions: Plot the training data into a figure using the
%               "figure" and "plot" commands. Set the axes labels using
%               the "xlabel" and "ylabel" commands. Assume the
%               population and revenue data have been passed in
%               as the x and y arguments of this function.
%
% Hint: You can use the 'rx' option with plot to have the markers
%       appear as red crosses. Furthermore, you can make the
%       markers larger by using plot(..., 'rx', 'MarkerSize', 10);

figure; % open a new figure window

plot(x, y, 'rx', 'MarkerSize', 10); % Plot the data
ylabel('Profit in $10,000s'); % Set the y􀀀axis label
xlabel('Population of City in 10,000s'); % Set the x􀀀axis label

% ============================================================

end


function plotData(x, y)
%PLOTDATA Plots the data points x and y into a new figure
%   PLOTDATA(x,y) plots the data points and gives the figure axes labels of
%   population and profit.

% ====================== YOUR CODE HERE ======================
% Instructions: Plot the training data into a figure using the
%               "figure" and "plot" commands. Set the axes labels using
%               the "xlabel" and "ylabel" commands. Assume the
%               population and revenue data have been passed in
%               as the x and y arguments of this function.
%
% Hint: You can use the 'rx' option with plot to have the markers
%       appear as red crosses. Furthermore, you can make the
%       markers larger by using plot(..., 'rx', 'MarkerSize', 10);

figure; % open a new figure window
plot(x, y, 'rx','MarkerSize', 10);
ylabel('Profit in $10,000s');
xlabel('Population of City in 10,000s');

% ============================================================

end


function plotData(x, y)
%PLOTDATA Plots the data points x and y into a new figure
%   PLOTDATA(x,y) plots the data points and gives the figure axes labels of
%   population and profit.

% ====================== YOUR CODE HERE ======================
% Instructions: Plot the training data into a figure using the
%               "figure" and "plot" commands. Set the axes labels using
%               the "xlabel" and "ylabel" commands. Assume the
%               population and revenue data have been passed in
%               as the x and y arguments of this function.
%
% Hint: You can use the 'rx' option with plot to have the markers
%       appear as red crosses. Furthermore, you can make the
%       markers larger by using plot(..., 'rx', 'MarkerSize', 10);

figure; % open a new figure window

plot(x,y,'rx','MarkerSize',10);%rx means color is red and show symbol is x,and x's size is 10
ylabel=('Profit in $10,000s');
xlabel=('Population of City in 10,000s');

% ============================================================

end