function behaviorAnnotator( fName, aName, tName )
% Caltech Behavior Annotator.
%
% Can only be used to annotated seq files. To annotate other types of video
% files, they need to be converted to seq files first. For example, to
% convert an avi file into an seq file, use the following command:
%  seqIo([seqName],'frImgs',struct('codec','jpg'),'aviName',[aviName])
% Where [seqName] denotes the name of the seq you want to save to and
% [aviName] is the avi file. See seqIo and seqIo>frimgs for more details.
%
% Use arrow keys to play the video. Controls are as follows:
% (1) If stopped: [L] play backward, [R] play forward
% (2) If playing forward: [L] halve speed, [R] double speed, [U/D] stop
% (3) If playing backward: [L] double speed, [R] halve speed,[U/D] stop
% You can explicitly enter a frame or use the slider to jump in the video.
%
% USAGE
%  behaviorAnnotator( [fName], [aName], [tName] )
%
% INPUTS
%  fName    - optional seq file to load at start
%  aName    - optional annotation file to load or import at start
%  tName    - optional tracking or detection file to load at start
%
% OUTPUTS
%
% EXAMPLE
%  behaviorAnnotator
%
% See also behaviorData, seqIo
%
% Piotr's Image&Video Toolbox      Version 2.60
% Copyright 2012 Piotr Dollar.  [pdollar-at-caltech.edu]
% Please email me if you find bugs, or have suggestions or questions!
% Licensed under the Simplified BSD License [see external/bsd.txt]

% handles to gui objects / other globals
[hFig,menu,pTop,pMid,pBot,dispApi,A,trk] = deal([]);

% initialize all
makeLayout();
menuApi = menuMakeApi();
dispApi = dispMakeApi();
annApi  = annMakeApi();
menuApi.vidClose();

% open vid, annotation and tracking results if given
if(nargin>=1 && ~isempty(fName)), menuApi.vidOpen(fName); end
if(nargin>=2 && ~isempty(aName)), menuApi.annOpen(aName); end
if(nargin>=3 && ~isempty(tName)), menuApi.trkOpen(tName); end

  function makeLayout()
    % common properties
    name = 'Caltech Behavior Annotator';
    bg='BackgroundColor'; fg='ForegroundColor';
    fs='FontSize'; ha='HorizontalAlignment';
    units = {'Units','pixels'}; st='String'; ps='Position';

    % initial figures size / pos
    set(0,'Units','pixels');  ss = get(0,'ScreenSize');
    if( ss(3)<800 || ss(4)<600 ); error('screen too small'); end;
    figPos = [(ss(3)-600)/2 (ss(4)-500)/2 600 500];

    % create main figure
    figPrp = {'NumberTitle','off', 'Toolbar','auto', 'MenuBar','none', 'Color','k'};
    hFig = figure(figPrp{:},'Visible','off', ps,figPos, 'Name',name );
    set(hFig,'DeleteFcn',@exitProg,'ResizeFcn',@figResized);

    % mid panel
    pMid.hAx=axes(units{:},'Parent',hFig); set(pMid.hAx,'XTick',[],'YTick',[]);
    pMid.hSl=uicontrol(hFig,'Style','slider','Min',0,'Max',1,bg,'k'); imshow(0);
    pMid.hBar=uicontrol(hFig,'Style','pushbutton',units{:},'Enable','inactive');
    pMid.hAdd=uicontextmenu('Parent',hFig); pMid.hAdds=[]; pMid.hTxt=[];

    % top panel
    pnlProp = [units {bg,[.1 .1 .1],'BorderType','none'}];
    txtPrp = {'Style','text',bg,[.1 .1 .1],fg,'w',ha};
    edtPrp = {'Style','edit',bg,[.1 .1 .1],fg,'w',ha};
    pTop.h = uipanel(pnlProp{:},'Parent',hFig);
    pTop.hBehLbl=uicontrol(pTop.h,txtPrp{:},'Left',st,'behavior:');
    pTop.hBh=uicontrol(pTop.h,units{:},'Style','popupmenu',fg,'w',bg,'k',st,{''});
    pTop.hFrmLbl=uicontrol(pTop.h,txtPrp{:},'Left',st,'frame:');
    pTop.hFrmInd=uicontrol(pTop.h,edtPrp{:},'Right');
    pTop.hTmLbl=uicontrol(pTop.h,txtPrp{:},'Left',st,'time:');
    pTop.hTmVal=uicontrol(pTop.h,txtPrp{:},'Left');
    pTop.hFrmNum=uicontrol(pTop.h,txtPrp{:},'Left');
    pTop.hPlyLbl=uicontrol(pTop.h,txtPrp{:},'Left',st,'speed:');
    pTop.hPlySpd=uicontrol(pTop.h,txtPrp{:},'Left',st,'0x');

    % bottom panel
    pBot.h=uipanel(pnlProp{:},'Parent',hFig); uiPr=[pBot.h,units,'Style'];
    uiButPr=[uiPr,'pushbutton', bg,[.9 .9 .9], fs,11, 'FontWeight','bold'];
    pBot.hStrm=uicontrol(uiPr{:},'popupmenu',fg,'w',bg,'k',st,{''});
    pBot.hBh=uicontrol(uiPr{:},'popupmenu',fg,'w',bg,'k',st,{''});
    pBot.hArrL=uicontrol(uiButPr{:},fg,'k',st,'<<');
    pBot.hSl=uicontrol(uiPr{:},'slider',bg,'k');
    pBot.hBar=uicontrol(uiPr{:},'pushbutton','Enable','inactive');
    pBot.hArrR=uicontrol(uiButPr{:},fg,'k',st,'>>');
    pBot.hSetL=uicontrol(uiPr{:},'pushbutton',fg,'k',bg,[.9 .9 .9],fs,8);
    pBot.hSetR=uicontrol(uiPr{:},'pushbutton',fg,'k',bg,[.9 .9 .9],fs,8);
    pBot.hZm=uicontrol(uiPr{:},'popupmenu',fg,'w',bg,'k',...
      st,int2str2([30 60 120 250*2.^(0:5)]),'Value',5);
    pBot.hDel=uicontrol(uiButPr{:},fg,[.5 0 0],st,'X');

    % icons for set region button
    I=ones(20,18); I(9:15,10:16)=triu(ones(7));
    I(14:15,4:13)=0; I=repmat(I,[1 1 3]);
    set(pBot.hSetL,'CData',I(:,end:-1:1,:));
    set(pBot.hSetR,'CData',I);

    % set the keyPressFcn for all focusable components (except popupmenus)
    set( [hFig, pMid.hSl pBot.hArrL pBot.hSl pBot.hArrR ...
      pBot.hSetL pBot.hSetR pBot.hDel], 'keyPressFcn',@keyPress );

    % create menus
    menu.hVid = uimenu(hFig,'Label','Video');
    menu.hVidOpn = uimenu(menu.hVid,'Label','Open');
    menu.hVidsOpn = uimenu(menu.hVid,'Label','Open dual');
    menu.hVidExp = uimenu(menu.hVid,'Label','Export');
    menu.hVidCls = uimenu(menu.hVid,'Label','Close');
    menu.hVidInf = uimenu(menu.hVid,'Label','Info');
    menu.hVidAud = uimenu(menu.hVid,'Label','Audio');
    menu.hAnn = uimenu(hFig,'Label','Annotation');
    menu.hAnnNew = uimenu(menu.hAnn,'Label','New');
    menu.hAnnOpn = uimenu(menu.hAnn,'Label','Open');
    menu.hAnnCls = uimenu(menu.hAnn,'Label','Close');
    menu.hAnnSav = uimenu(menu.hAnn,'Label','Save');
    menu.hAnnCnf = uimenu(menu.hAnn,'Label','Config');
    menu.hAnnMrg = uimenu(menu.hAnn,'Label','Merge');
    menu.hTrk = uimenu(hFig,'Label','Tracking');
    menu.hTrkOpn = uimenu(menu.hTrk,'Label','Open');
    menu.hTrkCls = uimenu(menu.hTrk,'Label','Close');

    % set hFig to visible upon completion
    set(hFig,'Visible','on'); drawnow;

    function keyPress( h, evnt )
      char=int8(evnt.Character); if(isempty(char)), char=0; end;
      % arrow keys control either video, main slider, or bottom slider
      if( char>=28 && char<=31 ) % L/R/U/D = 28/29/30/31
        if( h==hFig )
          if(char>=30), flag=0; else flag=double(char-28)*2-1; end
          dispApi.setSpeedCb(flag);
        elseif( h~=pBot.hSl && h~=pMid.hSl )
          flag = mod(double(char),2)*2-1;
          annApi.setFrame(flag);
        end
        return;
      end
      % all other keys require an annotation to be loaded
      if(isempty(A)), return; end; bp=[];
      % '-'/'=' control jumps, ','/'.' control moveLeft/Right
      if(char=='-'), bp='prevBeh'; end
      if(char=='='), bp='nextBeh'; end
      if(char==','), bp='moveLeft'; end
      if(char=='.'), bp='moveRight'; end
      if(~isempty(bp)), annApi.buttonPress(bp); return; end
      % make bindings for behavior creation
      [member,type]=ismember( char, int8(A.getKeys()) );
      if(member), annApi.insertBh(type); return; end;
    end

    function figResized( h, evnt ) %#ok<INUSD>
      % aspect ratio of video
      if(isempty(dispApi)), info=[]; else info=dispApi.getInfo(); end
      if(isempty(info)), ar=4/3; else ar=info.width/info.height; end
      % enforce minimum size (min width=500+pad*2)
      pos=get(hFig,ps); pad=8; htBot=26; htSl=20; htTop=20;
      mWd=500+pad*2; mHt=500/ar+htBot+htSl+htTop+pad*2;
      persistent posPrv;
      if(pos(3)<mWd || pos(4)<mHt && ~isempty(posPrv))
        set(hFig,ps,[posPrv(1:2) mWd mHt]); figResized(); return;
      end; posPrv=pos;
      % overall layout
      wd=pos(3)-2*pad; ht=pos(4)-2*pad-htSl-htTop-htBot;
      wd=min(wd,ht*ar); ht=min(ht,wd/ar); x=(pos(3)-wd)/2; y=pad;
      set(pBot.h,ps,[x y wd htBot]); y=y+htBot;
      set(pMid.hSl,ps,[x y wd htSl]); y=y+htSl;
      if(~isempty(pMid.hTxt)), set(pMid.hTxt,ps,[wd/2 ht]); end
      set(pMid.hAx,ps,[x y wd ht]); y=y+ht;
      set(pTop.h,ps,[x y wd htTop]);
      % position stuff in top panel
      x=10;
      set(pTop.hBehLbl,ps,[x 3 50 14]); x=x+50;
      set(pTop.hBh,ps,[x 7 140 14]); x=wd-300;
      set(pTop.hPlyLbl,ps,[x 3 40 14]); x=x+40;
      set(pTop.hPlySpd,ps,[x 3 40 14]); x=x+40;
      set(pTop.hTmLbl,ps,[x 3 30 14]); x=x+30;
      set(pTop.hTmVal,ps,[x 3 45 14]); x=x+50;
      set(pTop.hFrmLbl,ps,[x 3 35 14]); x=x+35;
      set(pTop.hFrmInd,ps,[x 3 50 16]); x=x+50;
      set(pTop.hFrmNum,ps,[x 3 60 14]);
      % position stuff in bottom panel
      x=5; wd0=wd-325;
      set(pBot.hStrm,ps,[x 5 35 20]); x=x+40;
      set(pBot.hBh,  ps,[x 5 85 20]); x=x+95;
      set(pBot.hArrL,ps,[x 4 25 20]); x=x+25;
      set(pBot.hSl, ps,[x 4 wd0 20]); x=x+wd0;
      set(pBot.hArrR,ps,[x 4 25 20]); x=x+30;
      set(pBot.hSetL,ps,[x 4 20 20]); x=x+20;
      set(pBot.hSetR,ps,[x 4 20 20]); x=x+25;
      set(pBot.hZm,  ps,[x 5 50 20]); x=x+60;
      set(pBot.hDel, ps,[x 4 20 20]);
      % update display
      if(~isempty(dispApi)); dispApi.requestUpdate(); end;
    end

    function exitProg( h, evnt ) %#ok<INUSD>
      menuApi.vidClose();
    end
  end

  function api = annMakeApi()
    % create api
    clrs=[]; fM=0;
    api = struct( 'updateDisp',@updateDisp, 'updateAnn',@updateAnn, ...
      'setFrame',@setFrame, 'buttonPress',@buttonPress, ...
      'insertBh',@insertBh,'setCenter',@setCenter,'dispTrack',@dispTrack );
    set( pBot.hStrm, 'callback', @(h,evnt) buttonPress('setStrm'));
    set( pBot.hBh,   'callback', @(h,evnt) buttonPress('setType'));
    set( pBot.hSetL, 'callback', @(h,evnt) buttonPress('moveLeft'));
    set( pBot.hSetR, 'callback', @(h,evnt) buttonPress('moveRight'));
    set( pBot.hDel,  'callback', @(h,evnt) buttonPress('delete'));
    set( pBot.hArrL, 'callback', @(h,evnt) buttonPress('prevBeh'));
    set( pBot.hArrR, 'callback', @(h,evnt) buttonPress('nextBeh'));
    set( pBot.hSl,   'callback', @(h,evnt) setFrame(0));
    set( pBot.hZm,   'callback', @(h,evnt) buttonPress('setZoom'));
    set( pTop.hBh,   'callback', @(h,evnt) buttonPress('gotoBeh'));

    function updateAnn()
      % new annotation loaded or annotation closed
      isAnn=~isempty(A); if(isAnn), en='on'; else en='off'; end;
      hAll=[pTop.hBh pBot.hBh pBot.hSetL pBot.hArrL pBot.hSl ...
        pBot.hArrR pBot.hSetR pBot.hZm pBot.hDel];
      set(hAll,'Enable',en); set([pBot.hBar pMid.hBar],'Visible',en);
      if(~isAnn), strs={''}; else strs=A.getNames(); end
      set(pBot.hBh,'Value',1,'String',strs);
      set(pTop.hBh,'Value',1,'String',{''});
      if(~isAnn), strs={''}; else strs=int2str2(1:A.nStrm()); end
      set(pBot.hStrm,'Value',1,'String',strs);
      if(isAnn), clrs=[.3 .3 .3; uniqueColors(ceil((A.k()-1)/6),6)]; end
      if(~isempty(pMid.hTxt)), set(pMid.hTxt,'String',''); end
      % update context menu for inserting behaviors
      if(isAnn), ns=A.getNames(); ks=A.getKeys(); k=A.k(); else k=0; end
      delete(pMid.hAdds); pMid.hAdds=zeros(1,k);
      ls=cell(1,k); for t=1:k, ls{t}=[ns{t} ' (' ks(t) ')']; end
      for t=1:k, pMid.hAdds(t)=uimenu(pMid.hAdd,'Label',ls{t}); end
      for t=1:k, set(pMid.hAdds(t),'callback',@(h,e) insertBh(t)); end
      % finally update display
      updateDisp();
    end

    function updateDisp()
      if(isempty(A)), return; end
      % get current frame / annotation info
      f=dispApi.getFrame(); id=A.getId(f); type=A.getType(id); n=A.n();
      nFrame=A.nFrame(); bs=A.getBnds(); ts=A.getTypes(); ns=A.getNames();
      % set bounds for zoomed slider
      h=pBot.hZm; s=get(h,'String'); v=get(h,'Value'); w=str2double(s{v});
      fL=max(0,fM-w/2); fR=min(nFrame,fL+w); fL=max(0,fR-w);
      % update standard GUI controls
      clr={'ForegroundColor',clrs(type,:)};
      set(pBot.hBh,'Value',type,clr{:}); ss=ns(ts);
      for i=1:n, ss{i}=sprintf('%i-%i %s',bs(i)+1,bs(i+1),ss{i}); end
      set(pTop.hBh,'String',ss,'Value',id,clr{:});
      if( nFrame==1 ), set(pBot.hSl,'Enable','off'); else
        set(pBot.hSl,'Min',fL,'Max',fR-1,'Value',f,'Enable','on');
        s=1/(fR-fL-1); set(pBot.hSl,'SliderStep',[s s]);
      end
      % display text label containing info about every stream
      strm0=get(pBot.hStrm,'Value'); nSt=A.nStrm(); s=cell(1,nSt);
      for i=1:nSt, A.setStrm(i); j=A.getId(f);
        s{i}=A.getName(j); if(i==strm0 && nSt>1), s{i}=['[' s{i} ']']; end
        s{i}=['\color[rgb]{' num2str(clrs(A.getType(j),:)) '}' s{i} ' / '];
      end
      s=[s{:}]; s=s(1:end-3); s(s=='_')='-'; A.setStrm(strm0);
      set(pMid.hTxt,'String',s);
      % update hBar for zoomed slider
      idL=A.getId(fL); idR=A.getId(fR-1);
      bs1=[fL bs(idL+1:idR) fR]-fL; ts1=ts(idL:idR);
      colBar( pBot.hBar, pBot.hSl, bs1, ts1, 4 );
      % update hBar for main slider
      colBar( pMid.hBar, pMid.hSl, bs, ts, 6, fL, fR );
      % finally backup annotation
      menuApi.annBackup();

      function colBar( hBar, hSl, bs, ts, h, fL, fR )
        % set position of hBar, adjust according to width of slider (8)
        p=get(hSl,'Position'); w0=16; w1=(p(3)-2*w0)/(bs(end)-bs(1));
        p(1)=p(1)+w0; p(2)=p(2)+p(4)-h; p(3)=p(3)-2*w0; p(4)=h;
        if(w1<8), p(1)=p(1)+4; p(3)=p(3)+round(w1)-8; end
        set(hBar,'Position',p); w=ceil(p(3)); h=ceil(p(4));
        % get hbar image
        nFrame=bs(end); bs=round(bs/nFrame*w); I=zeros(w,1);
        for i1=1:length(ts), I(bs(i1)+1:bs(i1+1),:)=ts(i1); end
        I=permute(clrs(I,:),[3 1 2]); I=I(ones(1,h),:,:);
        if(nargin==7)
          fL=round(fL/nFrame*w); fR=max(1,round(fR/nFrame*w));
          I(:,[fL+1 fR],:)=1; I([1 end],fL+1:fR,:)=1;
        end
        set(hBar,'CData',I);
      end
    end

    function setFrame(flag)
      assert(~isempty(A)); rng=get(pBot.hSl,{'Min','Max'});
      f = min(max(round(get(pBot.hSl,'Value')+flag),rng{1}),rng{2});
      setCenter = (f==rng{1} && f>0) || (f==rng{2} && f<A.nFrame()-1);
      set(pBot.hSl,'Value',f); dispApi.setFrame(f,0,setCenter);
    end

    function buttonPress( str )
      assert(~isempty(A));
      f=dispApi.getFrame(); id=A.getId(f);
      switch str
        case 'setStrm'
          strm = get(pBot.hStrm,'Value');
          A.setStrm(strm); dispApi.requestUpdate();
        case 'setType'
          type = get(pBot.hBh,'Value');
          A.setType(id,type); dispApi.requestUpdate();
        case 'moveRight'
          if(id==A.n()), return; end %id==A.getId(get(pBot.hSl,'Max'))
          A.move(id+1,f+1); dispApi.requestUpdate();
        case 'moveLeft'
          if(id==1), return; end %id==A.getId(get(pBot.hSl,'Min'))
          A.move(id,f); dispApi.requestUpdate();
        case 'delete'
          A.setType(id,1); %A.delete( id );
          dispApi.requestUpdate();
        case 'prevBeh'
          f1=A.getStart(id); if(id>1 && f==f1), f1=A.getStart(id-1); end
          dispApi.setFrame(f1,0);
        case 'nextBeh'
          f1=A.getEnd(id); if(id<A.n()), f1=f1+1; end
          dispApi.setFrame(f1,0);
        case 'setZoom'
          dispApi.setFrame(f,0)
        case 'gotoBeh'
          f1=A.getStart(get(pTop.hBh,'Value'));
          dispApi.setFrame(f1,0);
        otherwise
          assert(false);
      end
    end

    function insertBh( type )
      if(isempty(A)), return; end
      A.add(type,dispApi.getFrame());
      dispApi.requestUpdate();
    end

    function setCenter( frame ), fM=round(frame); end

    function hs = dispTrack( frame )
      if(isempty(trk)), hs=[]; return; end; hs0=[]; hs1=[]; hs2=[];
      cols=[0 1 0; 1 0 0; 0 0 1; 1 1 0; 0 1 1; 1 0 1];
      if(isfield(trk,'bbs')) % display detections
        bbs=trk.bbs{frame+1}; if(isempty(bbs)), bbs=zeros(0,6); end
        bbs=bbs(bbs(:,5)>0,:); n=size(bbs,1);
        hs0=zeros(1,n); rPrp={'LineWidth',2,'LineStyle','-','EdgeColor'};
        if(size(bbs,2)==6), is=mod(bbs(:,6)-1,6)+1; else is=ones(1,n); end
        for b=1:n, hs0(b)=rectangle('Position',bbs(b,1:4),rPrp{:},...
            cols(is(b),:)); end
      end
      if(isfield(trk,'Y')) % display (x,y) tracking results
        if(iscell(trk.Y)), Y=trk.Y{frame+1}; else Y=trk.Y(:,:,frame+1); end
        if(isempty(Y)), Y=zeros(0,2); end; m=size(Y,1); hs1=zeros(1,m);
        Y=max(Y,1); inf=dispApi.getInfo(); r=min(inf.width,inf.height)/70;
        Y(:,2)=min(Y(:,2),inf.height); Y(:,1)=min(Y(:,1),inf.width);
        rPrp={'LineWidth',2,'LineStyle','-','Curvature',[1 1],...
          'EdgeColor','w','FaceColor'};
        %D=pdist2(Y,Y)+diag(nan(m,1)); close=min(D)<125^2;
        %for i=1:m, if(close(i)), cols(i,:)=max(.7,cols(i,:)); end; end
        for i=1:m, hs1(i)=rectangle(rPrp{:},cols(i,:),...
            'Position',[Y(i,:)-r 2*r 2*r]); end
      end
      if(isfield(trk,'E')) % display ellipse tracking results
        if(iscell(trk.E)), E=trk.E{frame+1}; else E=trk.E(:,:,frame+1); end
        if(isempty(E)), E=zeros(0,5); end; m=size(E,1); hs2=zeros(3,m);
        hold on; for i=1:m, [hs2(1,i),hs2(2,i),hs2(3,i)]=plotEllipse(...
            E(i,1),E(i,2),E(i,3),E(i,4),E(i,5),cols(i,:),[],2); end
        hold off; hs2=hs2(:)';
      end
      hs = [hs0 hs1 hs2];
    end
  end

  function api = dispMakeApi()
    % create api
    [sr, audio, info, nFrame, speed, curInd, hs, ...
      hImg, needUpdate, prevTime, looping ]=deal([]);
    api = struct( 'setVid',@setVid, 'setAud',@setAud, ...
      'setSpeedCb',@setSpeedCb, 'getInfo',@getInfo, ...
      'getFrame',@getFrame, 'setFrame',@setFrame, ...
      'requestUpdate',@requestUpdate, 'exportVid',@exportVid );
    set(pMid.hSl,    'Callback',@(h,evnt) setFrameCb(0));
    set(pTop.hFrmInd,'Callback',@(h,evnt) setFrameCb(1));

    function setVid( sr1 )
      % reset local variables
      if(isstruct(sr)), sr=sr.close(); end
      if(~isempty(hs)), delete(hs); hs=[]; end
      [sr, audio, info, nFrame, speed, curInd, hs, ...
        hImg, needUpdate, prevTime, looping ]=deal([]);
      sr=sr1; nFrame=0; looping=0; speed=-1; setFrame( 0, 0 );
      % update GUI
      if(~isstruct(sr)), cla(pMid.hAx); pMid.hTxt=[]; else
        info=sr.getinfo(); nFrame=info.numFrames;
        sr.seek(0); s=max(1e-6,1/(nFrame-1)); ss={'SliderStep',[s,s]};
        if(nFrame>1), set(pMid.hSl,'Max',nFrame-1,ss{:}); end
        hImg = imshow( zeros(info.height,info.width,'uint8') );
        set(hImg,'UIContextMenu',pMid.hAdd);
        pMid.hTxt=text(0,0,'','FontSize',25,'Units','pixels',...
          'HorizontalAlignment','center', 'VerticalAlignment','top');
        %fprintf('fps of video = %f\n', info.fps); % temp display
      end
      set(pMid.hSl,'Value',0); v=(nFrame>1)+1;
      en={'off','on'}; set(pMid.hSl,'Enable',en{v});
      en={'inactive','on'}; set(pTop.hFrmInd,'Enable',en{v});
      set(pTop.hFrmInd,'String','0'); set(pTop.hTmVal,'String','0:00');
      set(pTop.hFrmNum,'String',[' / ' int2str(nFrame)]);
      % update display
      feval(get(hFig,'ResizeFcn'));
      requestUpdate();
    end

    function exportVid( nm )
      % prompt for range of frames to export
      prompt={'Start frame:','End frame:','Frame Skip','Quality (0-100)'};
      wi=getInfo(); dfs={'1', num2str(wi.numFrames),'1','80'};
      rng=str2double(inputdlg(prompt,'Select Export Range',1,dfs));
      if(any(isnan(rng)) || rng(1)>rng(2)), error('invalid range'); end
      f0=max(1,rng(1)); f1=min(rng(2),wi.numFrames); skip=max(1,rng(3));
      wi.codec='jpg'; wi.quality=rng(4);
      try %#ok<ALIGN> % export video
        for f=f0:skip:f1
          setFrame(f,0); I=getframe(pMid.hAx); I=I.cdata;
          c=2; I=I(1+c:end-c,1+c:end-c,:); h=size(I,1); w=size(I,2);
          if(f==f0), wi.height=h; wi.width=w; sw=seqIo(nm,'w',wi); end
          sw.addframe(I);
        end; sw.close();
      catch err, sw.close(); throw(err); end
    end

    function setAud(y,fs,nb)
      a = abs(1 - (length(y)/fs) / (nFrame/info.fps));
      if(a>.01), error('Audio/video mismatch.'); end
      audio.fPlay=audioplayer(y,fs,nb);
      audio.bPlay=audioplayer(flipud(y),fs,nb);
      audio.fs=fs; audio.ln=length(y); setSpeed(speed);
      requestUpdate();
    end

    function dispLoop()
      if(looping), return; end; looping=1;
      while( 1 )
        % exit if appropriate, or if vid not loaded do nothing
        if(~isstruct(sr) || ~isstruct(info)), looping=0; return; end

        % stop playing video if at begin/end
        if((speed>0&&curInd==nFrame-1) || (speed<0&&curInd==0))
          setSpeed(0); needUpdate=1;
        end

        % increment/decrement curInd appropriately
        if( speed~=0 )
          t=clock(); eTime=etime(t,prevTime);
          del = speed * max(10,info.fps) * min(.1,eTime);
          if( speed>0 ), del=min(del, nFrame-curInd-1 ); end
          if( speed<0 ), del=max(del, -curInd ); end
          setFrame(curInd+del, speed); prevTime=t; needUpdate=1;
        end

        % update display if necessary
        if(~needUpdate), looping=0; return; else
          sr.seek( round(curInd) ); I=sr.getframe();
          if(~isempty(hs)), delete(hs); hs=[]; end
          hs=annApi.dispTrack(round(curInd));
          assert(~isempty(I)); set(hImg,'CData',I);
          set(pMid.hSl,'Value',curInd); c=round(curInd/info.fps);
          set(pTop.hFrmInd,'String',int2str(round(curInd+1)));
          c0=floor(c/3600); c=mod(c,3600); c1=floor(c/60); c2=mod(c,60);
          if(~c0), set(pTop.hTmVal,'String',sprintf('%i:%02i',c1,c2)); else
            set(pTop.hTmVal,'String',sprintf('%i:%02i:%02i',c0,c1,c2)); end
          annApi.updateDisp(); needUpdate=false; drawnow();
        end
      end
    end

    function setSpeedCb( flag )
      if(~isstruct(sr)),return; end
      if( flag==0 )
        setSpeed( 0 );
      elseif( speed==0 )
        setSpeed( flag ); prevTime=clock();
      elseif( sign(speed)==flag && abs(speed)<2^12 )
        setSpeed( speed*2 );
      elseif( sign(speed)~=flag && abs(speed)>1/8 )
        setSpeed( speed/2 );
      elseif( sign(speed)~=flag )
        setSpeed( 0 );
      end
      requestUpdate();
    end

    function setSpeed( speed1 )
      if((speed1>0&&curInd==nFrame-1)||(speed1<0&&curInd==0)),speed1=0;end
      speed=speed1; p=abs(speed); if(speed<0), ss='-'; else ss=''; end
      if(p<1 && p~=0), s=['1/' int2str(1/p)]; else s=int2str(p); end
      set(pTop.hPlySpd,'String',[ss s 'x']);
      if(~isempty(audio))
        stop(audio.fPlay); stop(audio.bPlay); if(speed==0), return; end;
        st=curInd/(nFrame-1); if(speed<0), st=1-st; end; st=st*audio.ln+1;
        if(speed>0), plr=audio.fPlay; else plr=audio.bPlay; end;
        set(plr,'SampleRate',audio.fs*p); play(plr,st);
      end
    end

    function setFrameCb(flag)
      if( flag==0 )
        f=round(get(pMid.hSl,'Value')); set(pMid.hSl,'Value',f);
      elseif(flag==1)
        f=str2double(get(pTop.hFrmInd,'String'));
        if(isnan(f)), requestUpdate(); return; else f=f-1; end
      end
      setFrame(f,0);
    end

    function setFrame( curInd1, speed1, setCenter )
      curInd=max(0,min(curInd1,nFrame-1));
      if( speed~=speed1 ), setSpeed(speed1); end
      if(nargin<3 || setCenter), annApi.setCenter(curInd); end
      requestUpdate();
    end

    function requestUpdate(), needUpdate=true; dispLoop(); end

    function info1 = getInfo(), info1=info; end

    function curInd1 = getFrame(), curInd1=round(curInd); end
  end

  function api = menuMakeApi()
    % create api
    [fVid, fAnn, lastSave]=deal([]);
    api = struct('vidClose',@vidClose, 'annClose',@annClose, ...
      'vidOpen',@vidOpen, 'audOpen',@audOpen, 'trkOpen',@trkOpen, ...
      'annOpen',@annOpen, 'annBackup',@annBackup );
    set(menu.hVidOpn,'Callback',@(h,envt) vidOpen(1) );
    set(menu.hVidsOpn,'Callback',@(h,envt) vidOpen(2) );
    set(menu.hVidExp,'Callback',@(h,envt) vidExport() );
    set(menu.hVidCls,'Callback',@(h,envt) vidClose() );
    set(menu.hVidInf,'Callback',@(h,envt) vidInfo() );
    set(menu.hVidAud,'Callback',@(h,envt) audOpen() );
    set(menu.hAnnNew,'Callback',@(h,envt) annNew(0) );
    set(menu.hAnnOpn,'Callback',@(h,envt) annOpen(0) );
    set(menu.hAnnCls,'Callback',@(h,envt) annClose() );
    set(menu.hAnnSav,'Callback',@(h,envt) annSave() );
    set(menu.hAnnCnf,'Callback',@(h,envt) annNew(1) );
    set(menu.hAnnMrg,'Callback',@(h,envt) annOpen(1) );
    set(menu.hTrkOpn,'Callback',@(h,envt) trkOpen(0) );
    set(menu.hTrkCls,'Callback',@(h,envt) trkOpen([]) );

    function updateMenus()
      m=menu; if(isempty(fVid)), en='off'; else en='on'; end
      set([m.hVidExp m.hVidCls m.hVidInf m.hVidAud m.hAnnNew m.hAnnOpn ...
        m.hTrkOpn m.hTrkCls],'Enable',en);
      if(isempty(A)), en='off'; else en='on'; end
      set([m.hAnnSav m.hAnnCls m.hAnnCnf m.hAnnMrg],'Enable',en);
      nm='Caltech Behavior Annotator';
      if(~isempty(fVid)), [~,nm1]=fileparts(fVid); nm=[nm ' - ' nm1]; end
      set(hFig,'Name',nm); annApi.updateAnn(); dispApi.requestUpdate();
    end

    function vidClose()
      if(~isempty(A)), annClose(); end; trkOpen([]);
      fVid=[]; dispApi.setVid([]); updateMenus();
    end

    function vidOpen( flag )
      if(isempty(fVid)), d='.'; else d=fileparts(fVid); end
      if(all(ischar(flag)))
        [d,f]=fileparts(flag); if(isempty(d)), d='.'; end;
        d=[d '/']; f=[f '.seq']; flag=1;
      elseif( flag==1 )
        [f,d]=uigetfile('*.seq','Select video',[d '/*.seq']);
      elseif( flag==2 )
        [f,d]=uigetfile('*.seq','Select first video',[d '/*.seq']);
        [f2,d2]=uigetfile('*.seq','Select second video',[d '/*.seq']);
        if( f2==0 ), return; end
      end
      if( f==0 ), return; end; vidClose(); fVid=[d f];
      try
        if( flag==1 ), sr=seqIo(fVid,'r'); else
          sr=seqIo({fVid,[d2 f2]},'rdual'); end
        dispApi.setVid(sr); updateMenus();
      catch er
        errordlg(['Failed to load: ' fVid '. ' er.message],'Error');
        fVid=[]; return;
      end
    end

    function vidInfo()
      info=dispApi.getInfo(); txt=evalc('disp(info);'); %#ok<NASGU>
      while(txt(end)==10), txt=txt(1:end-1); end; txt=[char([10 10]) txt];
      pos=get(hFig,'Position'); pos(1:2)=pos(1:2)+100; pos(3:4)=[400 400];
      h=figure('NumberTitle','off', 'Toolbar','auto', ...
        'Color','k', 'MenuBar','none', 'Visible','on', ...
        'Name','', 'Resize','off','Position',pos); pos(1:2)=0;
      uicontrol(h,'Style','text','FontSize',10,'BackgroundColor','w',...
        'HorizontalAlignment','left','Position',pos,'String',txt,...
        'FontName','FixedWidth');
    end

    function vidExport()
      assert(~isempty(fVid)); fNm=[fVid(1:end-4) '-copy.seq'];
      [f,d] = uiputfile('*.seq','Select export file',fNm);
      if(f==0), return; end; f=[d f];
      try dispApi.exportVid(f); catch er
        errordlg(['Failed to export: ' f '. ' er.message],'Error'); end
    end

    function audOpen( fAud )
      if( nargin==0 ), [f,d]=uigetfile('*.wav','Select audio',...
          [fVid(1:end-3) 'wav']); if(f==0), return; end; fAud=[d f]; end
      try
        [y,fs,nb]=wavread(fAud); dispApi.setAud(y,fs,nb); %#ok<REMFF1>
      catch er
        errordlg(['Failed to load: ' fAud '. ' er.message],'Error');
      end
    end

    function annClose()
      assert(~isempty(A)); qstr='Save Current Annotation?';
      button = questdlg(qstr,'Save','yes','no','yes');
      if(strcmp(button,'yes')); annSave(); end
      [fAnn,A,lastSave]=deal([]); updateMenus();
    end

    function annOpen( flag )
      assert(~isempty(fVid)); A1=A; if(~isempty(A)), annClose(); end; e='';
      if( all(ischar(flag)) ), f=flag; flag=0;
        [d,f,e]=fileparts(f); if(isempty(d)), d='.'; end; d=[d '/'];
        if(isempty(e) && exist([d f '.txt'],'file')), e='.txt'; end
        if(isempty(e) && exist([d f '.bAnn'],'file')), e='.bAnn'; end
      else
        if(isempty(fAnn)), fAnn=[fVid(1:end-3) 'txt']; end
        [f,d]=uigetfile('*.txt;*.bAnn','Select Annotation',fAnn);
      end
      if( f==0 ), return; end; fAnn=[d f e];
      try
        if( flag==1 )
          A=A1; assert(~isempty(A)); A.merge(fAnn);
        elseif( flag==0 )
          A=behaviorData('load',fAnn);
          info=dispApi.getInfo(); nFrame=info.numFrames;
          if(A.nFrame()~=nFrame), error('Annotation/video mismatch.'); end
        end
        updateMenus();
      catch er
        errordlg(['Failed to load: ' fAnn '. ' er.message],'Error');
        [fAnn,A,lastSave]=deal([]); return;
      end
    end

    function annSave()
      assert(~isempty(fVid) && ~isempty(A) && ~isempty(fAnn));
      [f,d] = uiputfile('*.txt;*.bAnn','Select annotation',fAnn);
      if( f==0 ), return; end; fAnn=[d f]; A.save(fAnn);
    end

    function annNew( update )
      assert(~isempty(fVid)); A1=A; if(~isempty(A)), annClose(); end
      fNm=[fileparts(fVid) '/config.txt'];
      [f,d] = uigetfile('*.txt','Select config file',fNm);
      if( f==0 ), return; end; f=[d f]; fAnn=[fVid(1:end-3) 'txt'];
      try
        if( update )
          A=A1; assert(~isempty(A)); A.recreate(f); updateMenus();
        else
          info=dispApi.getInfo(); nFrame=info.numFrames;
          A=behaviorData('create',f,nFrame); updateMenus();
        end
      catch er
        errordlg(er.message, 'File Error'); A=[];
      end
    end

    function annBackup()
      if( isempty(lastSave) || etime(clock,lastSave)>60 )
        [d,f]=fileparts(fAnn); f=[d '/' f '-backup.txt'];
        assert(~isempty(A)); A.save(f); lastSave=clock();
      end
    end

    function trkOpen( flag )
      if(isempty(flag)), trk=[]; dispApi.requestUpdate(); return; end
      if( all(ischar(flag)) ), f=flag; else fNm=[fVid(1:end-3) 'mat'];
        [f,d]=uigetfile('*.mat','Load Tracking',fNm); f=[d f];
      end
      assert(~isempty(fVid)); if(f==0), return; end
      try
        tmp=load(f); assert(any(isfield(tmp,{'Y','bbs','E'})));
        if(isfield(tmp,'Y')), trk.Y=tmp.Y; T=size(trk.Y,3);
        elseif(isfield(tmp,'bbs')), trk.bbs=tmp.bbs; T=length(trk.bbs);
        elseif(isfield(tmp,'E')), trk.E=tmp.E; T=size(trk.E,3);
        end
        info=dispApi.getInfo(); nFrame=info.numFrames;
        if(T~=nFrame), error('Tracking/video mismatch.'); end
      catch er
        trk=[]; errordlg(['Failed to load: ' f '. ' er.message],'Error');
      end
      dispApi.requestUpdate();
    end

  end
end


function params = parseVarArgs(params,varargin)
% Parse variable input arguments supplied in name/value format.
%
%    params = parseVarArgs(params,'property1',value1,'property2',value2) sets
%    the fields propertyX in p to valueX.
%
%    params = parseVarArgs(params,varargin{:},'strict') only sets the field
%    names already present in params. All others are ignored.
%
% AE 2007-06-01

if isempty(varargin)
    return
end

% check if correct number of inputs
if mod(length(varargin),2)
    if ~strcmp(varargin{end},'strict')
        err.message = 'Name and value input arguments must come in pairs.';
        err.identifier = 'parseVarArgs:wrongInputFormat';
        error(err)
    else
        % in 'strict' case, remove all fields that are not already in params
        fields = fieldnames(params);
        ndx = find(~ismember(varargin(1:2:end-1),fields));
        varargin([2*ndx-1 2*ndx end]) = [];
    end
end

% parse arguments
for i = 1:2:length(varargin)
    if ischar(varargin{i})
        params.(varargin{i}) = varargin{i+1};
    else
        err.message = 'Name and value input arguments must come in pairs.';
        err.identifier = 'parseVarArgs:wrongInputFormat';
        error(err)
    end
end


% set default value for a field if not set yet.
function para = SetDefaultValue(para, fieldName, defaultValue)
    if ~isfield(para, fieldName)
        para.(fieldName) = defaultValue;
    end
end


function [parm_success] = get_parm_xml(xmlfile,parm,parm_key)
% function which will read xml file and updates the parameter_field matching
% the requested parameter. Will return "FAIL" or "SUCCESS" based on
% whether it found the variable
%
% Bekaert David - Jet Propulsion Laboratory

if nargin<3
    error('Require three inputs')
end

% initialize the field
temp = 0;
count_changes = 0;

% loop over the xml file
fid = fopen(xmlfile,'r');
fid_new = fopen([xmlfile '_temp'],'w');

while temp~=-1
    temp = fgetl(fid);
    if temp==-1
        break
    end
    ix =  strfind(temp,['"' parm '"']);
    if isempty(ix)
        ix = strfind(temp,['"' lower(parm) '"']);
    end
    if ~isempty(ix)
        % see if the value is at this string or not
        ix_value = strfind(temp,'<value>');
        while isempty(ix_value);
            % write the output in case its not a value line
            if isempty(ix_value);
                fprintf(fid_new,[temp '\n']);
            end

            temp = fgetl(fid);
            ix_value = strfind(temp,'<value>');

            % avoid inf loop
            if temp==-1
                break
            end
        end

        if temp~=-1
            % update with new value or string
            if isnumeric(parm_key)
                parm_key = strtrim(num2str(parm_key));
            end
            parm_key_str = [repmat(' ',1,ix_value-1) '<value>' parm_key '</value>'];
            fprintf(fid_new,[parm_key_str '\n']);
            count_changes = count_changes+1;
        end
    else
        fprintf(fid_new,[temp '\n']);
    end
end
fclose(fid);
fclose(fid_new);

% rename the file
delete(xmlfile)
movefile([xmlfile '_temp'],xmlfile)

% see if the search was succesful
if count_changes==0
    fprintf(['Could not find ' parm '... \n'])
else
    parm_success = 'SUCCESS';
end


%script to run the ring render gui:

clear
close all

ring_gui;


classdef MimMarkerGreen < MimGuiPlugin
    % MimMarkerGreen. Gui Plugin for switching transparency of zero
    %
    %     You should not use this class within your own code. It is intended to
    %     be used by the gui of the TD MIM Toolkit.
    %
    %     MimMarkerGreen is a Gui Plugin for the MIM Toolkit.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties
        ButtonText = 'Green'
        SelectedText = 'Green'
        ToolTip = 'Place green markers'
        Category = 'Marker colour'
        Visibility = 'Dataset'
        Mode = 'Markers'

        HidePluginInDisplay = false
        PTKVersion = '1'
        ButtonWidth = 5
        ButtonHeight = 1
        Icon = 'single_marker.png'
        IconColour = GemMarkerPoint.DefaultColours{2}
        Location = 2
    end

    methods (Static)
        function RunGuiPlugin(gui_app)
            gui_app.ImagePanel.NewMarkerColour = 2;
        end

        function enabled = IsEnabled(gui_app)
            enabled = gui_app.IsDatasetLoaded && isequal(gui_app.GetCurrentModeName, MimModes.MarkerMode);
        end

        function is_selected = IsSelected(gui_app)
            is_selected = gui_app.ImagePanel.NewMarkerColour == 2;
        end
    end
end


function anonAnonBeamGeometryS = defineAnonBeamgeometry()
%
% function anonAnonBeamGeometryS = defineAnonBeamgeometry()
%
% Defines the anonymous "header" data structure. The fields containing PHI have
% been left out by default. In order to leave out additional fields, remove
% them from the list below.
%
% The following three operations are allowed per field of the input data structure:
% 1. Keep the value as is: listed as 'keep' in the list below.
% 2. Allow only the specific values: permitted values listed within a cell array.
%    If no match is found, a default anonymous string will be inserted.
% 3. Insert dummy date: listed as 'date' in the list below. Date will be
%    replaced by a dummy value of 11/11/1111.
%
% APA, 1/11/2018

anonAnonBeamGeometryS = struct( ...
                   'imageType',  {{'BEAM GEOMETRY'}}, ...
                  'beamNumber',  'keep', ...
                'beamModality',  {{'PHOTON','PROTON'}}, ...
               'beamEnergyMeV',  'keep', ...
             'beamDescription',  'keep', ...
               'RxDosePerTxGy',  'keep', ...
           'MonitorUnitsPerTx',  'keep', ...
                  'numberOfTx',  'keep', ...
             'fractionGroupID',  {{'LTLUNG_D','RTLUNG_D'}}, ...
                    'beamType',  {{'DYNAMIC'}}, ...
              'planIDOfOrigin',  'keep', ...
              'collimatorType',  'keep', ...
                'apertureType',  {{'MLCX'}}, ...
         'apertureDescription',  'keep', ...
             'collimatorAngle',  'keep', ...
                 'gantryAngle',  'keep', ...
                  'couchAngle',  'keep', ...
                   'headInOut',  {{'HFS','HFP','FFS','FFP'}}, ...
    'nominalIsocenterDistance',  'keep', ...
        'numberRepresentation',  'keep', ...
                  'apertureID',  'keep', ...
                  'wedgeAngle',  'keep', ...
          'wedgeRotationAngle',  'keep', ...
                    'arcAngle',  'keep', ...
                   'machineID',  'keep', ...
                  'beamWeight',  'keep', ...
                 'weightUnits',  'keep', ...
                 'compensator',  'keep', ...
           'compensatorFormat',  'keep', ...
                     'beamUID',  'keep', ...
               'assocBeamsUID',  'keep'  ...
           );


root_10 = 'ACS_10_5YR_DP02';
ACS_path = [root_10, '/', root_10, '_metadata.csv'];
[~, text_10] = xlsread(ACS_path);

root_11 = 'ACS_11_5YR_DP02';
ACS_path = [root_11, '/', root_11, '_metadata.csv'];
[~, text_11] = xlsread(ACS_path);

root_12 = 'ACS_12_5YR_DP02';
ACS_path = [root_12, '/', root_12, '_metadata.csv'];
[~, text_12] = xlsread(ACS_path);

root_13 = 'ACS_13_5YR_DP02';
ACS_path = [root_13, '/', root_13, '_metadata.csv'];
[~, text_13] = xlsread(ACS_path);

root_14 = 'ACS_14_5YR_DP02';
ACS_path = [root_14, '/', root_14, '_metadata.csv'];
[~, text_14] = xlsread(ACS_path);

root_15 = 'ACS_15_5YR_DP02';
ACS_path = [root_15, '/', root_15, '_metadata.csv'];
[~, text_15] = xlsread(ACS_path);

root_16 = 'ACS_11_5YR_DP02';
ACS_path = [root_16, '/', root_16, '_metadata.csv'];
[~, text_16] = xlsread(ACS_path);


function b = subsref(a,S)
% B = A.FIELDNAME; % SUBSREF - subscripted indexing
%
% See also GET

switch length(S)
case 1
    switch S.type
    case '()'
        b = get(a,S.subs{:});
    case '.'
        b = get(a,S.subs);
    end;
case 2
    if strcmp(S(1).type,'()')&strcmpi(S(2).type,'.')    % a(i).field
        b = get(a,S(1).subs{:},S(2).subs);
    elseif strcmp(S(1).type,'.')&strcmpi(S(2).type,'()')% a.field(i)
        b = get(a,S(1).subs,S(2).subs{:});
    elseif strcmp(S(1).type,'.')&strcmpi(S(2).type,'.') % a.field1.field2
        b = get(a,[S(1).subs,'.',S(2).subs]);
    else
        error('Sorry - output structure not supported');
    end;
otherwise
    error('Sorry - output structure not supported');
end;


load '..\uavW.mat'
i1 = uavW;
clear uavW;


classdef ViewEventsTypeEnum < matlab.mixin.SetGet
    %ViewGridTypeEnum Summary of this class goes here
    %   Detailed explanation goes here

    enumeration
        SoIChunk('View By SoI Grouping')
        All('View All')
    end

    properties
        name(1,:) char = '';
    end

    methods
        function obj = ViewEventsTypeEnum(name)
            obj.name = name;
        end
    end

    methods(Static)
        function listBoxStr = getListBoxStr()
            m = enumeration('ViewEventsTypeEnum');
            [~,I] = sort({m.name});
            listBoxStr = {m(I).name};
        end

        function [ind, enum] = getIndForName(name)
            m = enumeration('ViewEventsTypeEnum');
            [~,I] = sort({m.name});
            m = m(I);
            ind = find(ismember({m.name},name),1,'first');
            enum = m(ind);
        end

        function [enum, ind] = getEnumForListboxStr(nameStr)
            m = enumeration('ViewEventsTypeEnum');
            ind = find(ismember({m.name},nameStr),1,'first');
            enum = m(ind);
        end
    end
end


classdef MivFrameworkAppDef < handle
    % MivFrameworkAppDef. Defines application-dependent behaviour for the
    % Framework
    %
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties (Access = private)
        Config
        ContextDef
        Directories
        ClassFactory
    end

    methods
        function obj = MivFrameworkAppDef
            obj.Config = MimConfig();
            obj.ContextDef = MivContextDef();
            obj.Directories = MimDirectories(obj.GetApplicationParentDirectory, obj.Config);
            obj.ClassFactory = MimClassFactory();
        end

        function context_def = GetContextDef(obj)
            context_def = obj.ContextDef;
        end

        function class_factory = GetClassFactory(obj)
            class_factory = obj.ClassFactory;
        end

        function parent_directory = GetApplicationParentDirectory(obj)
            parent_directory = CoreDiskUtilities.GetUserDirectory;
        end

        function bin_directory = GetBinDirectory(obj)
            bin_directory = fullfile(CoreDiskUtilities.GetUserDirectory, 'mim', 'bin');
        end

        function files_to_compile = GetFilesToCompile(obj, reporting)
            files_to_compile = MivGetMexFilesToCompile(reporting);
        end

        function config = GetFrameworkConfig(obj)
            config = obj.Config;
        end

        function directories = GetFrameworkDirectories(obj)
            directories = obj.Directories;
        end

        function debug_mode = IsDebugMode(obj)
            debug_mode = false;
        end

        function time_functions = TimeFunctions(obj)
            time_functions = obj.Config.TimeFunctions;
        end

        function NewDatasetLoaded(obj, dataset_uid, dataset, reporting)
        end

        function plugins_path = GetUserPath(~)
            full_path = mfilename('fullpath');
            [path_root, ~, ~] = fileparts(full_path);
            plugins_path = fullfile(path_root, '..', 'User');
        end
    end
end


  function num = qsublisten(callback, varargin)

% QSUBLISTEN checks whether jobs, submitted by qsubfeval, have been
% completed. Whenever a job returns, it executes the provided callback function
% (should be a function handle), with the job ID as an input argument. Results
% can then be retrieved by calling QSUBGET. If a cell-array is provided as
% a the 'filter' option (see below), the second input argument passed to the
% callback function will be an index into this cell-array (to facilitate
% checking which job returned in the callback function).
%
% Note that this function is blocking; i.e., it only returns after a
% certain criterion has been met.
%
% Arguments can be supplied with key-value pairs:
%     maxnum      = maximum number of jobs to collect, function will return
%                   after this is reached. Default = Inf; so it is highly
%                   recommended you provide something here, since with
%                   maxnum=Inf the function will never return.
%     filter      = regular expression filter for job IDs to respond to.
%                   The default tests for jobs generated from the current
%                   MATLAB process. A cell-array of strings can be
%                   provided; in that case, exact match is required.
%     sleep       = number of seconds to sleep between checks (default=0)
%
% This function returns the number of jobs that were collected and for
% which the callback function was called.

% Copyright (C) 2012, Eelke Spaak
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

maxnum = ft_getopt(varargin, 'maxnum', Inf);
filter = ft_getopt(varargin, 'filter', [generatesessionid '.*']);
sleep  = ft_getopt(varargin, 'sleep', 0);

if ischar(filter)
  regexpFilt = 1;
elseif iscellstr(filter)
  regexpFilt = 0;
else
  error('filter should either be a regexp string or cell-array of exact-match strings');
end

% keep track of which job IDs we have already recognized and fired the callback for
foundJobs = [];

curPwd = getcustompwd();

num = 0;
while (num < maxnum)

  files = dir();
  for k = 1:numel(files)

    % preliminary filter to get just the qsub-specific output files
    jobid = regexp(files(k).name, '^(.*)\.o.*$', 'tokens');

    if ~isempty(jobid) && ~contains(foundJobs, jobid{1}{1})
      jobid = jobid{1}{1};

      % wait until not only the stdout file exists, but also the stderr and
      % _output.mat. If we fire the callback before all three files are
      % present, a subsequent call to qsubget will fail
      outputfile   = fullfile(curPwd, sprintf('%s_output.mat', jobid));
      logerr       = fullfile(curPwd, sprintf('%s.e*', jobid));
      while ~exist(outputfile,'file') || ~isfile(logerr)
        pausejava(0.01);
      end

      if (regexpFilt && ~isempty(regexp(jobid, filter, 'once'))) || (~regexpFilt && ~isempty(find(strcmp(jobid, filter))))

        if (~regexpFilt && nargin(callback)>1)
          % also provide an index into the filter array
          callback(jobid, find(strcmp(jobid, filter)));
        else
          callback(jobid);
        end

        num = num+1;
        foundJobs = [foundJobs '|' jobid];
      end

    end
  end

  if (sleep > 0)
    pausejava(sleep);
  end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% helper function that detects a file, even with a wildcard in the filename
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function status = isfile(name)
tmp = dir(name);
status = length(tmp)==1 && ~tmp.isdir;
end

end


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

% TODO : Add parameter display for hyperparapmeter for distributions
% FIXME: Fix the behavior for very large models

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

% Last Modified by GUIDE v2.5 09-Sep-2006 14:45:09

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

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

% --- Executes just before ARMAX_viewer is made visible.
function ARMAX_viewer_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to ARMAX_viewer (see VARARGIN)

% Choose default command line output for ARMAX_viewer
handles.output  = hObject;
handles.Results = varargin{1};
if length(varargin)==2
    handles.DisplayNumber = varargin{2};
else
    handles.DisplayNumber = length(handles.Results);
end

% Update handles structure
guidata(hObject, handles);
popup_cell=[];
for i=1:length(handles.Results);
    popup_cell{i}=handles.Results{i}.StringID;
end
set(handles.model_popupmenu,'String',popup_cell)
set(handles.model_popupmenu,'Value',length(handles.Results))
set(gcf,'Position',[10 10 1000 706])
%
display_model(handles,hObject);

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

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

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

% --- Executes on mouse press over axes background.
function model_axes_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to model_axes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

function generate_text(handles,hObject);
% Get rid of results not needed
number = handles.DisplayNumber;
results=handles.Results{number};
% Compute the needed strings
[ParameterString,VariableString]= generate_strings(results);
% Layout the model
[model_layout_info]=model_layout(ParameterString,VariableString,handles);
% Layout the model statistics
[model_stats_layout_info]=model_stats_layout(results,handles);
% Resize the Parameter Estimates Window
[estimate_window]=resize_parameter_estimate_uipanel(handles);
% Layout the column titles and return their position
[title_positions]=title_layout(handles);
% Compute the text elements for the main display and their positions
[text_positions]=compute_text_positions(handles,ParameterString,title_positions);
% Update the results with a flag that the text layout is done and with all
% of the relevant information, also set the handles
handles.Results{number}.ParameterString=ParameterString;
handles.Results{number}.VariableString=VariableString;
handles.Results{number}.model_layout_info=model_layout_info;
handles.Results{number}.model_stats_layout_info=model_stats_layout_info;
handles.Results{number}.estimate_window=estimate_window;
handles.Results{number}.title_position=title_positions;
handles.Results{number}.text_position=text_positions;
handles.Results{number}.TextFinished=true;
guidata(hObject, handles);

function [ParameterString,VariableString]= generate_strings(results);
% Not finished
% TO DO: ARARCH, EGARCH and AGARCH
index=1;

ParameterString{index}='y_t =';
c{index}='';
index=index+1;

if results.constant
    ParameterString{index}='\phi_0';
    VariableString{index}='';
    index=index+1;
end

if ~isempty(results.ARlags)
    if max(results.ARlags)>0
        for i=1:length(results.ARlags)
            ParameterString{index}=['\phi_{' num2str(results.ARlags(i)) '}'];
            VariableString{index}=['y_{t-' num2str(results.ARlags(i)) '}'];
            index=index+1;
        end
    end
end

if ~isempty(results.MAlags)
    if max(results.MAlags)>0
        for i=1:length(results.MAlags)
            ParameterString{index}=['\theta_{' num2str(results.MAlags(i)) '}'];
            VariableString{index}=['\epsilon_{t-' num2str(results.MAlags(i)) '}'];
            index=index+1;
        end
    end
end

ParameterString{index}='';
VariableString{index}='\epsilon_t';

function [layout_info]=model_layout(ParameterString,VariableString,handles)

axes(handles.model_axes);
%set(gcf,'Position',[ -3          35        1024         664],'Units','pixels')
%set(ax,'Units','pixels','Position',[134.1200   500.0400  793.6000  120])
line=0.75;
count=1;

result = handles.Results{handles.DisplayNumber};
ModelString = result.StringID;
% Add on the model specific string
final_str{count}=ModelString;
count=count+1;

%FIX ME, need to worry about no constant!
if result.constant
    final_str{count}=[ParameterString{1} ParameterString{2}];
else
    final_str{count}=[ParameterString{1} ParameterString{2} VariableString{2}];
end

xpos=.05;
for i=3:length(ParameterString)
    t=text(xpos,line,['$' final_str{count} '+' ParameterString{i} VariableString{i} '$' ],'Interpreter','latex','FontSize',12);
    ex=get(t,'Extent');
    delete(t);
    if ex(1)+ex(3)<1
        % No problems, add and try next
        final_str{count}=[final_str{count} '+' ParameterString{i} VariableString{i}];
    else
        %Problem, no not add and start a new line
        xpos=.1;
        count=count+1;
        final_str{count}=['+' ParameterString{i} VariableString{i}];
    end
end

t=[];
line=0.75;
for i=1:count;
    if i<3
        xpos=.02;
    else
        xpos=.07;
    end
    if i>1
        t{i}=text(xpos,line,['$' final_str{i} '$'] ,'Interpreter','latex','FontSize',12);
    else
        t{i}=text(xpos,line,['$' final_str{i} '$'] ,'FontSize',12);
    end
    line=line-0.25;
end

% Now that the text is layed out, the axes and uipanel need to be resized
% accordingly and the text strings need to be vertically repositioned
% axes height is 33* no lines, y position is always 9
% uipanel height is 33*no lines+24, y position is 650-uiheight
no_lines=length(final_str);
uiHeight = no_lines*33+24;
uiY = 670 - uiHeight;
uiP=get(handles.model_uipanel,'Position');
uiP(2)=uiY;
uiP(4)=uiHeight;
set(handles.model_uipanel,'Position',uiP);

axHeight = 33*no_lines;
axY = 9;
axP=get(handles.model_axes,'Position');
axP(2)=axY;
axP(4)=axHeight;
set(handles.model_axes,'Position',axP);

% Finally uniformly distribute the t{i} between at
% [1:no_lines]/(no_lines+1)
positions = (no_lines:-1:1)/(no_lines+1);
for i=1:no_lines;
    tP=get(t{i},'Position');
    tP(2)=positions(i);
    set(t{i},'Position',tP);
end

% Need to return everything needed to make relayout easy:
%   uipanle position
%   axes position
%   text strings
%   text string position
layout_info.model_uipanel_position = get(handles.model_uipanel,'Position');
layout_info.model_axes_position = get(handles.model_axes,'Position');
layout_info.model_text_strings = final_str;
for i=1:length(final_str);
    layout_info.model_text_strings_position{i}=get(t{i},'Position');
end
% Clear the axis
cla

function [layout_info]=model_stats_layout(results,handles)
% 1. Fix the position of and size of the UI and axes
% 2. Layout the important stats and their labels

axes(handles.stats_axes);
set(handles.stats_axes,'Position',[15     9   920   60])
model_uipanel_position=get(handles.model_uipanel ,'Position');
set(handles.model_stats_uipanel,'Position',[25  model_uipanel_position(2)-84    950   84])

titles={'Log-likelihood','No. of Params',' Akaike IC','Schwartz/Bayesian IC','$\hat{\sigma}^2$'};
positions=linspace(.02,.9,length(titles));
total_space=0;
for i=1:length(titles)
    if i==5
        t(i)=text(positions(i),.66,titles{i},'FontName','Tahoma','FontSize',10,'Interpreter','latex');
    else
        t(i)=text(positions(i),.66,titles{i},'FontName','Tahoma','FontSize',10);
    end
    ex(i,:)=get(t(i),'Extent');
    if i>1
        total_space=total_space+ex(i,1)-(ex(i-1,3)+ex(i-1,1));
    end
end
total_space=total_space+.98-(ex(5,3)+ex(5,1));
% Compute the total amount of space between the objects and uniformly
% respace them
spaces=[4 4 4 4]*total_space/22;
for i=2:length(titles)
    ex=get(t(i-1),'Extent');
    set(t(i),'Position',[ex(1)+ex(3)+spaces(i-1) .66 0]);
end

% Set up the text strings.  I need to limit the width
data{1}=sprintf('%0.7g',results.ll);
data{2}=num2str(results.K);
data{3}=sprintf('%0.7g',results.AIC);
data{4}=sprintf('%0.7g',results.BIC);
data{5}=sprintf('%0.7g',results.seregression);

for i=1:5
    d(i)=text(positions(i),.33,data{i},'FontName','Tahoma','FontSize',10);
    % Now move it so that it is right aligned
    p1 = get(d(i),'Extent');
    p2 = get(t(i),'Extent');
    set(d(i),'Position',[p2(1)+p2(3)-p1(3) .33 0]);
end
AX=axis;
hold on;
for i=1:5
    % Now move it so that it is right aligned
    p2 = get(t(i),'Extent');
    axis(AX);
    x_line_data{i}=[p2(1) p2(1)+p2(3)]';
    y_line_data{i}=[p2(2) p2(2)];
    plot([p2(1) p2(1)+p2(3)],[p2(2) p2(2)],'k');
end
hold off;

% Need to return all of teh strings along with all of their positions
layout_info.Titles=titles;
layout_info.Data=data;
layout_info.TitlePosition=[];
layout_info.DataPosition=[];

for i=1:5;
    layout_info.TitleHandles{i}=t(i);
    layout_info.TitlePosition{i}=get(t(i),'Position');
    layout_info.DataHandle{i}=d(i);
    layout_info.DataPosition{i}=get(d(i),'Position');
end
layout_info.model_stats_uipanel_position = get(handles.model_stats_uipanel,'Position');
layout_info.stats_axes_position = get(handles.stats_axes,'Position');
layout_info.x_line_data=x_line_data;
layout_info.y_line_data=y_line_data;
% Clear the axis
cla

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

% Hints: contents = get(hObject,'String') returns model_popupmenu contents as cell array
%        contents{get(hObject,'Value')} returns selected item from model_popupmenu
handles.DisplayNumber = get(hObject,'Value');
guidata(hObject, handles);
display_model(handles,hObject);

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

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

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

% Hints: get(hObject,'String') returns contents of page_number_edit as text
%        str2double(get(hObject,'String')) returns contents of page_number_edit as a double
val = get(hObject,'String');
result = handles.Results{handles.DisplayNumber};
LastPage = handles.LastPage;
NumberOfPages = size(result.text_position.x_pos,3);
[CurrentPage, OK] = bounded_integer_validate(val,LastPage,1,NumberOfPages);
if ~OK
    set(hObject,'String',num2str(CurrentPage));
end
draw_parameter_table(result,CurrentPage,handles)
handles.LastPage=CurrentPage;
% Update handles structure
guidata(hObject, handles);

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

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

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

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

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

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

% --- Executes on button press in increase_page_pushbutton.
function increase_page_pushbutton_Callback(hObject, eventdata, handles)
% hObject    handle to increase_page_pushbutton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
result = handles.Results{handles.DisplayNumber};
NumberOfPages = size(result.text_position.x_pos,3);
CurrentPage = str2double(get(handles.page_number_edit,'String'));
if CurrentPage < NumberOfPages
    CurrentPage = CurrentPage +1;
    set(handles.page_number_edit,'String',num2str(CurrentPage));

    draw_parameter_table(result,CurrentPage,handles)
    handles.LastPage=CurrentPage;
    % Update handles structure
    guidata(hObject, handles);

end

% --- Executes on button press in decrease_page_pushbutton.
function decrease_page_pushbutton_Callback(hObject, eventdata, handles)
% hObject    handle to decrease_page_pushbutton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
result = handles.Results{handles.DisplayNumber};
CurrentPage = str2double(get(handles.page_number_edit,'String'));
if CurrentPage >1
    CurrentPage = CurrentPage - 1;
    set(handles.page_number_edit,'String',num2str(CurrentPage));

    draw_parameter_table(result,CurrentPage,handles)
    handles.LastPage=CurrentPage;
    % Update handles structure
    guidata(hObject, handles);
end

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

function [window_stats]=resize_parameter_estimate_uipanel(handles)
model_uipanel_pos=get(handles.model_stats_uipanel,'Position');
estimates_uipanel_pos=get(handles.estimates_uipanel ,'Position');
estimates_uipanel_pos(4)=estimates_uipanel_pos(4)+model_uipanel_pos(2)-sum(estimates_uipanel_pos([2 4]));
set(handles.estimates_uipanel ,'Position',estimates_uipanel_pos);

estimates_axes_pos=get(handles.estimates_axes,'Position');
estimates_axes_pos(2)=12;
set(handles.estimates_axes,'Position',estimates_axes_pos)

estimates_uipanel_pos=get(handles.estimates_uipanel ,'Position');
estimates_axes_pos=get(handles.estimates_axes,'Position');
estimates_axes_pos(4)=estimates_uipanel_pos(4)-24;
set(handles.estimates_axes,'Position',estimates_axes_pos);
estimates_axes_pos=get(handles.estimates_axes,'Position');
% Note, I want ot make sure that the estimates window is at least 330
% pixels high

% FIXME: Fix the behavior for very large models
% ScreenSize = get(0,'ScreenSize')
% set(handles.figure1,'Position',[1 ScreenSize-HeightofWindow-33 WidthofWindow HeightofWindow])
% Return the position of the uipanel, the axes and the figure!
window_stats.axes_pos=estimates_axes_pos;
window_stats.uipanel_pos=estimates_uipanel_pos;
window_stats.figure_pos=get(handles.figure1,'Position');

function [title_position]=title_layout(handles)
titles={'Parameter','Estimate','Std. Error','T-stat','P-val'};
axes(handles.estimates_axes);
t(1)=text(.02,.5,titles{1},'FontName','Tahoma','FontSize',10);
axes_pos=get(handles.estimates_axes,'Position');
set(t(1),'Units','pixels');
t_pos=get(t(1),'Position');
t_ex = get(t(1),'Extent');
y=floor(axes_pos(4)-t_ex(4)/2-10);
t_pos(2)=y;
set(t(1),'Position',t_pos)
position{1}=t_pos;
for i=2:length(titles);
    t(i)=text((i-1)/6+.02,.5,titles{i},'FontName','Tahoma','FontSize',10);
    set(t(i),'Units','pixels') ;
    t_pos=get(t(i),'Position');
    t_pos(2)=y;
    set(t(i),'Position',t_pos);
    position{i}=t_pos;
end
AX=axis;
hold on;
for i=1:length(titles)
    % Now move it so that it is right aligned
    set(t(i),'Units','normalized');
    p2 = get(t(i),'Extent');
    % Need to get the right points of each label to do right alignment
    right_points{i}=p2(1)+p2(3);
    axis(AX);
    x_plot{i}=[p2(1) p2(1)+p2(3)];
    y_plot{i}=[p2(2) p2(2)]';
    plot(x_plot{i},y_plot{i},'k');
end

hold off;
tt=text(.5,.5,'$\alpha \beta \gamma \lambda \omega \nu \mu$','Interpreter','latex','FontSize',14);
row_height=get(tt,'Extent');
delete(tt);
row_height=row_height(4);
title_position.Titles=titles;
title_position.TitlePosition=position;
title_position.x_line_data=x_plot;
title_position.y_line_data=y_plot;
title_position.RowHeight=row_height;
title_position.NumRows=floor((1-0.5*row_height)/row_height);
title_position.RightPoints=right_points;
% Clear the axis
cla

function [text_positions]=compute_text_positions(handles,str,title_position)
% First set up the texts

results = handles.Results{handles.DisplayNumber};
right_points = title_position.RightPoints;
actual_rows=title_position.NumRows-1;
for i=2:length(str)-1
    % first compute the page
    page = ceil((i-1)/actual_rows);

    if mod(i-1,actual_rows)==0
        table_position = actual_rows;
    else
        table_position = mod(i-1,actual_rows);
    end
    for j=1:5
        switch j
            case 1
                % Latex string
                window_text{table_position,j,page} = ['$' str{i} '$'];
            case 2
                % Estimate
                temp = results.parameters(i-1);
            case 3
                % Estimate
                temp = results.StdErr(i-1);
            case 4
                % Estimate
                temp = results.Tstat(i-1);
            case 5
                % Estimate
                temp = results.Pval(i-1);
        end
        if j~=1
            if temp<.000001
                window_text{table_position,j,page} = sprintf('%0.3g',temp);
            else
                window_text{table_position,j,page} = sprintf('%0.5f',temp);
            end
        end
    end
    if mod(i-1,actual_rows)==0
        actual_position = actual_rows+1;
    else
        actual_position = mod(i-1,actual_rows)+1;
    end
    row_height=title_position.RowHeight;
    for j=1:5
        y_pos{table_position,j,page} = 1-(actual_position)*row_height;
        if j==1
            temp_text =text(.5,y_pos{table_position,j,page},window_text{table_position,j,page},'Interpreter','latex','FontSize',12);
        else
            temp_text =text(.5,y_pos{table_position,j,page},window_text{table_position,j,page},'FontName','Tahoma','FontSize',10);
        end
        ex=get(temp_text,'Extent');
        delete(temp_text);
        x_pos{table_position,j,page}=right_points{j}-ex(3);
    end
end
% Set up the return data
text_positions.x_pos = x_pos;
text_positions.y_pos = y_pos;
text_positions.window_text = window_text;

function draw_data(result,handles,hObject)
% Resize the figure
set(handles.figure1,'Position',result.estimate_window.figure_pos);
% Resize the uipanels
set(handles.model_uipanel,'Position',result.model_layout_info.model_uipanel_position);
set(handles.model_stats_uipanel,'Position',result.model_stats_layout_info.model_stats_uipanel_position);
set(handles.estimates_uipanel,'Position',result.estimate_window.uipanel_pos)
% Resize the axes
set(handles.model_axes,'Position',result.model_layout_info.model_axes_position);
set(handles.stats_axes ,'Position',result.model_stats_layout_info.stats_axes_position);
set(handles.estimates_axes,'Position',result.estimate_window.axes_pos);
% Draw the model
axes(handles.model_axes)
% Make sure itis clear
cla
str = result.model_layout_info.model_text_strings;
pos = result.model_layout_info.model_text_strings_position;
N = length(str);
for i=1:N
    x=pos{i}(1);
    y=pos{i}(2);
    if i>1
        text(x,y,['$' str{i} '$'],'Unit','normalized','interpreter','latex','FontSize',12)
    else
        text(x,y,[str{i}],'Unit','normalized','FontSize',12)
    end
end
% Draw the model statistic titles, lines, and values
axes(handles.stats_axes)
% Make sure itis clear
cla
str = result.model_stats_layout_info.Titles;
pos = result.model_stats_layout_info.TitlePosition;
N = length(str);
for i=1:N
    x=pos{i}(1);
    y=pos{i}(2);
    if i~=5
        text(x,y,str{i},'Unit','normalized','FontSize',10,'FontName','Tahoma')
    else
        text(x,y,str{i},'Unit','normalized','FontSize',10,'FontName','Tahoma','Interpreter','latex')
    end
end
hold on;
for i=1:N
    % Now move it so that it is right aligned
    x=result.model_stats_layout_info.x_line_data{i};
    y=result.model_stats_layout_info.y_line_data{i};
    plot(x,y,'k');
end
hold off;
str = result.model_stats_layout_info.Data;
pos = result.model_stats_layout_info.DataPosition;
N = length(str);
for i=1:N
    x=pos{i}(1);
    y=pos{i}(2);
    text(x,y,str{i},'Unit','normalized','FontSize',10,'FontName','Tahoma')
end
% Call the function to draw the correct page, presumably one
draw_parameter_table(result,1,handles) % In the initial draw we will always draw page 1

handles.LastPage=1;
% Update handles structure
guidata(hObject, handles);

% Finally update the UI elements that have to do with the pages
NumberOfPages = size(result.text_position.x_pos,3);
if NumberOfPages==1
    % Easy case
    set(handles.page_number_edit,'BackgroundColor',[0.5 0.5 0.5],'String','1')
    set(handles.increase_page_pushbutton,'Enable','off')
    set(handles.decrease_page_pushbutton,'Enable','off')
    set(handles.number_of_pages_text,'String',num2str(NumberOfPages));
else
    set(handles.page_number_edit,'BackgroundColor',[1 1 1],'String','1')
    set(handles.increase_page_pushbutton,'Enable','on')
    set(handles.decrease_page_pushbutton,'Enable','on')
    set(handles.number_of_pages_text,'String',num2str(NumberOfPages));
end

function draw_parameter_table(result,page,handles)
axes(handles.estimates_axes)
% Make sure it is clear
cla
% Draw the column titles
str = result.title_position.Titles;
pos = result.title_position.TitlePosition;
N = length(str);
for i=1:N
    x=pos{i}(1);
    y=pos{i}(2);
    text(x,y,str{i},'Unit','pixel','FontSize',10,'FontName','Tahoma')
end
% Draw the lines
hold on;
for i=1:N
    % Now move it so that it is right aligned
    x=result.title_position.x_line_data{i};
    y=result.title_position.y_line_data{i};
    plot(x,y,'k');
end
hold off;
x = result.text_position.x_pos(:,:,page);
y = result.text_position.y_pos(:,:,page);
str = result.text_position.window_text(:,:,page);

N = size(x,1);

for i=1:N
    for j=1:5
        % Draw the data on the correct page
        if j==1
            text(x{i,j},y{i,j},str{i,j},'Interpreter','latex','FontSize',12);
        else
            text(x{i,j},y{i,j},str{i,j},'FontName','Tahoma','FontSize',10);
        end
    end
end

function display_model(handles,hObject)
% Check to see if the moodel has been processed.  If it has, go striaght to
% the draw model, else compute te strings then draw the model
results = handles.Results{handles.DisplayNumber};
if isfield(results,'TextFinished')
    draw_data(results,handles,hObject)
else
    % Generate the text labels
    generate_text(handles,hObject);
    % update handles
    handles = guidata(hObject);
    results = handles.Results{handles.DisplayNumber};
    % Draw the labels
    draw_data(results,handles,hObject)
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to do integer vallidation for ACF Lags
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [val, OK] = bounded_integer_validate(val,orig_val,LB,UB)
val = str2double(val);
% Test cases
%   Nan: return old value and  OK=0
%   <0 return 0 and OK=0
%   + but not integer, floor and OK=0
%   +, integer, OK =1
OK = false;
if isnan(val)
    val = orig_val;
elseif val<LB
    val = LB;
elseif val>UB;
    val = UB;
elseif floor(val)~=val
    val=floor(val);
else
    OK = true;
end


function [blastStructure,diamondReport]=getDiamond(organismID,fastaFile,...
    modelIDs,refFastaFiles,developMode,hideVerbose)
% getDiamond
%   Uses DIAMOND to perform a bidirectional BLAST between the organism
%   of interest and a set of template organisms
%
%   Input:
%   organismID      the id of the organism of interest. This should also
%                   match with the id supplied to getModelFromHomology
%   fastaFile       a FASTA file with the protein sequences for the
%                   organism of interest
%   modelIDs        a cell array of model ids. These must match the
%                   "model.id" fields in the "models" structure if the
%                   output is to be used with getModelFromHomology
%   refFastaFiles   a cell array with the paths to the corresponding FASTA
%                   files
%   developMode     true if blastReport should be generated that is used
%                   in the unit testing function for DIAMOND (opt, default
%                   false)
%   hideVerbose     true if no status messages should be printed (opt,
%                   default false)
%
%   Output:
%   blastStructure  structure containing the bidirectional homology
%                   measurements which are used by getModelFromHomology
%   diamondReport   structure containing MD5 hashes for FASTA database
%                   files and non-parsed BLAST output data. Will be blank
%                   if developMode is false.
%
%   NOTE: This function calls DIAMOND to perform a bidirectional homology
%   search between the organism of interest and a set of other organisms
%   using the '--more-sensitive' setting from DIAMOND. For the most
%   sensitive results, the use of getBlast() is adviced, however,
%   getDiamond() is a fast alternative (>15x faster). The blastStructure
%   generated is in the same format as those obtained from getBlast().
%
%   Usage: [blastStructure,diamondReport]=getDiamond(organismID,fastaFile,...
%    modelIDs,refFastaFiles,developMode,hideVerbose)

if nargin<5
    developMode = false;
end
if nargin<6
    hideVerbose = false;
end

%Everything should be cell arrays
organismID=convertCharArray(organismID);
fastaFile=convertCharArray(fastaFile);
modelIDs=convertCharArray(modelIDs);
refFastaFiles=convertCharArray(refFastaFiles);

%Create blank structures for results
blastStructure=[];
diamondReport.dbHashes={};
diamondReport.diamondTxtOutput={};

%Get the directory for RAVEN Toolbox.
ravenPath=findRAVENroot();

%Generate temporary names for DIAMOND databases and output files
tmpDB=tempname;
outFile=tempname;

%Check for existence of files. If no full path is specified for a file,
%assume that it is in the current folder
if isrow(refFastaFiles)
    files=horzcat(fastaFile,refFastaFiles);
else
    files=vertcat(fastaFile,refFastaFiles);
end

files=checkFileExistence(files,2); %Copy files to temp dir
fastaFile = files(1);
refFastaFiles = files(2:end);

%Identify the operating system
if isunix
    if ismac
        binEnd='.mac';
    else
        binEnd='';
    end
elseif ispc
    binEnd='.exe';
else
    dispEM('Unknown OS, exiting.')
    return
end

%Run DIAMOND multi-threaded to use all logical cores assigned to MATLAB.
cores = evalc('feature(''numcores'')');
cores = strsplit(cores, 'MATLAB was assigned: ');
cores = regexp(cores{2},'^\d*','match');
cores = cores{1};

%Create a database for the new organism and blast each of the refFastaFiles
%against it
[status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" makedb --in "' fastaFile{1} '" --db "' fullfile(tmpDB) '"']);
if developMode
    diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message,'[a-f0-9]{32}','match'));
end
if status~=0
    EM=['DIAMOND makedb did not run successfully, error: ', num2str(status)];
    dispEM(EM,true);
end

for i=1:numel(refFastaFiles)
    if ~hideVerbose
        fprintf(['Running DIAMOND blastp with "' modelIDs{i} '" against "' organismID{1} '"..\n']);
    end
    [status, ~]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" blastp --query "' refFastaFiles{i} '" --out "' outFile '_' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores ]);
    if developMode
        diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_' num2str(i)]);
    end
    if status~=0
        EM=['DIAMOND blastp did not run successfully, error: ', num2str(status)];
        dispEM(EM,true);
    end
end
delete([tmpDB filesep 'tmpDB*']);

%Then create a database for each of the reference organisms and blast the
%new organism against them
for i=1:numel(refFastaFiles)
    if ~hideVerbose
        fprintf(['Running DIAMOND blastp with "' organismID{1} '" against "' modelIDs{i} '"..\n']);
    end
    [status, message]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" makedb --in "' refFastaFiles{i} '" --db "' fullfile(tmpDB) '"']);
    if status~=0
        EM=['DIAMOND makedb did not run successfully, error: ', num2str(status)];
        dispEM(EM,true);
    end
    [status, ~]=system(['"' fullfile(ravenPath,'software','diamond',['diamond' binEnd]) '" blastp --query "' fastaFile{1} '" --out "' outFile '_r' num2str(i) '" --db "' fullfile(tmpDB) '" --more-sensitive --outfmt 6 qseqid sseqid evalue pident length bitscore ppos --threads ' cores]);
    if developMode
        diamondReport.dbHashes{numel(diamondReport.dbHashes)+1} = char(regexp(message,'[a-f0-9]{32}','match'));
        diamondReport.diamondTxtOutput{numel(diamondReport.diamondTxtOutput)+1}=importdata([outFile '_r' num2str(i)]);
    end
    if status~=0
        EM=['DIAMOND blastp did not run successfully, error: ', num2str(status)];
        dispEM(EM,true);
    end
    delete([tmpDB filesep 'tmpDB*']);
end

%Done with the DIAMOND blastp, do the parsing of the text files
for i=1:numel(refFastaFiles)*2
    tempStruct=[];
    if i<=numel(refFastaFiles)
        tempStruct.fromId=modelIDs{i};
        tempStruct.toId=organismID{1};
        A=readtable([outFile '_' num2str(i)],'Delimiter','\t','Format','%s%s%f%f%f%f%f');
    else
        tempStruct.fromId=organismID{1};
        tempStruct.toId=modelIDs{i-numel(refFastaFiles)};
        A=readtable([outFile '_r' num2str(i-numel(refFastaFiles))],'Delimiter','\t','Format','%s%s%f%f%f%f%f');
    end
    tempStruct.fromGenes=A{:,1};
    tempStruct.toGenes=A{:,2};
    tempStruct.evalue=table2array(A(:,3));
    tempStruct.identity=table2array(A(:,4));
    tempStruct.aligLen=table2array(A(:,5));
    tempStruct.bitscore=table2array(A(:,6));
    tempStruct.ppos=table2array(A(:,7));
    blastStructure=[blastStructure tempStruct];
end

%Remove the old tempfiles
delete([outFile '*']);
%Remove the temp fasta files
delete(files{:})
end


function varargout = finish_processing_step(this, module, varargin)
% finishes current processing step by deleting SPM-related temporary data
% on disk and writing back processed data from disk into data matrix
% results of processing step
%
%   Y = MrImage()
%   Y.finish_processing_step(module, varargin)
%
% This is a method of class MrImage.
%
% IN
%
% OUT
%
% EXAMPLE
%   Y.finish_processing_step('smooth', fileOutputSpm);
%
%   See also MrImage

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-07-03
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

itemsSave = this.parameters.save.keepCreatedFiles;

switch itemsSave
    case 0
        itemsSave = 'none';
    case 1
        itemsSave = 'all';
end

doSave = ~strcmpi(itemsSave, 'none');
doSaveRaw = ismember(itemsSave, {'all'});
doSaveNifti = ismember(itemsSave, {'nii', 'all', 'processed'});
doSaveObject = ismember(itemsSave, {'object', 'all', 'processed'});
doSaveProcessed = ismember(itemsSave, {'processed'});

pathSave        = this.parameters.save.path;
fileRaw         = this.get_filename('prefix', 'raw');
pathRaw         = fileparts(fileRaw);
fileProcessed   = this.get_filename;
nameImage       = this.name;

% for file prefixes
isSuffix = false;
isMixedCase = true;
doKeepCreatedFiles = (this.parameters.save.keepCreatedFiles == 1) & ...
    ~strcmpi(this.parameters.save.keepCreatedFiles, 'none');
hasMatlabbatch = ismember(module, this.get_all_matlabbatch_methods());
varargout = {};

% nifti leftovers exist and resulting files have to be renamed
if hasMatlabbatch
    fileMatlabbatch = fullfile(this.parameters.save.path, ...
        'matlabbatch.mat');
    filesCreated = {
        fileRaw
        fileProcessed
        fileMatlabbatch
        };
    switch module
        case 'apply_transformation_field'
            prefixOutput = 'w';
            fileOutputSpm = tapas_uniqc_prefix_files(fileRaw, prefixOutput);

        case 'coregister_to'
            % has matlabbatch, but does not create unnecessary files...,
            % since matlabbatch not executed...
            prefixOutput = '';
            fileStationaryImage = varargin{1};
            tapas_uniqc_delete_with_hdr(fileStationaryImage);
            fileOutputSpm = varargin{2}; % SPM output of reslice
            filesCreated = [
                filesCreated
                {fileStationaryImage}
                ];

        case 'apply_realign'

            prefixOutput = 'r';
            fileOutputSpm = tapas_uniqc_prefix_files(fileRaw, prefixOutput);
            filesCreated = {
                fileRaw
                fileProcessed
                };

        case 'realign'

            fileWeightingRealign = varargin{1};

            prefixOutput = 'r';
            fileOutputSpm = tapas_uniqc_prefix_files(fileRaw, prefixOutput);

            fileRealignmentParameters = regexprep(...
                tapas_uniqc_prefix_files(fileRaw, 'rp_'), '\.nii', '\.txt');
            fileRealignMean = tapas_uniqc_prefix_files(fileRaw, 'mean');
            filesCreated = [
                filesCreated
                {fileRealignmentParameters; fileRealignMean;}
                fileWeightingRealign
                ];

            varargout{1} = load(fileRealignmentParameters, '-ascii');

        case 'reslice'

            prefixOutput = 'r';
            fileOutputSpm = tapas_uniqc_prefix_files(fileRaw, prefixOutput);

            fnTargetGeometry = varargin{1};

            % dummy image of target geometry always deleted
            tapas_uniqc_delete_with_hdr(fnTargetGeometry);

        case 'segment'
            % parse inputs
            tissueTypes = varargin{1};
            imageOutputSpace = varargin{2};
            deformationFieldDirection = varargin{3};
            splitSuffix = varargin{4};

            % the bias-corrected anatomical is the output
            prefixOutput = 'm';
            fileOutputSpm = tapas_uniqc_prefix_files(fileRaw, prefixOutput);
            tpmOutputSpm = MrImage(tapas_uniqc_prefix_files(fileOutputSpm, '*', 1), ...
                'updateProperties', 'save');
            delete(tapas_uniqc_prefix_files(fileOutputSpm, '*', 1));
            tpmOutputSpm.save();

            % get current and new tissue probability map file names
            allTissueTypes = {'GM', 'WM', 'CSF', 'bone', 'fat', 'air'};
            indTissueTypes = find(ismember(lower(allTissueTypes), ...
                lower(tissueTypes)));
            nTissues = numel(indTissueTypes);

            filesTpm = cell(nTissues,1);
            filesTpmProcessed = cell(nTissues,1);

            for iTissue = 1:nTissues
                indTissue = indTissueTypes(iTissue);
                filesTpm{iTissue} = tapas_uniqc_prefix_files(fileRaw, ...
                    sprintf('c%d%', indTissue));
                % add wildcard for multi-channel segmentation
                filesTpm{iTissue} = tapas_uniqc_prefix_files(filesTpm{iTissue}, splitSuffix, 1);
                filesTpmProcessed{iTissue} = tapas_uniqc_prefix_files( ...
                    fullfile(this.parameters.save.path, sprintf('%s.nii', ...
                    lower(allTissueTypes{indTissue}))), ...
                    'tissueProbabilityMap', isSuffix, isMixedCase);
            end

            if ismember(imageOutputSpace, {'mni', 'standard', 'template', 'warped'})
                filesTpm = tapas_uniqc_prefix_files(filesTpm, 'w');
                filesTpmProcessed = tapas_uniqc_prefix_files(filesTpmProcessed, ...
                    'warped', isSuffix, isMixedCase);
            end

            % deformation field file names, if saved
            filesDeformationField = {};
            filesDeformationFieldProcessed = {};

            hasForwardField = ismember(deformationFieldDirection, {'forward', 'both', 'all'});
            hasBackwardField = ismember(deformationFieldDirection, {'backward', 'both', 'all'});
            if hasForwardField
                filesDeformationField{end+1,1} = tapas_uniqc_prefix_files(fileRaw, 'y_');
                filesDeformationField{end,1} = tapas_uniqc_prefix_files(filesDeformationField{end,1}, splitSuffix, 1);
                filesDeformationFieldProcessed{end+1,1} = ...
                    fullfile(pathSave, 'forwardDeformationField.nii');
            end
            if hasBackwardField
                filesDeformationField{end+1, 1} = tapas_uniqc_prefix_files(fileRaw, 'iy_');
                filesDeformationField{end,1} = tapas_uniqc_prefix_files(filesDeformationField{end,1}, splitSuffix, 1);
                filesDeformationFieldProcessed{end+1, 1} = ...
                    fullfile(pathSave, 'backwardDeformationField.nii');
            end

            % bias field names
            fileBiasField = cellstr(tapas_uniqc_prefix_files(fileRaw, ...
                'BiasField_'));
            fileBiasFieldProcessed = cellstr(fullfile(pathSave, ...
                'biasField.nii'));

            % join bias field and bias field corrected outputs
            % add wildcard for multi-channel segmentation
            biasFieldPath = dir(tapas_uniqc_prefix_files(fileBiasField{1}, '*', 1));
            if ~isempty(biasFieldPath)
                tmpBiasField = MrImage(tapas_uniqc_prefix_files(fileBiasField{1}, '*', 1), ...
                    'updateProperties', 'save');
                delete(tapas_uniqc_prefix_files(fileBiasField{1}, '*', 1));
                tmpBiasField.save();
            else
                this.save('fileName', fullfile(fileBiasField{1}));
            end

            % move all image files to their final names
            filesMoveSource = [
                filesTpm
                filesDeformationField
                fileBiasField
                ];
            filesMoveTarget = [
                filesTpmProcessed
                filesDeformationFieldProcessed
                fileBiasFieldProcessed
                ];
            tapas_uniqc_move_with_hdr(filesMoveSource, filesMoveTarget);

            % now load all output variables

            % load tissue probability maps
            doLoadTissueProbabilityMaps = nargin >= 1;
            if doLoadTissueProbabilityMaps
                nTissues = numel(filesTpmProcessed);
                for iTissue = 1:nTissues
                    varargout{1}{iTissue,1} = MrImage(filesTpmProcessed{iTissue}, ...
                        'updateProperties', 'all');
                end
            end

            % load deformation fields, if wanted
            doLoadDeformationFields = nargout >= 2;
            if doLoadDeformationFields
                varargout{2} = {};
                if hasForwardField
                    varargout{2}{1,1} = MrImage(...
                        filesDeformationFieldProcessed{1}, ...
                        'updateProperties', 'all');
                    % 5th dimension is the deformation dimensions
                    allDimLabels = varargout{2}{1,1}.dimInfo.dimLabels;
                    varargout{2}{1,1}.dimInfo.dimLabels{5} = ['d_', ...
                        allDimLabels{1}, allDimLabels{2}, allDimLabels{3}];
                end

                if hasBackwardField
                    varargout{2}{end+1,1} = MrImage(...
                        filesDeformationFieldProcessed{end}, ...
                        'updateProperties', 'all');
                    % 5th dimension is the deformation dimensions
                    allDimLabels = varargout{2}{end,1}.dimInfo.dimLabels;
                    varargout{2}{end,1}.dimInfo.dimLabels{5} = ['d_', ...
                        allDimLabels{1}, allDimLabels{2}, allDimLabels{3}];
                end

            end

            doLoadBiasField = nargout >= 3;
            if doLoadBiasField
                varargout{3}{1} = MrImage(fileBiasFieldProcessed, ...
                    'updateProperties', 'all');
            else
                varargout{3}{1} = {};
            end

            % other file with normalization information for old
            % segmentation
            fileSeg8 = regexprep(fileRaw, '\.nii$', '_seg8\.mat');

            % files to be deleted, if specified
            if doSaveProcessed
                filesCreated = [
                    filesCreated
                    fileSeg8
                    ];
            else
                filesCreated = [
                    filesCreated
                    filesMoveTarget
                    fileSeg8
                    ];
            end

        case 'smooth'
            prefixOutput = 's';
            fileOutputSpm = tapas_uniqc_prefix_files(fileRaw, prefixOutput);
    end

    % copy dimInfo to SPM-output file, if it exists
    % coregister has already written new file incl. dimInfo
    % segment does not change the image
    if ~any(strcmp(module, {'coregister_to'}))
        fileDimInfoRaw = this.get_filename('prefix', 'dimInfoRaw');
        if exist(fileDimInfoRaw, 'file')
            copyfile(fileDimInfoRaw, tapas_uniqc_prefix_files(fileDimInfoRaw, prefixOutput))
        end
    end

    tapas_uniqc_move_with_hdr(fileOutputSpm, fileProcessed);

    % load back data into matrix
    this.load(fileProcessed);

    % check whether dimInfo is available as well
    [pathstr,name] = fileparts(fileProcessed);
    dimInfoFileName = dir(fullfile(pathstr, [name, '_dimInfo.mat']));
    if ~isempty(dimInfoFileName) && ~isempty(dimInfoFileName.name)
        loadDimInfo = load(fullfile(dimInfoFileName.folder, dimInfoFileName.name));
        % add only non-geometry related properties, as they are already set
        % from the loaded file (and change e.g. for reslice)
        newDimInfo = MrDimInfo(loadDimInfo.objectAsStruct);
        nonGeomDims = find(~ismember(newDimInfo.dimLabels, {'x', 'y', 'z'}));
        for nUpdateDims = nonGeomDims
            dimLabel = newDimInfo.dimLabels{nUpdateDims};
            % dim exists but needs to be set to correct value
            if ismember(dimLabel, this.dimInfo.dimLabels)
                this.dimInfo.set_dims(dimLabel, ...
                    'units', newDimInfo.(dimLabel).units, ...
                    'samplingPoints', newDimInfo.(dimLabel).samplingPoints, ...
                    'samplingWidths', newDimInfo.(dimLabel).samplingWidths);
                % dim does not exist and needs to be added
            else
                this.dimInfo.add_dims(dimLabel, ...
                    'units', newDimInfo.(dimLabel).units{:}, ...
                    'samplingPoints', newDimInfo.(dimLabel).samplingPoints{:}, ...
                    'samplingWidths', newDimInfo.(dimLabel).samplingWidths);
            end
        end
        % also add dimInfo to the bias field
        if any(strcmp(module, {'segment'})) && ~isempty(biasFieldPath)
            varargout{3}{1}.dimInfo = newDimInfo.copyobj();
        end
    end

    % remove NaNs
    this.data(isnan(this.data)) = 0;

    % delete all unwanted files
    if ~doSaveRaw
        tapas_uniqc_delete_with_hdr(filesCreated);

        % delete raw sub-folder of current processing step
        if exist(pathRaw, 'dir')
            delete(fullfile(pathRaw, '*'));
            rmdir(pathRaw);
        end

        [stat, mess, id] = rmdir(this.parameters.save.path);
    end
else % no matlabbatch created

    % NOTE: this saving is important, e.g. for compute_masks to allow for
    % multiple processing steps within a bigger step to be saved
    if doSave
        this.save();
    end
end

% to preserve name after reloading, because not in nifti-file
this.name = nameImage;

end


classdef(Abstract) AbstractDragCoefficientModel < matlab.mixin.SetGet & matlab.mixin.Heterogeneous & matlab.mixin.Copyable
    %AbstractDragCoefficientModel Summary of this class goes here
    %   Detailed explanation goes here

    properties(Abstract, Constant)
        enum DragCoefficientModelEnum
    end

    methods
        CdA = getDragCoeff(obj, ut, rVect, vVect, bodyInfo, mass, altitude, pressure, density, vVectECEFMag, totalAoA, aoa, sideslip);

        tf = usesTotalAoA(obj)

        tf = usesAoaAndSideslip(obj)

        useTf = openEditDialog(obj)
    end
end