function PIVlab_capture_lensctrl_GUI
fh = findobj('tag', 'lens_control_window');
if isempty(fh)
	hgui=getappdata(0,'hgui');
	mainpos=get(hgui,'Position');
	[focus,aperture,lighting]=get_lens_status;
	lens_control_window = figure('numbertitle','off','MenuBar','none','DockControls','off','Name','Lens control','Toolbar','none','Units','characters','Position',[mainpos(1)+mainpos(3)-35 mainpos(2) 35 15+1.5+4],'tag','lens_control_window','visible','on','KeyPressFcn', @key_press,'resize','off','CloseRequestFcn',@CloseRequestFcn);
	set (lens_control_window,'Units','Characters');

	handles = guihandles; %alle handles mit tag laden und ansprechbar machen
	guidata(lens_control_window,handles)
	setappdata(0,'hlens',lens_control_window);

	parentitem = get(lens_control_window, 'Position');

	margin=1.5;

	panelheight=5;
	handles.aperturepanel = uipanel(lens_control_window, 'Units','characters', 'Position', [1 parentitem(4)-panelheight-1.5 parentitem(3)-2 panelheight],'title','Aperture control','fontweight','bold','tag','aperturepanel');
	handles.focuspanel = uipanel(lens_control_window, 'Units','characters', 'Position', [1 parentitem(4)-panelheight*2-1.5 parentitem(3)-2 panelheight],'title','Focus control','fontweight','bold','tag','focuspanel');
	handles.lightpanel = uipanel(lens_control_window, 'Units','characters', 'Position', [1 parentitem(4)-panelheight*3-1.5 parentitem(3)-2 panelheight],'title','Light control','fontweight','bold','tag','lightpanel');
	handles.anglepanel = uipanel(lens_control_window, 'Units','characters', 'Position', [1 parentitem(4)-panelheight*3-1.5-4 parentitem(3)-2 4],'title','Camera attitude','fontweight','bold','tag','anglepanel');

	%% Load last selected setting & setup Configurations
	warning off
	load ('PIVlab_capture_resources\PIVlab_capture_lensconfig.mat','lens_configurations','selected_lens_config_nr');
	warning on
	if ~exist('selected_lens_config_nr','var')
		selected_lens_config_nr = 2; %set default to zeiss Dimension
	end
	put('selected_lens_config',selected_lens_config_nr)
	save ('PIVlab_capture_resources\PIVlab_capture_lensconfig.mat','lens_configurations','selected_lens_config_nr');
	% New lens configurations can be added to the table by modifying the variable 'lens_configurations' in the file 'PIVlab_capture_lensconfig.mat :
	% Example: lens_configurations=addvars(lens_configurations,[500;2500;500;2500],'NewVariableNames','Generic lens')
	handles.configu = uicontrol(lens_control_window,'Style','popupmenu', 'String',lens_configurations.Properties.VariableNames,'Value',retr('selected_lens_config'),'Units','characters', 'Fontunits','points','Position',[1 parentitem(4)-1.5 parentitem(3)/3*2 1.5],'Tag','configu','TooltipString','Lens configuration. Sets the limits for the servo motors.','Callback',@configu_Callback);
	parentitem=get(lens_control_window, 'Position');
	item=[parentitem(3)/3*2 0 parentitem(3)/3 1.5];
	handles.lens_status = uicontrol(lens_control_window,'Style','edit','units','characters','HorizontalAlignment','center','position',[item(1)+margin parentitem(4)-1.5 item(3)-margin*1.5 item(4)],'String','N/A','tag', 'lens_status','FontName','FixedWidth','BackgroundColor',[1 0 0],'Foregroundcolor',[0 0 0],'Enable','inactive','Fontweight','bold');
	if selected_lens_config_nr ~=4
		all_features_visible='on';
	else
		all_features_visible='off';
	end
	put ('all_features_visible',all_features_visible);

	%% APERTURE
	parentitem=get(handles.aperturepanel, 'Position');
	item=[0 0 0 0];
	item=[parentitem(3)/2*0 item(2)+item(4) parentitem(3)/2*1 1.5];
	handles.aperture_open = uicontrol(handles.aperturepanel,'Style','pushbutton','String','Iris open','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@aperture_set,'open'} ,'TooltipString','Load image data');

	item=[parentitem(3)/2*1 item(2) parentitem(3)/2*1 1.5];
	handles.aperture_close = uicontrol(handles.aperturepanel,'Style','pushbutton','String','Iris close','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@aperture_set,'close'} ,'TooltipString','Load image data');

	item=[parentitem(3)/2*0 item(2)+item(4) parentitem(3)/2 1];
	handles.aperture_label = uicontrol(handles.aperturepanel,'Style','text','String','Iris [us]','units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'horizontalalignment','right');

	item=[parentitem(3)/2*1 item(2) parentitem(3)/3 1];
	handles.aperture_edit = uicontrol(handles.aperturepanel,'Style','edit','String',num2str(aperture),'units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback',@aperture_edit_Callback,'tag','aperture_edit');

	%% FOCUS
	parentitem=get(handles.focuspanel, 'Position');
	item=[0 0 0 0];
	item=[0 item(2)+item(4) parentitem(3)/3 1.5];
	handles.focus_close = uicontrol(handles.focuspanel,'Style','pushbutton','String','Near','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@focus_set,'near'} ,'TooltipString','Load image data');

	item=[parentitem(3)/3*1 item(2) parentitem(3)/3 1.5];
	handles.focus_auto = uicontrol(handles.focuspanel,'Style','pushbutton','String','Auto','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@focus_set,'auto'} ,'TooltipString','Load image data');

	item=[parentitem(3)/3*2 item(2) parentitem(3)/3 1.5];
	handles.focus_far = uicontrol(handles.focuspanel,'Style','pushbutton','String','Far','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@focus_set,'far'} ,'TooltipString','Load image data');

	item=[parentitem(3)/2*0 item(2)+item(4) parentitem(3)/2 1];
	handles.focus_label = uicontrol(handles.focuspanel,'Style','text','String','Focus [us]','units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'horizontalalignment','right');

	item=[parentitem(3)/2*1 item(2) parentitem(3)/3 1];
	handles.focus_edit = uicontrol(handles.focuspanel,'Style','edit','String',num2str(focus),'units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback',@focus_edit_Callback,'tag','focus_edit');
	setappdata(lens_control_window,'handle_to_focus_edit_field',handles.focus_edit); %needed so other files can edit the contents.

	%% LIGHT
	parentitem=get(handles.lightpanel, 'Position');
	item=[0 0 0 0];
	item=[parentitem(3)/2*0 item(2)+item(4) parentitem(3)/2*1 1.5];
	handles.light_on = uicontrol(handles.lightpanel,'Style','pushbutton','String','Light on','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@light_switch,'on'} ,'TooltipString','Load image data');

	item=[parentitem(3)/2*1 item(2) parentitem(3)/2*1 1.5];
	handles.light_off = uicontrol(handles.lightpanel,'Style','pushbutton','String','Light off','Units','characters', 'Fontunits','points','Position',[item(1)+margin parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback', {@light_switch,'off'},'TooltipString','Load image data');

	item=[parentitem(3)/2*0 item(2)+item(4) parentitem(3)/2 1];
	handles.light_label = uicontrol(handles.lightpanel,'Style','text','String','Light status','units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'horizontalalignment','right');
	item=[parentitem(3)/2*1 item(2) parentitem(3)/3 1];
	handles.light_edit = uicontrol(handles.lightpanel,'Style','edit','String',num2str(lighting),'units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'Callback',@focus_edit_Callback,'tag','light_edit','enable','off');

	configu_Callback(handles.configu,[]) %execute callback and set servo limits

	%% Angle feedback
	parentitem=get(handles.anglepanel, 'Position');
	item=[0 0 parentitem(3) 1];
	handles.angle_measure = uicontrol(handles.anglepanel,'Style','checkbox','String','Measure angle','units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'horizontalalignment','left','Callback',@angle_measure_Callback,'tag','angle_measure');

	item=[parentitem(3)/2*0 item(2)+item(4) parentitem(3)/3 1.5];
	handles.pitch = uicontrol(handles.anglepanel,'Style','text','String','Pitch: 0','units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'horizontalalignment','left','tag','pitch');

	item=[parentitem(3)/2*1 item(2) parentitem(3)/3 1.5];
	handles.roll = uicontrol(handles.anglepanel,'Style','text','String','Roll: 0','units','characters','position',[item(1) parentitem(4)-item(4)-margin-item(2) item(3)-margin*2 item(4)],'horizontalalignment','left','tag','roll');
	limit_displayed_features
	find_devices

else %Figure handle does already exist --> bring UI to foreground.
	figure(fh)
end

function limit_displayed_features
all_features_visible=retr('all_features_visible');
aperturepanel=findobj('Tag','aperturepanel');
lightpanel=findobj('Tag','lightpanel');
anglepanel=findobj('Tag','anglepanel');

set(aperturepanel,'Visible',all_features_visible)
set(lightpanel,'Visible',all_features_visible)
set(anglepanel,'Visible',all_features_visible)

function find_devices
hgui = getappdata(0,'hgui');
serpo=getappdata(hgui,'serpo');
try
	serpo.Port; %is there no other way to determine if serialport is working...?
	%configureTerminator(serpo,'CR/LF');
	alreadyconnected=1;
catch
	alreadyconnected=0;
	delete(serpo)
end
if alreadyconnected==1
	flush(serpo)
	writeline(serpo,'WhatIsTheAngle?');
	warning off
	serial_answer=readline(serpo);
	warning on
	handles=gethand;
	lens_available=strfind(serial_answer,'Measured_Roll:');
	if ~isempty(lens_available) &&  lens_available~=0
		set(handles.lens_status, 'Backgroundcolor',[0 1 0])
		set(handles.lens_status, 'String','OK')
	end
end

function angle_measure_Callback (inpt,~)
if inpt.Value == 1 %on
	t = timer;
	t.Tag='lens_timer';
	t.StartDelay = 0.1;
	t.ExecutionMode = 'fixedRate';
	t.Period = 0.5;
	t.TimerFcn = @lens_timer_tick_fcn;
	start(t)
	setappdata(0,'handle_to_lens_timer_checkbox',inpt);
else
	stop(timerfind)
	delete(timerfind)
end

function lens_timer_tick_fcn(~,~)
hgui = getappdata(0,'hgui');
serpo=getappdata(hgui,'serpo');
try
	serpo.Port; %is there no other way to determine if serialport is working...?
	alreadyconnected=1;
catch
	alreadyconnected=0;
	delete(serpo)
end
if alreadyconnected==1
	flush(serpo)
	writeline(serpo,'WhatIsTheAngle?');
	warning off
	serial_answer=readline(serpo);
	warning on
	handles=gethand;
	if ~isempty (serial_answer) & strfind(serial_answer,'Measured_Roll:')==1
		Roll=serial_answer{1}(strfind(serial_answer,'Measured_Roll:')+14:strfind(serial_answer,char(9))-1);
		Pitch=serial_answer{1}(strfind(serial_answer,'Measured_Pitch:')+15:end);
		Roll=str2double(Roll)/100+retr('Roll_Offset');
		Pitch=str2double(Pitch)/100+retr('Pitch_Offset');
		set(handles.pitch,'String',['P: ' num2str(Pitch)])
		set(handles.roll,'String',['R: ' num2str(Roll)])
	else
		set(handles.pitch,'String','No reply')
		set(handles.roll,'String','No reply')
	end
end

function configu_Callback (inpt,~)
load ('PIVlab_capture_lensconfig.mat','lens_configurations');
focus_servo_lower_limit = lens_configurations{1,inpt.Value};
focus_servo_upper_limit = lens_configurations{2,inpt.Value};
aperture_servo_lower_limit = lens_configurations{3,inpt.Value};
aperture_servo_upper_limit = lens_configurations{4,inpt.Value};
Pitch_Offset = lens_configurations{5,inpt.Value};
Roll_Offset = lens_configurations{6,inpt.Value};

put('focus_servo_lower_limit',focus_servo_lower_limit)
put('focus_servo_upper_limit',focus_servo_upper_limit)
put('aperture_servo_lower_limit',aperture_servo_lower_limit)
put('aperture_servo_upper_limit',aperture_servo_upper_limit)
put('Pitch_Offset',Pitch_Offset)
put('Roll_Offset',Roll_Offset)

put('selected_lens_config',inpt.Value)
selected_lens_config_nr=inpt.Value;
save ('PIVlab_capture_resources\PIVlab_capture_lensconfig.mat','lens_configurations','selected_lens_config_nr');

handles=gethand;
focus=retr('focus');
aperture=retr('aperture');
lighting=retr('lighting');
if isempty(focus)
	focus=1500;
end
if isempty(aperture)
	aperture=1500;
end
if isempty(lighting)
	lighting=0;
end
set (handles.aperture_edit,'String',num2str(aperture))
set (handles.focus_edit,'String',num2str(focus))
set (handles.light_edit,'String',num2str(lighting))
%focus_edit_Callback(handles.aperture_edit,[])
%pause(0.2)
%aperture_edit_Callback(handles.focus_edit,[])
if selected_lens_config_nr ~=4
	all_features_visible='on';
else
	all_features_visible='off';
end
put ('all_features_visible',all_features_visible);
limit_displayed_features

function focus_set (~,~,inpt)

focus_step=100;
[focus,aperture,lighting]=get_lens_status;
if strmatch(inpt,'auto')
	put('sharpness_enabled',0);
	put('hist_enabled',0);
	if retr('capturing')==1 %camera is recording
		if retr('autofocus_enabled') == 1 %user pressed button while autofocus is running: Stop autofocus.
			put('autofocus_enabled',0); %toggles the autofocus_enabled variable. That is checked in PIVlab_capture_pco after each frame capture
		else
			put('autofocus_enabled',1);
		end
		%move to lower limit
		PIVlab_capture_lensctrl (retr('focus_servo_lower_limit'), retr('aperture'),retr('lighting'))
	else
		put('autofocus_enabled',0);
	end
end

if strmatch(inpt,'far')
	if focus>=retr('focus_servo_lower_limit')+focus_step
		focus=focus-focus_step;
	else
		focus=retr('focus_servo_lower_limit');
	end
	PIVlab_capture_lensctrl (focus, aperture,lighting)
end
if strmatch(inpt,'near')
	if focus<=retr('focus_servo_upper_limit')-focus_step
		focus=focus+focus_step;
	else
		focus=retr('focus_servo_upper_limit');
	end
	PIVlab_capture_lensctrl (focus, aperture,lighting)
end
%put('focus',focus);

update_edit_fields

function [focus,aperture,lighting]=get_lens_status
try %try to switch of camera angle report
	stop(timerfind)
	delete(timerfind)
	set(getappdata(0,'handle_to_lens_timer_checkbox'),'Value',0)
catch
end
focus=retr('focus');
aperture=retr('aperture');
lighting=retr('lighting');
if isempty(focus)
	focus=1500;
end
if isempty(aperture)
	aperture=1500;
end
if isempty(lighting)
	lighting=0;
end

function aperture_set (~,~,inpt)
[focus,aperture,lighting]=get_lens_status;
aperture_step=100;
if strmatch(inpt,'open')
	if aperture>=retr('aperture_servo_lower_limit')+aperture_step
		aperture=aperture-aperture_step;
	else
		aperture=retr('aperture_servo_lower_limit');
	end
end
if strmatch(inpt,'close')
	if aperture<=retr('aperture_servo_upper_limit')-aperture_step
		aperture=aperture+aperture_step;
	else
		aperture=retr('aperture_servo_upper_limit');
	end
end
%put('aperture',aperture);
PIVlab_capture_lensctrl (focus, aperture,lighting)
update_edit_fields

function aperture_edit_Callback(caller,~)
[focus,~,lighting]=get_lens_status;
aperture=str2double(caller.String);
if aperture > retr('aperture_servo_upper_limit')
	aperture =retr('aperture_servo_upper_limit');
end
if aperture < retr('aperture_servo_lower_limit')
	aperture =retr('aperture_servo_lower_limit');
end
caller.String=num2str(aperture);
%put('aperture',aperture);
PIVlab_capture_lensctrl (focus, aperture,lighting)

function focus_edit_Callback(caller,~)
[~,aperture,lighting]=get_lens_status;
focus=str2double(caller.String);
if focus > retr('focus_servo_upper_limit')
	focus =retr('focus_servo_upper_limit');
end
if focus< retr('focus_servo_lower_limit')
	focus =retr('focus_servo_lower_limit');
end
caller.String=num2str(focus);
%put('focus',focus);
PIVlab_capture_lensctrl (focus, aperture,lighting)

function update_edit_fields(~,~)
[focus,aperture,lighting]=get_lens_status;
handles=gethand;
set(handles.focus_edit ,'String', num2str(focus))
set(handles.aperture_edit ,'String', num2str(aperture))
set(handles.light_edit ,'String', num2str(lighting))

function light_switch (~, ~,inpt)
[focus,aperture,lighting]=get_lens_status;
if strmatch(inpt,'on')
	lighting=1;
end
if strmatch(inpt,'off')
	lighting=0;
end
%put('lighting',lighting);
PIVlab_capture_lensctrl (focus, aperture,lighting)
update_edit_fields

function put(name, what)
hgui=getappdata(0,'hgui');
setappdata(hgui, name, what);

function var = retr(name)
hgui=getappdata(0,'hgui');
var=getappdata(hgui, name);

function handles=gethand
hlens=getappdata(0,'hlens');
handles=guihandles(hlens);

function CloseRequestFcn(hObject, ~, ~)
try
	stop(timerfind)
	delete(timerfind)
catch
end
try
	delete(hObject);
catch
	delete(gcf);
end
put('autofocus_enabled',0);


function nWritten = export_MR_IOD(planC, filenameRoot, filenumber)
%"export_MR_IOD"
%   Writes to disk all scans contained in planC by constructing the modules
%   specified in part A.3.3 of PS 3.3 of the 2006 DICOM standard.  The
%   planC MUST have first been run through the function:
%
%   generate_DICOM_UID_Relationships.m.
%
%   filenameRoot is the the base path + filename to have file numbers
%   appended to, ie, "C:\tmp\DCM".  filenumber is the starting filenumber
%   to append, with results such as "C:\tmp\DCM00000", "C:\tmp\DCM00001"
%   etc.
%
%DK 07/25/08
%
%Usage:
%   nWritten = export_MR_IOD(planC, filenameRoot, filenumber);
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

indexS  = planC{end};

%Init number of files written to zero.
nWritten = 0;

for scanNum = 1:length(planC{indexS.scan})

    %Get the scan we are currently working with
    scanS   = planC{indexS.scan}(scanNum);

    %Build patient module.
    patientobj      = export_module('patient', 'scan', scanS);

    %Build general study module.
    studyobj        = export_module('general_study', scanS);

    %Build general series module.
    seriesobj       = export_module('general_series', scanS);

    %Build a frame of reference module.
    frameobj        = export_module('frame_of_reference', scanS);

    %Build a general equipment module.
    equipobj        = export_module('general_equipment', 'scan', scanS);

    %For slice-specific modules iterate over scaninfo.
    for i=1:length(planC{indexS.scan}.scanInfo)

        %Create a dcmobj to hold a single slice.
        dcmobj = org.dcm4che2.data.BasicDicomObject;

        %Get info for the slice we are handling.
        scanInfoS = scanS.scanInfo(i);

        %Build an image module from this particular slice (scanInfoS)
        imgobj          = export_module('general_image', 'scan', scanInfoS);

        %Build an image plane module from this particular slice (scanInfoS)
        imgplaneobj     = export_module('image_plane', 'scan', scanInfoS, scanS);

        %Build an image pixel module from this particular slice (scanInfoS)
        imgpixelobj     = export_module('image_pixel', 'scan', scanInfoS, scanS);

        %Build an MR image module from this particular slice (scanInfoS)
        mrimageobj      = export_module('MR_image', scanInfoS, scanS);

        SOPobj = export_module('SOP_common', 'scanInfo', scanInfoS);

        %Combine all modules into a single dcmobj.
        patientobj.copyTo(dcmobj);
        studyobj.copyTo(dcmobj);
        seriesobj.copyTo(dcmobj);
        frameobj.copyTo(dcmobj);
        equipobj.copyTo(dcmobj);
        imgobj.copyTo(dcmobj);
        imgplaneobj.copyTo(dcmobj);
        imgpixelobj.copyTo(dcmobj);
        mrimageobj.copyTo(dcmobj);
        SOPobj.copyTo(dcmobj);

        clear imgobj imgplaneobj imgpixelobj mrimageobj SOPobj

        fileNum  = num2str(filenumber + nWritten);
        filename = [filenameRoot repmat('0', [1 5-length(fileNum)]) fileNum];

        writefile_mldcm(dcmobj, filename);

        nWritten = nWritten + 1;

        clear dcmobj;
    end

    clear patientobj studyobj seriesobj frameobj equipobj

end


function annModel = annotateGEM(model,annPath,annType,addMiriams,addFields,overwrite)
% Add reaction, metabolite, and/or gene annotation to a model.
%
% Input:
%
%   model        Model structure.
%
%   annPath      Path to the annotation files, which suppose to be named as
%                'reactions.tsv', 'metabolites.tsv', and 'genes.tsv'。
%
%   annType      String or cell array of strings specifying the type(s) of
%                annotation data to add: 'rxn', 'met', and/or 'gene'. To
%                add all annotation types, use 'all'.
%                For example, annType={'rxn','gene'} will add reaction and
%                gene annotation information, but not metabolite.
%                (Opt, default 'all')
%
%   addMiriams   If TRUE, annotation information will be added to the
%                metMiriams field. If the metMiriams field does not exist,
%                it will be created.
%                (Opt, default TRUE)
%
%   addFields    If TRUE, separate model fields will be added for each
%                added ID type, e.g., reaction KEGG IDs will be added to a
%                rxnKEGGID field.
%                (Opt, default TRUE)
%
%   overwrite    If TRUE, any existing metMiriam or model ID field entries
%                will be overwritten. If FALSE, they will be appended with
%                the new data.
%                (Opt, default TRUE)
%
%
% Output:
%
%   annModel     Model structure with added annotation information.
%
%
% Usage:
%
%   annModel = annotateGEM(model,annPath,annType,addMiriams,addFields,overwrite);
%

%% Inputs and setup

if nargin < 2
    [ST, I] = dbstack('-completenames');
    annPath = strcat(fileparts(ST(I).file),'/../model');
end

if nargin < 3 || isempty(annType) || isequal(annType,'all')
    annType = {'rxn','met','gene'};
elseif ~all(ismember(annType,{'rxn','met','gene','reaction','metabolite'}))
    error('annType input(s) not recognized. Valid options are "rxn", "met", and/or "gene", or "all"');
end

if nargin < 4 || isempty(addMiriams)
    addMiriams = true;
end

if nargin < 5 || isempty(addFields)
    addFields = true;
end

if nargin < 6
    overwrite = true;
end

if (~addMiriams) && (~addFields)
    error('addMiriams and addFields are both FALSE - no annotation will be added.')
end

% map field names to those on identifiers.org (used for building Miriams)
id2miriam = {%reactions
             'rxnKEGGID'        'kegg.reaction'
             'rxnBiGGID'        'bigg.reaction'
             'rxnREACTOMEID'    'reactome'
             'rxnRecon3DID'     'vmhreaction'
             'rxnMetaNetXID'    'metanetx.reaction'
             'rxnTCDBID'        'tcdb'
             'rxnRheaID'        'rhea'
             'rxnRheaMasterID'  'rhea'
             % metabolites
             'metBiGGID'        'bigg.metabolite'
             'metKEGGID'        'kegg.compound'
             'metHMDBID'        'hmdb'
             'metChEBIID'       'chebi'
             'metPubChemID'     'pubchem.compound'
             'metLipidMapsID'   'lipidmaps'
             'metRecon3DID'     'vmhmetabolite'
             'metMetaNetXID'    'metanetx.chemical'
             % genes
             'genes'            'ensembl'
             'geneENSTID'       'ensembl'
             'geneENSPID'       'ensembl'
             'geneUniProtID'    'uniprot'
             'geneSymbols'      'hgnc.symbol'
             'geneEntrezID'     'ncbigene'
             };

%% Load and organize annotation data

% load reaction annotation data
if any(ismember({'rxn','reaction'},lower(annType)))
    tmpfile = fullfile(annPath,'reactions.tsv');
    rxnAssoc = importTsvFile(tmpfile);

    % strip "RHEA:" prefix from Rhea IDs since it should not be included in
    % the identifiers.org URL
    rxnAssoc.rxnRheaID = regexprep(rxnAssoc.rxnRheaID, 'RHEA:', '');
    rxnAssoc.rxnRheaMasterID = regexprep(rxnAssoc.rxnRheaMasterID, 'RHEA:', '');

    rxnAssocArray = struct2cell(rxnAssoc);
    numericFields = find(cellfun(@isnumeric, rxnAssocArray));
    for i = 1:numel(numericFields)
        % numeric fields must be converted to text for miriams
        rxnAssocArray{numericFields(i)} = arrayfun(@num2str, rxnAssocArray{numericFields(i)}, 'UniformOutput', false);
    end
    rxnAssocArray = horzcat(rxnAssocArray{:});
else
    rxnAssoc = [];
end

% load metabolite annotation data
if any(ismember({'met','metabolite'},lower(annType)))
    tmpfile = fullfile(annPath,'metabolites.tsv');
    metAssoc = importTsvFile(tmpfile);

    % ChEBI IDs should be of the form "CHEBI:#####"
    metAssoc.metChEBIID = regexprep(metAssoc.metChEBIID,'(^)(\d+)|(;\s*)(\d+)','$1CHEBI:$2');

    metAssocArray = struct2cell(metAssoc);
    numericFields = find(cellfun(@isnumeric, metAssocArray));
    for i = 1:numel(numericFields)
        % numeric fields need to be converted to text for miriams
        metAssocArray{numericFields(i)} = arrayfun(@num2str, metAssocArray{numericFields(i)}, 'UniformOutput', false);
    end
    metAssocArray = horzcat(metAssocArray{:});
else
    metAssoc = [];
end

% load and organize gene annotation data
if ismember('gene',lower(annType))
    tmpfile = fullfile(annPath,'genes.tsv');
    geneAssoc = importTsvFile(tmpfile);

    % add geneEnsemblID field if missing
    if ~isfield(geneAssoc, 'geneEnsemblID')
        geneAssoc.geneEnsemblID = geneAssoc.genes;
    end

    geneAssocArray = struct2cell(geneAssoc);
    geneAssocArray = horzcat(geneAssocArray{:});
else
    geneAssoc = [];
end

%% Add annotation data to model MIRIAM fields

if ( addMiriams )

    % Reactions
    if ~isempty(rxnAssoc)
        % map model rxns to those in the association structures
        [hasRxn,rxnInd] = ismember(model.rxns,rxnAssoc.rxns);

        % add rxnMiriams field if it does not exist or if overwrite is TRUE
        if ~isfield(model,'rxnMiriams') || overwrite
            model.rxnMiriams = repmat({''},size(model.rxns));
        end

        % map rxn ID fields to the proper identifiers.org terms
        [hasRxnField,rxnFieldInd] = ismember(fieldnames(rxnAssoc),id2miriam(:,1));
        miriamNames = id2miriam(rxnFieldInd(hasRxnField),2);
        miriamValues = rxnAssocArray(:,hasRxnField);

        % assign SBO terms to model reactions
        rxnSBO = repmat({'SBO:0000176'},size(model.rxns));  % default is "biochemical reaction"

        biomass_rxn = contains(lower(model.rxns),'biomass');
        biomass_met = ismember(lower(model.metNames),'biomass') & ismember(model.comps(model.metComps),'c');
        biomass_rxn = biomass_rxn | (model.S(biomass_met,:) > 0)';
        rxnSBO(biomass_rxn) = {'SBO:0000629'};  % "biomass production"

        transport_rxn = getTransportRxns(model);
        rxnSBO(transport_rxn) = {'SBO:0000185'};  % "translocation reaction"

        [~,exchange_rxn] = getExchangeRxns(model);
        rxnSBO(exchange_rxn) = {'SBO:0000627'};  % "exchange reaction"

        % add new data to the rxnMiriams field
        for i = 1:numel(model.rxns)
            if hasRxn(i)
                % add annotation
                model.rxnMiriams{i} = appendMiriamData(model.rxnMiriams{i}, miriamNames, miriamValues(rxnInd(i),:)');
            end
            % add SBO terms
            model.rxnMiriams{i} = appendMiriamData(model.rxnMiriams{i}, {'sbo'}, rxnSBO(i));
        end
    end

    % Metabolites
    if ~isempty(metAssoc)
        % map model mets to those in the association structures
        [hasMet,metInd] = ismember(model.mets,metAssoc.mets);

        % add metMiriams field if it does not exist or if overwrite is TRUE
        if ~isfield(model,'metMiriams') || overwrite
            model.metMiriams = repmat({''},size(model.mets));
        end

        % map met ID fields to the proper identifiers.org terms
        [hasMetField,metFieldInd] = ismember(fieldnames(metAssoc),id2miriam(:,1));
        miriamNames = id2miriam(metFieldInd(hasMetField),2);
        miriamValues = metAssocArray(:,hasMetField);

        % add new data to the metMiriams field
        for i = 1:numel(model.mets)
            if hasMet(i)
                % add annotation
                model.metMiriams{i} = appendMiriamData(model.metMiriams{i}, miriamNames, miriamValues(metInd(i),:)');
            end
            % add SBO term (SBO:0000247, "simple chemical" for all mets)
            model.metMiriams{i} = appendMiriamData(model.metMiriams{i}, {'sbo'}, {'SBO:0000247'});
        end
    end

    % Genes
    if ~isempty(geneAssoc)
        % Note: because geneAssoc was built using geneIDs directly from the
        %       model, we do not need to map model.genes to the geneAssoc
        %       structure - the indexing should be identical.

        % map model genes to those in the association structures
        [hasGene,geneInd] = ismember(model.genes,geneAssoc.genes);

        % add geneMiriams field if it does not exist or if overwrite is TRUE
        if ~isfield(model,'geneMiriams') || overwrite
            model.geneMiriams = repmat({''},size(model.genes));
        end

        % map gene ID fields to the proper identifiers.org terms
        [hasGeneField,geneFieldInd] = ismember(fieldnames(geneAssoc),id2miriam(:,1));
        miriamNames = id2miriam(geneFieldInd(hasGeneField),2);
        miriamValues = geneAssocArray(:,hasGeneField);

        % add new data to the geneMiriams field
        for i = 1:numel(model.genes)
            if hasGene(i)
                % add annotation
                model.geneMiriams{i} = appendMiriamData(model.geneMiriams{i}, miriamNames, miriamValues(geneInd(i),:)');
            end
            % add SBO term (SBO:0000243, "gene" for all genes)
            model.geneMiriams{i} = appendMiriamData(model.geneMiriams{i}, {'sbo'}, {'SBO:0000243'});
        end
    end

end

%% Add annotation data to individual model fields

if ( addFields )

    % merge association structures
    allAssoc = mergeStructures({rxnAssoc,metAssoc,geneAssoc});

    % some fields should not be added to the model
    remFields = {'rxns','mets','metsNoComp','genes'};
    allAssoc = rmfield(allAssoc, intersect(fieldnames(allAssoc),remFields));

    % get fields and their types
    f = fieldnames(allAssoc);

    if ~isempty(rxnAssoc)
        fieldType = repmat({'rxn'}, numel(f), 1);
    end

    if ~isempty(metAssoc)
        fieldType(ismember(f, fieldnames(metAssoc))) = {'met'};
    end

    if ~isempty(geneAssoc)
        fieldType(ismember(f, fieldnames(geneAssoc))) = {'gene'};
    end

    % add individual ID fields to the model
    for i = 1:numel(f)

        switch fieldType{i}
            case 'rxn'
                [hasRxn,rxnInd] = ismember(model.rxns,rxnAssoc.rxns);
                if isnumeric(allAssoc.(f{i}))
                    ids = NaN(size(model.rxns));
                else
                    ids = repmat({''},size(model.rxns));
                end
                ids(hasRxn) = allAssoc.(f{i})(rxnInd(hasRxn));
            case 'met'
                [hasMet,metInd] = ismember(model.mets,metAssoc.mets);
                if isnumeric(allAssoc.(f{i}))
                    ids = NaN(size(model.mets));
                else
                    ids = repmat({''},size(model.mets));
                end
                ids(hasMet) = allAssoc.(f{i})(metInd(hasMet));
            case 'gene'
                [hasGene,geneInd] = ismember(model.genes,geneAssoc.genes);
                if isnumeric(allAssoc.(f{i}))
                    ids = NaN(size(model.genes));
                else
                    ids = repmat({''},size(model.genes));
                end
                ids(hasGene) = allAssoc.(f{i})(geneInd(hasGene));
            otherwise
                continue
        end

        if ~isfield(model,f{i}) || overwrite
            model.(f{i}) = ids;
        else
            % merge new IDs with existing IDs
            merged_ids = join([model.(f{i}), ids],';');
            merged_ids = arrayfun(@(id) strjoin(unique(split(id,';')),';'), merged_ids, 'UniformOutput', false);
            model.(f{i}) = regexprep(merged_ids,'^;\s*','');  % remove any preceding semicolons
        end

    end

end

%%

% assign output
annModel = model;

end

%% Additional functions

function miriam_new = appendMiriamData(miriam,names,values)
% Append new entries to an existing miriam structure.

% ignore empty entries
remove = cellfun(@isempty,values);
names(remove) = [];
values(remove) = [];
if isempty(names)
    miriam_new = miriam;
    return
end

% convert existing miriam entry to cell array
if ~isempty(miriam)
    miriam = [miriam.name(:), miriam.value(:)];
end

% append miriam entry with new data
for i = 1:numel(names)
    if contains(values{i},';')
        % handle multiple values separated by semicolon (;)
        val = strtrim(split(values{i},';'));
        name = repmat(names(i),numel(val),1);
        miriam = [miriam; [name, val]];
    else
        miriam = [miriam; [names(i), values(i)]];
    end
end

% remove any repeated entries
[~,miriam_num] = ismember(miriam,miriam);
[~,uniq_ind] = unique(miriam_num,'rows');
miriam = miriam(uniq_ind,:);

% convert back to structure
miriam_new.name = miriam(:,1);
miriam_new.value = miriam(:,2);

end

function mergedStruct = mergeStructures(structs)
% Merge multiple structures into a single structure.

mergedStruct = {};
for i = 1:numel(structs)

    s = structs{i};

    if isempty(s)
        continue
    end

    f = fieldnames(s);
    for j = 1:numel(f)
        mergedStruct.(f{j}) = s.(f{j});
    end

end

end


function spm_clf(F)
% Clear specified figure of objects with visible handles
% FORMAT spm_clf(F)
% F - Figure number, or 'Tag' string of figure(s) to clear
%__________________________________________________________________________
%
% Clears the specified figure, deleting all objects with visible
% handles ('HandleVisibility'=='on').
%
% If the current window is 'Tag'ged interactive, then the figures name
% is cleared and the pointer reset.
%
% F Defaults to the current figure, if there is one.
%
% This is just a gateway to spm_figure('Clear',F).
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Andrew Holmes
% $Id: spm_clf.m 3899 2010-05-25 15:36:40Z guillaume $

%-Call spm_figure
%--------------------------------------------------------------------------
if nargin==0
    spm_figure('Clear')
else
    spm_figure('Clear',F)
end


 function out = subsref(ob, arg)
%function out = subsref(ob, arg)
% evaluate reference of the form ob.ref or ob(ref,:)

%
% ob.?
% get from Gblock object if possible, otherwise from base object
%

if arg(1).type == '.'

	if isfield(struct(ob), arg(1).subs)
		out = struct(ob);
		out = out.(arg(1).subs);
		if length(arg) > 1
			out = subsref(out, arg(2:end));
		end
		return
	end

	if isfield(struct(ob.base), arg(1).subs)
		out = struct(ob.base);
		out = out.(arg(1).subs);
		if length(arg) > 1
			out = subsref(out, arg(2:end));
		end
		return
	end

	% otherwise, error
	disp 'field names of base system'
	disp(fieldnames(ob.base))
	printf('subscript "%s" not a field of object', arg.subs)
	error bug

elseif length(arg) > 1
	disp '-------------'
	disp(arg)
%	disp(arg(:).type)
%	disp(arg(:).subs)
	error 'not done'

%
% ob{i_block}
% this is primary method 'handled' by this object!
%
elseif arg.type == '{}' && length(arg.subs) == 1

	out = ob;
	out.i_block = arg.subs{1};
	if out.i_block < 1 || out.i_block > out.nblock
		error 'bad block index'
	end

%
% ob(i_block)
% included for backward compatibility
%
elseif arg.type == '()' && length(arg.subs) == 1

	warning 'please use {} instead of () now for block objects'
	out = ob;
	out.i_block = arg.subs{1};
	if out.i_block < 1 || out.i_block > out.nblock
		error 'bad block index'
	end

%
% anything else gets passed to base object
% and we hope that it can handle it!
%
else
	tmp = subsref(ob.base, arg);
	if isa(tmp, class(ob.base))
		out = ob;
		out.base = tmp;
	else
		out = tmp;
	end
end


% test202 Read data from cube.db.

% OtMatlab: Matlab interface to the Omnitrans transport planning software

% Copyright (c) 2008, VORtech Computing
% Copyright (c) 2010, Omnitrans International

% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:

%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%     * Neither the name of VORtech Computing and Omnitrans International nor the names
%       of its contributors may be used to endorse or promote products derived
%       from this software without specific prior written permission.

% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

% 2006-04-25 Created (Johan Meijdam, VORtech)

function test202()
   OtStart();
   htable = OtTableNew();
   OtTableSetName(htable, [testdir() 'cube.DB']);
   if OtTableOpen(htable) ~= 1
      error('OtTableOpen failed')
   end
   OtTableLast(htable);

   disp('------');
   disp('FIELDS');
   disp('------');
   arr = OtTableFields(htable);
   disp(arr);
   disp('------------');
   disp(['VALUES (' num2str(OtTableRecordCount(htable)) ')']);
   disp('------------');
   while ~OtTableBof(htable)
      arr = OtTableGetAll(htable);
      disp(arr);
      OtTablePrior(htable);
   end
   OtTableClose(htable);
   OtTableFree(htable);
   OtStop();
end


function f = fliplr(f)
%FLIPLR   This has no effect on DELTAFUN object.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% This has no effect!

end


function rate = getFrameRate(view,scan)
%
% rate = getFrameRate(view,scan)
%
% djh, 2/21/2001
% ARW Renamed to getFrameRate 102402 to avoid conflict with BP psychToolbox
% function
% $Author: sayres $
% $Date: 2004/12/16 22:38:10 $

global dataTYPES;

rate = dataTYPES(view.curDataType).scanParams(scan).framePeriod;


function callback_tracker(mysrc,~,fn)
    % provide message showing where something is called from
    cb = mysrc.Callback;
    if iscell(cb)
        cb=char(cb{1});
    end
    if isa(cb,'function_handle')
        cb=func2str(cb);
    end
    fprintf('in: %s called from %s\n',cb,fn);
end


function [outputTable] = readExternalTable(inputTableFilename,varargin)
	% Read in table, decides whether to do a single or multiple tables, if multiple tables, should have the same column names.
	% Biafra Ahanonu
	% branched from controllerAnalysis: 2014.08.01 [16:09:16]
	% inputs
		% inputTableFilename - string pointing toward file
	% outputs
		%

	[outputTable] = ciapkg.io.readExternalTable(inputTableFilename,'passArgs', varargin);
end


function out = chebfunroot()
%CHEBFUNROOT   Root directory of Chebfun installation.
%   S = CHEBFUNROOT() returns a string that is the name of the directory where
%   Chebfun is installed.

out = fileparts(which('chebfunroot'));

end


function success=testAddSecondExperimentToNewWingedFlyJab()

testDataDirName='/groups/branson/bransonlab/projects/JAABA/test_data/flies_wing';
%testDataDirName='/Users/taylora/jaaba/sampledata_copy/flies_wing';
nameOfExpToAdd1='pBDPGAL4U_TrpA_Rig2Plate17BowlA_20110929T143440';
nameOfExpToAdd2='GMR_71G01_AE_01_TrpA_Rig2Plate17BowlD_20110921T084818';

% Make a Macguffin, from which we'll open a new file in JLabelData
macguffin=Macguffin('flies_wing');
macguffin.setMainBehaviorName('may22ing');
macguffin.setScoreFileName('scores_may22ing.mat');
macguffin.setTrxFileName('wingtracking_results.mat');
macguffin.setMovieFileName('movie.ufmf');

% Create the JLabelData object
data=JLabelData('setstatusfn',@(str)(fprintf('%s\n',str)), ...
                'clearstatusfn',@()(nop()), ...
                'isInteractive',false);
data.perframeGenerate=true;  % tell JLabelData to generate any missing perframe files

% Create a new file in the JLabelData object
data.newJabFile(macguffin);

% Add a new exp dir
[success,msg]=data.AddExpDir(fullfile(testDataDirName,nameOfExpToAdd1));
if ~success ,
  error(msg);
end

% Add a second exp dir
[success,msg]=data.AddExpDir(fullfile(testDataDirName,nameOfExpToAdd2));
if ~success ,
  error(msg);
end

% Clean up
data.closeJabFile();
data=[];  %#ok
success=true;

end


%GETSIZE_IN Get size_in field of mapping
%
%    SIZE_IN = GETSIZE_IN(W)


function add_rx_io(hRD)

% add AXI4 and AXI4-Lite slave interfaces
hRD.addAXI4SlaveInterface( ...
    'InterfaceConnection', 'axi_cpu_interconnect/M11_AXI', ...
    'BaseAddress',         '0x43C00000', ...
    'MasterAddressSpace',  'sys_ps7/Data');

% Control Status Pins
hRD.addInternalIOInterface( ...
    'InterfaceID',    'AD9361 CTRL OUT', ...
    'InterfaceType',  'IN', ...
    'PortName',       'read_status_ctrl', ...
    'PortWidth',      8, ...
    'InterfaceConnection', 'gpio_status', ...
    'IsRequired',     false);

% AGC Control Pin
hRD.addInternalIOInterface( ...
    'InterfaceID',    'Enable AGC', ...
    'InterfaceType',  'OUT', ...
    'PortName',       'en_agc', ...
    'PortWidth',      1, ...
    'InterfaceConnection', 'gpio_en_agc', ...
    'IsRequired',     false);

% DMA Ready signal
hRD.addInternalIOInterface( ...
    'InterfaceID',    'DMA Ready', ...
    'InterfaceType',  'IN', ...
    'PortName',       'dma_rdy', ...
    'PortWidth',      1, ...
    'InterfaceConnection', 'axi_ad9361_adc_dma/s_axis_ready', ...
    'IsRequired',     false);

% Reference design interfaces
hRD.addInternalIOInterface( ...
    'InterfaceID',    'IP Data Valid OUT', ...
    'InterfaceType',  'OUT', ...
    'PortName',       'dut_data_valid', ...
    'PortWidth',      1, ...
    'InterfaceConnection', 'util_ad9361_adc_pack/adc_valid_0', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'IP Data 0 OUT', ...
    'InterfaceType',  'OUT', ...
    'PortName',       'dut_data_0', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_0', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'IP Data 1 OUT', ...
    'InterfaceType',  'OUT', ...
    'PortName',       'dut_data_1', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_1', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'IP Data 2 OUT', ...
    'InterfaceType',  'OUT', ...
    'PortName',       'dut_data_2', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_2', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'IP Data 3 OUT', ...
    'InterfaceType',  'OUT', ...
    'PortName',       'dut_data_3', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_3', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'AD9361 ADC Data I0', ...
    'InterfaceType',  'IN', ...
    'PortName',       'sys_wfifo_0_dma_wdata', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_0', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'AD9361 ADC Data Q0', ...
    'InterfaceType',  'IN', ...
    'PortName',       'sys_wfifo_1_dma_wdata', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_1', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'AD9361 ADC Data I1', ...
    'InterfaceType',  'IN', ...
    'PortName',       'sys_wfifo_2_dma_wdata', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_2', ...
    'IsRequired',     false);

hRD.addInternalIOInterface( ...
    'InterfaceID',    'AD9361 ADC Data Q1', ...
    'InterfaceType',  'IN', ...
    'PortName',       'sys_wfifo_3_dma_wdata', ...
    'PortWidth',      16, ...
    'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_3', ...
    'IsRequired',     false);


function hRD = plugin_rd_rxtx
% Reference design definition

%   Copyright 2014-2015 The MathWorks, Inc.

% Call the common reference design definition function
hRD = AnalogDevices.pluto.plugin_rd('Rx & Tx');
AnalogDevices.pluto.add_io(hRD,'Rx & Tx');


% Shortcut to open the main settings editor of goGPS

%--- * --. --- --. .--. ... * ---------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%   This program is free software: you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation, either version 3 of the License, or
%   (at your option) any later version.
%
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License
%   along with this program.  If not, see <http://www.gnu.org/licenses/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------

addPathGoGPS
log = Core.getLogger();
log.setOutMode(0,[],1);
ui = Core_UI.getInstance();
flag_wait = false;
ui.openGUI(flag_wait);


%--- help for dsge/set_solution_to_companion ---
%
%  INTERNAL FUNCTION
%
%
%    Other functions named set_solution_to_companion
%
%       generic/set_solution_to_companion
%


function ft_realtime_fileproxy(cfg)

% FT_REALTIME_FILEPROXY reads continuous data from an EEG/MEG file and writes it to a
% FieldTrip buffer. This works for any file format that is supported by FieldTrip.
%
% The FieldTrip buffer is a network transparent server that allows the acquisition
% client to stream data to it. An analysis client can connect to read the data upon
% request. Multiple clients can connect simultaneously, each analyzing a specific
% aspect of the data concurrently.
%
% Use as
%   ft_realtime_fileproxy(cfg)
% with the following configuration options
%   cfg.minblocksize         = number, in seconds (default = 0)
%   cfg.maxblocksize         = number, in seconds (default = 1)
%   cfg.channel              = cell-array, see FT_CHANNELSELECTION (default = 'all')
%   cfg.jumptoeof            = jump to end of file at initialization (default = 'no')
%   cfg.readevent            = whether or not to copy events (default = 'no'; event type can also be specified; e.g., 'UPPT002')
%   cfg.speed                = relative speed at which data is written (default = inf)
%
% The source of the data is configured as
%   cfg.source.dataset       = string
% or alternatively to obtain more low-level control as
%   cfg.source.datafile      = string
%   cfg.source.headerfile    = string
%   cfg.source.eventfile     = string
%   cfg.source.dataformat    = string, default is determined automatic
%   cfg.source.headerformat  = string, default is determined automatic
%   cfg.source.eventformat   = string, default is determined automatic
%
% The target to write the data to is configured as
%   cfg.target.datafile      = string, target destination for the data (default = 'buffer://localhost:1972')
%   cfg.target.dataformat    = string, default is determined automatic
%
% To stop this realtime function, you have to press Ctrl-C
%
% See also FT_REALTIME_SIGNALPROXY, FT_REALTIME_SIGNALVIEWER

% Copyright (C) 2008, Robert Oostenveld
%
% 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$

% set the defaults
if ~isfield(cfg, 'source'),               cfg.source = [];                                  end
if ~isfield(cfg, 'target'),               cfg.target = [];                                  end
if ~isfield(cfg.source, 'headerformat'),  cfg.source.headerformat = [];                     end % default is detected automatically
if ~isfield(cfg.source, 'dataformat'),    cfg.source.dataformat = [];                       end % default is detected automatically
if ~isfield(cfg.target, 'headerformat'),  cfg.target.headerformat = [];                     end % default is detected automatically
if ~isfield(cfg.target, 'dataformat'),    cfg.target.dataformat = [];                       end % default is detected automatically
if ~isfield(cfg.target, 'datafile'),      cfg.target.datafile = 'buffer://localhost:1972';  end
if ~isfield(cfg, 'minblocksize'),         cfg.minblocksize = 0;                             end % in seconds
if ~isfield(cfg, 'maxblocksize'),         cfg.maxblocksize = 1;                             end % in seconds
if ~isfield(cfg, 'channel'),              cfg.channel = 'all';                              end
if ~isfield(cfg, 'jumptoeof'),            cfg.jumptoeof = 'no';                             end % jump to end of file at initialization
if ~isfield(cfg, 'readevent'),            cfg.readevent = 'no';                             end % capture events?
if ~isfield(cfg, 'speed'),                cfg.speed = inf ;                                 end % inf -> run as fast as possible

% translate dataset into datafile+headerfile
cfg.source = ft_checkconfig(cfg.source, 'dataset2files', 'yes');
cfg.target = ft_checkconfig(cfg.target, 'dataset2files', 'yes');
ft_checkconfig(cfg.source, 'required', {'datafile' 'headerfile'});
ft_checkconfig(cfg.target, 'required', {'datafile' 'headerfile'});

if ~isfield(cfg.source,'eventfile') || isempty(cfg.source.eventfile)
    cfg.source.eventfile = cfg.source.datafile;
end

if ~isfield(cfg.target,'eventfile') || isempty(cfg.target.eventfile)
    cfg.target.eventfile = cfg.target.datafile;
end

% ensure that the persistent variables related to caching are cleared
clear ft_read_header
% read the header for the first time
hdr = ft_read_header(cfg.source.headerfile);
fprintf('updating the header information, %d samples available\n', hdr.nSamples*hdr.nTrials);

% define a subset of channels for reading
cfg.channel = ft_channelselection(cfg.channel, hdr.label);
chanindx    = match_str(hdr.label, cfg.channel);
nchan       = length(chanindx);
if nchan==0
    ft_error('no channels were selected');
end

minblocksmp = round(cfg.minblocksize*hdr.Fs);
minblocksmp = max(minblocksmp, 1);
maxblocksmp = round(cfg.maxblocksize*hdr.Fs);
count       = 0;

if strcmp(cfg.jumptoeof, 'yes')
    prevSample = hdr.nSamples * hdr.nTrials;
else
    prevSample  = 0;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% this is the general BCI loop where realtime incoming data is handled
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

evt = [];
while true

    % determine number of samples available in buffer
    hdr = ft_read_header(cfg.source.headerfile, 'cache', true);

    % see whether new samples are available
    newsamples = (hdr.nSamples*hdr.nTrials-prevSample);

    if newsamples>=minblocksmp

        begsample  = prevSample+1;
        endsample  = prevSample+min(newsamples,maxblocksmp);

        % remember up to where the data was read
        count       = count + 1;
        fprintf('processing segment %d from sample %d to %d\n', count, begsample, endsample);

        % read data segment
        dat = ft_read_data(cfg.source.datafile,'header', hdr, 'dataformat', cfg.source.dataformat, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', false);

        % it only makes sense to read those events associated with the currently processed data
        if ~strcmp(cfg.readevent,'no')
          evt = ft_read_event(cfg.source.eventfile, 'header', hdr, 'minsample', begsample, 'maxsample', endsample);

          if ~strcmp(cfg.readevent,'yes')
            evt = ft_filter_event(evt, 'type', cfg.readevent);
          end

        end

        prevSample  = endsample;

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        % from here onward it is specific to writing the data to another stream
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

        if count==1
            % the input file may have a different offset than the output file
            offset = begsample - 1;
            % flush the file, write the header and subsequently write the data segment
            ft_write_data(cfg.target.datafile, dat, 'header', hdr, 'dataformat', cfg.target.dataformat, 'chanindx', chanindx, 'append', false);
            if ~strcmp(cfg.readevent,'no')
                for i=1:numel(evt)
                    evt(i).sample = evt(i).sample - offset;
                end
                ft_write_event(cfg.target.eventfile,evt,'append',false);
            end
        else
            % write the data segment
            ft_write_data(cfg.target.datafile, dat, 'header', hdr, 'dataformat', cfg.target.dataformat, 'chanindx', chanindx, 'append', true);
            if ~strcmp(cfg.readevent,'no')
                for i=1:numel(evt)
                    evt(i).sample = evt(i).sample - offset;
                end
                ft_write_event(cfg.target.eventfile,evt,'append',true);
            end
        end % if count==1

        % wait for a realistic amount of time
        pause(((endsample-begsample+1)/hdr.Fs)/cfg.speed);

    end % if enough new samples
end % while true


function S = nameval2struct(C)
%NAMEVAL2STRUCT Converts a cell array of name-value pairs to a struct.
% Usage:
%   struct = nameval2struct(nameval_cell)
%
% See also: struct2nameval

C = C(:);

% Check input
validateattributes(C, {'cell'}, {'vector', 'nonempty'})
assert(iseven(length(C)), ...
    'Cell arrays of name-value pairs must have an even number of elements.')
assert(all(cellfun(@ischar, C(1:2:end))), ...
    ['Cell arrays of name-value pairs must be in the format: ' ...
    '{''Name1'', Val1, ..., ''NameN'', ValN}'])

% Fix non-array inputs
N = cellfun(@numel, C(2:2:end));
if numel(unique(N)) ~= 1
    C(2:2:end) = cf(@(x) {x}, C(2:2:end));
end

% Convert to structure
S = struct(C{:});

end


% OtTableEdit Put the table into edit mode.
%
% Usage:
%    OtTableEdit(htable)

% OtMatlab: Matlab interface to the Omnitrans transport planning software

% Copyright (c) 2008, VORtech Computing
% Copyright (c) 2010, Omnitrans International

% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:

%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%     * Neither the name of VORtech Computing and Omnitrans International nor the names
%       of its contributors may be used to endorse or promote products derived
%       from this software without specific prior written permission.

% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

% 2006-04-11 Created (Johan Meijdam, VORtech)

function iret = OtTableEdit(htable)
   iret = Ot('table.edit', htable);
end


function result=isControlLike(modifiers)

result= ( ( ~ismac() && any(strcmp('control',modifiers)) ) ...
          || ...
          (  ismac() && any(strcmp('command',modifiers)) ) ) ;

end


function printpdf(fname)
%PRINTPDF Prints the current figure into a pdf document

% set(gca, 'LooseInset', get(gca, 'TightInset'));
fname = [regexprep(fname, '^(.*)\.pdf$', '$1'), '.eps'];
print('-depsc', fname);
if ~system(['epstopdf ', fname])
  system(['rm ', fname]);
end


%%  Publish vistasoft functional data on remote data server
%  (Archiva)
%
% These functional data are used as part of the mrvTest protocol. They
% accompany the validation data that are also stored on the remote site.
%
% BW did an svn update on the vistadata directory.  Then ran through this
% script.
%
% BW/ Copyright Vistasoft Team 2016

%% Open the remote data object

% Create remote data toolbox object
rd = RdtClient('vistasoft');

% To write to the archive, you must have a password
rd.credentialsDialog;

rd.crp('/vistadata/functional');

folder = fullfile(vistaRootPath,'local','functional');
a = rd.publishArtifacts(folder,'type','zip','verbose',true);
rdtPrintArtifactTable(a);

%% OLD

%% Open the remote data object

% Create remote data toolbox object
rd = RdtClient('vistasoft');

% To write to the archive, you must have a password
rd.credentialsDialog;

% Act with lots of printouts
rd.configuration.verbosity=0;

% Theses are the paths on the remote data server
p = rd.listRemotePaths;

%% First, copy the elements in the mrBOLD_01 directory
baseDir = fullfile(vistaRootPath,'..','vistadata','functional');
cd(baseDir);

% Note the the remote path and the local path match.
rd.crp('/vistadata/functional/mrBOLD_01');
fullDirectory = fullfile(baseDir,'mrBOLD_01');

% Publish the files in the mrBOLD_01 directory.
rd.publishArtifacts(fullDirectory,'print',true);

% You can check for the files in that remote path
a = rd.listArtifacts('print',true);

% Read one of the ones in the remote path
d = rd.readArtifact(a(12));

%% Now make the base mrBOLD_01 directory and publish the various subdirs

% Sometimes we have to go two deep.
baseDir = fullfile(vistaRootPath,'..','vistadata','functional','mrBOLD_01');
cd(baseDir)

%% Gray
rd.crp('/vistadata/functional/mrBOLD_01/Gray');
fullDirectory = fullfile(baseDir,'Gray');
dir(fullDirectory)

rd.publishArtifacts(fullDirectory,'verbose',true);
a = rd.listArtifacts;
a(:).artifactId

%% Inplane
rd.crp('/functional/mrBOLD_01/Inplane');
fullDirectory = fullfile(baseDir,'Inplane');
rd.publishArtifacts(fullDirectory);
a = rd.listArtifacts;
a(:).artifactId

%% Inplane/Original
rd.crp('/functional/mrBOLD_01/Inplane/Original');
fullDirectory = fullfile(baseDir,'Inplane','Original');
dir(fullDirectory)

rd.publishArtifacts(fullDirectory);
a = rd.listArtifacts;
a(:).artifactId

%% Inplane/ROIs
rd.crp('/functional/mrBOLD_01/Inplane/ROIs');
fullDirectory = fullfile(baseDir,'Inplane','ROIs');
dir(fullDirectory)

rd.publishArtifacts(fullDirectory);
a = rd.listArtifacts;
a(:).artifactId

%% Raw
rd.crp('/functional/mrBOLD_01/Raw');
fullDirectory = fullfile(baseDir,'Raw');
dir(fullDirectory)

rd.publishArtifacts(fullDirectory);
a = rd.listArtifacts;
a(:).artifactId

%% 3DAnatomy
rd.crp('/functional/mrBOLD_01/3DAnatomy');
fullDirectory = fullfile(baseDir,'3DAnatomy');
dir(fullDirectory)

rd.publishArtifacts(fullDirectory);
a = rd.listArtifacts;
a(:).artifactId

%% Volume
rd.crp('/functional/mrBOLD_01/Volume');
fullDirectory = fullfile(baseDir,'Volume');
dir(fullDirectory)

rd.publishArtifacts(fullDirectory);
a = rd.listArtifacts;
a(:).artifactId

%%


% inputdlg2() - inputdlg function clone with coloring and help for
%               eeglab().
%
% Usage:
%   >> Answer = inputdlg2(Prompt,Title,LineNo,DefAns,funcname);
%
% Inputs:
%   Same as inputdlg. Using the optional additionnal funcname parameter
%   the function will create a help button. The help message will be
%   displayed using the pophelp() function.
%
% Output:
%   Same as inputdlg
%
% Note: The advantage of this function is that the color of the window
%       can be changed and that it displays an help button. Edit
%       supergui to change window options. Also the parameter LineNo
%       can only be one.
%
% Author: Arnaud Delorme, CNL / Salk Institute, La Jolla, 11 August 2002
%
% See also: supergui(), inputgui()

% Copyright (C) Arnaud Delorme, CNL / Salk Institute, arno@salk.edu
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [result] = inputdlg2(Prompt,Title,LineNo,DefAns,funcname);

if nargin < 4
   help inputdlg2;
   return;
end;
if nargin < 5
	funcname = '';
end;

if length(Prompt) ~= length(DefAns)
	error('inputdlg2: prompt and default answer cell array must have the smae size');
end;

geometry = {};
listgui = {};

% determine if vertical or horizontal
% -----------------------------------
geomvert = [];
for index = 1:length(Prompt)
	geomvert = [geomvert size(Prompt{index},1) 1];  % default is vertical geometry
end;
if all(geomvert == 1) & length(Prompt) > 1
	geomvert = []; % horizontal
end;

for index = 1:length(Prompt)
	if ~isempty(geomvert) % vertical
		geometry = { geometry{:} [ 1] [1 ]};
	else
		geometry = { geometry{:} [ 1 0.6 ]};
	end;
	listgui = { listgui{:} { 'Style', 'text', 'string', Prompt{index}}  ...
				{ 'Style', 'edit', 'string', DefAns{index} } };
end;

result = inputgui(geometry, listgui, ['pophelp(''' funcname ''');'], Title, [], 'normal', geomvert);


clc; clear variables; format long g; close all;

%Include matlabrc, hopefully
%#function matlabrc

%set pathes if not deployed
if(~isdeployed)
    addpath(genpath('helper_methods'));
    addpath(genpath('formsGUIs'));
    addpath(genpath('kspTOT_RTS'));
    addpath(genpath('kspTOT_MissionArchitect'));
    addpath(genpath('kspTOT_LaunchVehicleDesigner'));
    addpath(genpath('kspTOT_VehicleSizer'));
    addpath(genpath('kspTOT_SingleUIs'));
    addpath(genpath('images'));
    addpath(genpath('kos_scripts'));
%     addpath('zArchive');
end

%set look and feel if deployed
if(isdeployed)
    if(ispc)
        javax.swing.UIManager.setLookAndFeel(com.sun.java.swing.plaf.windows.WindowsLookAndFeel);
    elseif(isunix)
        javax.swing.UIManager.setLookAndFeel(com.jgoodies.looks.plastic.Plastic3DLookAndFeel);
    elseif(ismac)
        javax.swing.UIManager.setLookAndFeel(com.apple.laf.AquaLookAndFeel);
    end
end

%Turn off class destructor warnings.  These pop up a lot for App Designer
%UIs when closing while using uiwait().
warning('off','MATLAB:class:DestructorError');

%Turn off java component being removed warnings.  User doesn't need to see
%that and I already know.
warning('off','MATLAB:ui:javacomponent:FunctionToBeRemoved');

%Populate initial ksptot log file
writeKsptotLogFileHeaderToConsole();

%display splashscreen
hS = splashScreenGUI_App(); drawnow;
t = tic;

%Initialize KSPTOT options
appOptions = getAppOptionsFromFile();

%Initalize KSP celestial body data
if(isprop(appOptions.ksptot,'bodiesinifile') && ~isempty(appOptions.ksptot.bodiesinifile) && exist(appOptions.ksptot.bodiesinifile,'file'))
    [celBodyDataFromINI,~,~] = inifile(appOptions.ksptot.bodiesinifile,'readall');
else
    [celBodyDataFromINI,~,~] = inifile('bodies.ini','readall');
end
celBodyData = processINIBodyInfo(celBodyDataFromINI, false, 'bodyInfo');
celBodyData = CelestialBodyData(celBodyData);
[goodTF, celBodyWarnMsgs] = verifyCelBodyData(celBodyData);
bodyNames = fieldnames(celBodyData);

if(not(goodTF))
    msg = {sprintf('Potential issues were found with the loaded celestial body information: \n')};
    for(i=1:length(celBodyWarnMsgs)) %#ok<*NO4LP>
        msg{end+1} = sprintf('\t%s\n', celBodyWarnMsgs{i}); %#ok<SAGROW>
    end

    msgbox(msg,'Celestial Body Data Warnings','warn');
end

%Pause for some time to show the splash screen
while(toc(t) < 1)
    pause(0.1);
end

%Set up the GUI for use
% mainGUIHandle = mainGUI(celBodyData, bodyNames, hS, appOptions);
mainGUIHandle = mainGUI_App(celBodyData, bodyNames, hS, appOptions);
if(isvalid(hS))
    delete(hS);
end


classdef MimImageDatabaseSeries < handle
    % MimImageDatabaseSeries.
    %
    %
    %     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 (Constant)
        CurrentVersionNumber = 3
    end

    properties (SetAccess = private)
        Name
        StudyName
        ImageMap
        Modality
        Date
        Time
        SeriesUid
        PatientId

        Version
        FirstImagePath
        ImageFileFormat
        StudyUid

    end

    methods
        function obj = MimImageDatabaseSeries(series_id, single_image_metainfo)
            if nargin > 0
                obj.Name = single_image_metainfo.SeriesDescription;
                obj.StudyName = single_image_metainfo.StudyDescription;
                obj.Modality = single_image_metainfo.Modality;
                obj.Date = single_image_metainfo.Date;
                obj.Time = single_image_metainfo.Time;
                obj.SeriesUid = series_id;
                obj.PatientId = single_image_metainfo.PatientId;

                obj.FirstImagePath = single_image_metainfo.ImagePath;
                obj.ImageFileFormat = single_image_metainfo.ImageFileFormat;
                obj.StudyUid = single_image_metainfo.StudyUid;

                obj.ImageMap = containers.Map;

                obj.Version = obj.CurrentVersionNumber;
            end
        end

        function visible_path = GetVisiblePath(obj)
            if obj.ImageMap.Count == 1
                filenames = obj.ImageMap.values;
                first_filename = filenames{1};
                visible_path = first_filename.FullFile;
            else
                visible_path = obj.FirstImagePath;
            end
        end

        function AddImage(obj, single_image_metainfo)
            obj.ImageMap(single_image_metainfo.ImageUid) = CoreFilename(single_image_metainfo.ImagePath, single_image_metainfo.ImageFilename);
        end

        function num_images = NumberOfImages(obj)
            num_images = obj.ImageMap.Count;
        end

        function image_info = GetImageInfo(obj)
            filenames_cells = [];
            filenames_objects = obj.ImageMap.values;
            for image_index = 1 : numel(filenames_objects)
                filenames_cells{end + 1} = filenames_objects{image_index};
            end
            path = obj.FirstImagePath;
            image_type = obj.ImageFileFormat;
            uid = obj.SeriesUid;
            study_uid = obj.StudyUid;
            modality = obj.Modality;
            image_info = PTKImageInfo(path, filenames_cells, image_type, uid, study_uid, modality);
        end

    end

    methods (Static)
        function obj = loadobj(a)
            % This method is called when the object is loaded from disk.

            if isa(a, 'MimImageDatabaseSeries')
                obj = a;
            else
                % In the case of a load error, loadobj() gives a struct
                obj = MimImageDatabaseSeries;
                for field = fieldnames(a)'
                    if isprop(obj, field{1})
                        mp = findprop(obj, (field{1}));
                        if (~mp.Constant) && (~mp.Dependent) && (~mp.Abstract)
                            obj.(field{1}) = a.(field{1});
                        end
                    end
                end
            end

        end
    end

end


classdef BSPTestsBase < matlab.unittest.TestCase
    properties(TestParameter)
        % Pull in board permutations
        configs = board_variants;
        ignored_builds = {'AnalogDevices.adrv9361z7035.ccbox_lvds.modem.plugin_board'};
        SynthesizeDesign = {false};
    end

    properties
        Count = 0;
        TotalTests = 0;
        Folder = pwd;
    end

    methods(TestClassSetup)
        function disableWarnings(~)
            warning('off','hdlcommon:hdlcommon:InterfaceNotAssigned');
        end
        function testCount(testCase)
            testCase.TotalTests = length(testCase.configs);
            CountS = 0;
            save('tc.mat','CountS');
        end
        function setRuntimeFolder(testCase)
            testCase.Folder = tempname(pwd);
        end
    end

    methods(TestClassTeardown)
        function enableWarnings(~)
            warning('on','hdlcommon:hdlcommon:InterfaceNotAssigned');
        end
        function collectLogs(~)
            if ~exist([pwd,'/logs'],'dir')
                mkdir('logs');
            end
            system('cp *.log logs/');
        end
    end

    methods(TestMethodSetup)
        function loadTestCount(testCase)
            l = load('tc.mat');
            CountS = l.CountS + 1;
            testCase.Count = CountS;
            save('tc.mat','CountS');
        end
    end

    methods

        function CollectLogs(testCase,cfgb)
            disp('Log collector called');
            rdn = strrep(cfgb.ReferenceDesignName,'/','_');
            rdn = strrep(rdn,'(','');
            rdn = strrep(rdn,')','');
            system(join(["find '",testCase.Folder,"' -name 'workflow_task_VivadoIPPackager.log' | xargs -I '{}' cp {} ."],''));
            if exist('workflow_task_VivadoIPPackager.log','file')
                disp('Found workflow_task_VivadoIPPackager... copying');
                movefile('workflow_task_VivadoIPPackager.log',[rdn,'_VivadoIPPackager_',cfgb.mode,'.log']);
            end
            system(join(["find '",testCase.Folder,"' -name 'workflow_task_CreateProject.log' | xargs -I '{}' cp {} ."],''));
            if exist('workflow_task_CreateProject.log','file')
                disp('Found workflow_task_CreateProject... copying');
                movefile('workflow_task_CreateProject.log',[rdn,'_CreateProject_',cfgb.mode,'.log']);
            end
            system(join(["find '",testCase.Folder,"' -name 'workflow_task_BuildFPGABitstream.log' | xargs -I '{}' cp {} ."],''));
            if exist('workflow_task_BuildFPGABitstream.log','file')
                disp('Found workflow_task_BuildFPGABitstream... copying');
                movefile('workflow_task_BuildFPGABitstream.log',[rdn,'_BuildFPGABitstream_',cfgb.mode,'.log']);
            end
        end

        function cfg = ADRV9361_Variants(~,s)

            variants = {...
                'ccbob_cmos','ccbob_lvds',...
                'ccbox_lvds','ccfmc_lvds'};
            cfg = {};
            s = strjoin(s(1:end-2),'.');
            h1 = str2func([s,'.common.plugin_board']);h1 = h1();

            for k = 1:length(variants)

                mode = 'rx';
                h2 = str2func([s,'.',variants{k},'.plugin_rd_rx']);h2 = h2();
                ReferenceDesignName = h2.ReferenceDesignName;
                vivado_version = h2.SupportedToolVersion{:};
                cfg1 = struct('Board',h1,...
                    'ReferenceDesignName',ReferenceDesignName,...
                    'vivado_version',vivado_version,'mode',mode);

                mode = 'tx';
                h2 = str2func([s,'.',variants{k},'.plugin_rd_tx']);h2 = h2();
                ReferenceDesignName = h2.ReferenceDesignName;
                vivado_version = h2.SupportedToolVersion{:};
                cfg2 = struct('Board',h1,...
                    'ReferenceDesignName',ReferenceDesignName,...
                    'vivado_version',vivado_version,'mode',mode);

                mode = 'rx_tx';
                h2 = str2func([s,'.',variants{k},'.plugin_rd_rxtx']);h2 = h2();
                ReferenceDesignName = h2.ReferenceDesignName;
                vivado_version = h2.SupportedToolVersion{:};
                cfg3 = struct('Board',h1,...
                    'ReferenceDesignName',ReferenceDesignName,...
                    'vivado_version',vivado_version,'mode',mode);
                cfg = [cfg(:)',{cfg1},{cfg2},{cfg3}];

            end

        end

        function cfg = extractConfigs(~,config)
            s = strsplit(config,'.');
            modes = strsplit(s{end},'_');
            mode = modes{end};
            h1 = str2func(config);h1 = h1();

            if strcmp(s{2},'adrv9361z7035') && ~isempty(strfind(s{2},'modem'))
                assert(0);
            elseif strcmp(s{2},'adrv9361z7035') || ...
                    strcmp(s{2},'adrv9364z7020')
                h = str2func([strjoin(s(1:2),'.'),'.common.plugin_board']);
            else
                h = str2func([strjoin(s(1:end-1),'.'),'.plugin_board']);
            end
            board = h();

            ReferenceDesignName = h1.ReferenceDesignName;
            vivado_version = h1.SupportedToolVersion{:};
            cfg = struct('Board',board,...
                'ReferenceDesignName',ReferenceDesignName,...
                'vivado_version',vivado_version,'mode',mode);
        end

        function setVivadoPath(~,vivado)
            if ispc
                pathname = ['C:\Xilinx\Vivado\',vivado,'\bin\vivado.bat'];
            elseif isunix
                pathname = ['/opt/Xilinx/Vivado/',vivado,'/bin/vivado'];
            end
            assert(exist(pathname,'file')>0,'Correct version of Vivado is unavailable or in a non-standard location');
            hdlsetuptoolpath('ToolName', 'Xilinx Vivado', ...
                'ToolPath', pathname);
            pause(4);
        end
    end

    methods(Test)
        function testMain(testCase, configs, SynthesizeDesign)
            % Filter out ignored configurations
            if ismember(configs,testCase.ignored_builds)
                assumeFail(testCase);
            end
            % Extract board configuration
            cfgb = testCase.extractConfigs(configs);
            %             for cfg = cfgs
            if exist(testCase.Folder,'dir')
                rmdir(testCase.Folder,'s');
                pause(1);
            end
            % Set up vivado
            testCase.setVivadoPath(cfgb.vivado_version);
            % Build
            disp(repmat('/',1,80));
            disp(['Building: ',cfgb.Board.BoardName,' | ',cfgb.mode,...
                ' (',num2str(testCase.Count),' of ',num2str(testCase.TotalTests),')']);
            res = build_design(cfgb.Board,cfgb.ReferenceDesignName,...
                cfgb.vivado_version,cfgb.mode,cfgb.Board.BoardName,...
                SynthesizeDesign,testCase.Folder);
            % Check
            if isfield(res,'message') || isa(res,'MException')
                disp(['Build error: ', cfgb.ReferenceDesignName]);
                disp(res);
                disp(res.message);
                disp(res.stack);
                testCase.CollectLogs(cfgb);
                verifyEmpty(testCase,res,res.message);
            end
        end
    end
end


function [metabolite_structure] = getMissingHMDBMolForm(metabolite_structure,molFileDirectory,retrievePotHMDB,startSearch,endSearch)
% This function uses getMolFileFromHMDB.m to obtain mol files for those
% metabolite structure entries that have hmdb id's specified. It also has
% the option find novel hmdb entries by querying hmdb for the metabolite
% names. Only perfect matches are considered. See retrievePotHitsHMDB.m for
% more details.
%
% INPUT
% metabolite_structure  metabolite structure
% molFileDirectory      directory where mol files should be stored
% retrievePotHMDB       default: true (attention: this could be time
%                       consuming)
% startSearch           specify where the search should start in the
%                       metabolite structure. Must be numeric (optional, default: all metabolites
%                       in the structure will be search for)
% endSearch             specify where the search should end in the
%                       metabolite structure. Must be numeric (optional, default: all metabolites
%                       in the structure will be search for)
%
% OUTPUT
% metabolite_structure  updated metabolite structure
%
% Ines Thiele 09/21

if ~exist('retrievePotHMDB','var')
    retrievePotHMDB = 1;
end
annotationSource = 'HMDB';
annotationType = 'automatic';

if ~exist('startSearch','var')
    startSearch = 1;
end
if ~exist('endSearch','var')
    endSearch = length(F);
end
%% obtain inchiString, charged formula, mol files for each metabolite
F = fieldnames(metabolite_structure);
for i = startSearch : endSearch
    if retrievePotHMDB == 1
        if isempty(metabolite_structure.(F{i}).hmdb) || ~isempty(find(isnan(metabolite_structure.(F{i}).hmdb)))
            % try to find the hmdb entry based on name
            met = metabolite_structure.(F{i}).metNames;
             % remove abbr with non-breaking space
             if any(ismember(met,char(160)))
                 met=strsplit(met,char(160));
                 met= met{1};
             end
            % remove parenthesis from metabolite names
            met = regexprep(met,'\(','');
            met = regexprep(met,'\)','');
            met = regexprep(met,'\[','');
            met = regexprep(met,'\]','');
            % retrieve potential hits
            try
                hmdb =retrievePotHitsHMDB(met); % the catching should avoid that script fails when connection to HMDB fails
            catch
                hmdb = [];
            end
            if ~isempty(hmdb)
                metabolite_structure.(F{i}).hmdb = hmdb;
                metabolite_structure.(F{i}).hmdb_source = ['Metabolite searched in HMDB by name',':',annotationType,':',datestr(now)];
            end
        end
    end
    % check that hmdb id is defined
    if ~isempty(metabolite_structure.(F{i}).hmdb) && length(find(isnan(metabolite_structure.(F{i}).hmdb)))==0
        % get inchistring only if there is no inchistring for the metabolite
        % defined
        %         if (isempty(metabolite_structure.(F{i}).inchiString)) || length(find(isnan(metabolite_structure.(F{i}).inchiString)))> 0
        %             % input into this function is the HMDB ID
        %             [inchiString] = getInchiStringFromHMDB( metabolite_structure.(F{i}).hmdb);
        %             if ~isempty(inchiString)
        %                 metabolite_structure.(F{i}).inchiString = inchiString;
        %                 metabolite_structure.(F{i}).inchiString_source = [annotationSource,':',annotationType,':',datestr(now)];
        %             end
        %         end
        % get mol files for each entry
        if isempty(metabolite_structure.(F{i}).hasmolfile) || length(find(isnan(metabolite_structure.(F{i}).hasmolfile)))>0 ...
                || strcmp(metabolite_structure.(F{i}).hasmolfile,'0')
            %             [outFile] = getMolFileFromHMDB(metabolite_structure.(F{i}).VMHId, metabolite_structure.(F{i}).hmdb,molFileDirectory);
            %             if ~isempty(outFile)
            %                 metabolite_structure.(F{i}).hasmolfile = num2str(1);
            %                 metabolite_structure.(F{i}).hasmolfile_source = [annotationSource,':',annotationType,':',datestr(now)];
            %
            [metabolite_structure,molCollectionReport] = getMolFilesMultipleSources(metabolite_structure, molFileDirectory,i,i,'hmdb');
            [metabolite_structure,IDsAdded,InchiKeyList,InchiStringList] = generateInchiFromMol(metabolite_structure,molFileDirectory, 1, 1,1,i,i);
            % end
        end

    end
end
[metabolite_structure] = addMetFormulaCharge(metabolite_structure,startSearch,endSearch);


function [outputData] = computeSaleaeOutput(matfile,varargin)
	% Processes Saleae output files. This is for data collected with Saleae Logic 1.x software, NOT 2.x.
	% Biafra Ahanonu
	% started: 2014.01.03 [19:13:01]
	% inputs
		% matfile - Str: path to Mat-file with Salaea outputs
	% outputs
		% outputData - table consisting of session times, frame clock (digital channel 0), and events in each channel or analog values in that frame.

	[outputData] = ciapkg.behavior.computeSaleaeOutput(matfile,'passArgs', varargin);
end


function [fmodel,removed] = filterMetMNXIDsViaRxns(model,mnx,ignoreComp,keepAtLeastOne)
%filterMetMNXIDsViaRxns  Remove met MNXID associations based on their rxns.
%
% filterMetMNXIDsViaRxns determines which met MNXIDs (if any) should be
% removed for mets associated with multiple MNXIDs. This is accomplished by
% finding all model rxns that involve the met, obtaining their associated
% MNX rxn IDs, and retrieving those rxns from the MNX database. Any MNXIDs
% associated with the met that are not included in that set of rxns from
% the MNX database will be removed.
%
% NOTE: metabolites that occur only in reactions that have no associated
%       rxnMNXIDs will be skipped, as there is insufficient evidence to
%       properly filter their associated metMNXIDs.
%
%
% USAGE:
%
%   [fmodel,removed] = filterMetMNXIDsViaRxns(model,mnx,ignoreComp,keepAtLeastOne);
%
% INPUT:
%
%   model    A model structure containing reaction and metabolite MNX ID
%            association fields ("rxnMNXID" and "metMNXID", respectively).
%
%   mnx      (Optional) An MNX database structure, containing reaction-
%            related information retrieved from the MNX database, generated
%            using the following command: mnx = buildMNXmodel('rxn');
%            By default, the function will automatically run the above
%            command to regenerate the MNX database structure (slower).
%
%   ignoreComp   (Optional, Default FALSE) If TRUE, metabolite compartments
%                will be ignored. In this case, identical mets of different
%                compartments will be lumped together, and when searching
%                for rxns involving the metabolite, the compartment will be
%                ignored.
%
%   keepAtLeastOne  (Optional, Default FALSE) In some cases, none of the
%                   MNXIDs associated with a metabolite are found in any of
%                   the reactions, and will result in a met with zero MNXID
%                   associations in the filtered model.
%                   If keepAtLeastOne = TRUE, then in cases such as this,
%                   the MNXID associations will not be removed in the
%                   filtered model, but will be indicated in the "removed"
%                   structure, with the text "ALL SHOULD BE REMOVED!" next
%                   to the metabolite ID.
%
% OUTPUT:
%
%   fmodel   A filtered model, which has removed metabolite MNXIDs that
%            were not found in any of the MNX reactions associated with the
%            model and that metabolite.
%
%   removed  A structure containing more detailed information on the met
%            MNXID associations that were removed.
%

% handle input arguments
if nargin < 2 || isempty(mnx)
    mnx = buildMNXmodel('rxn');
end
if nargin < 3
    ignoreComp = false;
end
if nargin < 4
    keepAtLeastOne = false;
end

% initialize "removed" structure and "fmodel" outputs
fmodel = model;
removed.mets = {};
removed.metMNXID = {};

% if metMNXID and/or rxnMNXID field contains multiple columns, consolidate
% into a single column with nested cell entries
if size(model.metMNXID,2) > 1
    model.metMNXID = nestCell(model.metMNXID,true);
end
if size(model.rxnMNXID,2) > 1
    model.rxnMNXID = nestCell(model.rxnMNXID,true);
end

if ( ignoreComp )

    % check if model has already merged met compartments, or if "mets"
    % contains non-unique elements
    if length(unique(model.mets)) ~= length(model.mets)
        error('Input model "mets" field must contain unique (non-repeated) elements.');
    elseif any(regexp(model.mets{1},'\d$'))
        % Note: this test is specific to models whose met IDs end in
        % numbers (e.g., "m00001"), with compartments appended to the end
        % of the ID (e.g., "m00001c" or "m00001[c]").
        fprintf('\nIt appears that the model compartments have already been merged.\n');
        fprintf('The "ignoreComp" flag will be ignored, since it will have no effect.\n');
        S = model.S;
        ignoreComp = false;
    else

        % strip compartment label from model met ID
        if endsWith(model.mets{1},']')
            % compartment name is formatted as "m00001[c]"
            model.mets = regexprep(model.mets,'\[\w\]$','');
        else
            % compartment name is formatted as "m00001c"
            model.mets = regexprep(model.mets,'.$','');
        end

        % check if ignoring compartments will actually do anything
        if length(unique(model.mets)) == length(model.mets)
            fprintf('\nIt appears that the model compartments have already been merged.\n');
            fprintf('The "ignoreComp" flag will be ignored, since it will have no effect.\n');
            S = model.S;
            ignoreComp = false;
        else

            fprintf('\nMetabolite compartments will be ignored. NOTE: This process will merge metMNXIDs\n');
            fprintf('of mets that are identical except for their compartment. If any mets are associated\n');
            fprintf('with compartment-specific metMNXIDs, this is not recommended.\n');

            % If ignoring compartments, convert the stoich matrix into a
            % binary met-rxn association matrix (i.e., set all nonzero
            % entries = 1), and combine all associations for metabolites
            % that are identical except for their compartment. Also combine
            % their metMNXIDs.
            S = (model.S ~= 0);
            [~,uniq_ind,met_groups] = unique(model.mets);
            h = waitbar(0,'Merging model metabolites across compartments...');
            for i = 1:max(met_groups)
                ind = (met_groups == i);
                S(ind,:) = repmat(any(S(ind,:),1),sum(ind),1);
                model.metMNXID(ind) = repmat({unique(horzcat(model.metMNXID{ind}))},sum(ind),1);
                waitbar(i/max(met_groups),h);
            end
            close(h);

            % now merge mets of different compartments into single met
            model.mets_nocomp = model.mets;  % first save these for indexing later
            model.mets = model.mets(uniq_ind);
            S = S(uniq_ind,:);
            model.metMNXID = model.metMNXID(uniq_ind);

        end

    end

else
    S = model.S;
end

% % Identify all mets with two or more MNXID associations. Mets with one or
% % zero MNXIDs will be ignored.
% multi_ind = find(cellfun(@numel,model.metMNXID) > 1);

% Iterate through mets with multiple MNXIDs, and determine which MNXIDs
% should be removed.
h = waitbar(0,'Processing metabolites...');
for i = 1:length(model.mets)

    % get indices of all rxns in which the met participates
    rxn_ind = S(i,:) ~= 0;

    % obtain a unique list of all the rxn MNXIDs associated with those
    % rxns, as well as their indices in the MNX database structure
    MNX_rxnIDs = unique(horzcat(model.rxnMNXID{rxn_ind}));
    MNX_rxn_ind = ismember(mnx.rxns,MNX_rxnIDs);

    if isempty(MNX_rxnIDs)
        % If none of the rxns are associated to any rxn MNXIDs, just skip
        % this metabolite. Otherwise, all met MNXIDs associated with the
        % metabolite will be removed, which is not so helpful.
        continue
    end

    % obtain unique set of all mets participating in the MNX rxns
    MNX_metIDs = unique([mnx.rxnMets{MNX_rxn_ind}]);

    % determine which of the MNXIDs currently associated to the model met
    % are not found in any of the MNX rxns
    rem_mets = ~ismember(model.metMNXID{i},MNX_metIDs);

    if ~any(rem_mets)
        % if no met MNXID associations should be removed, continue to next met
        continue
    elseif all(rem_mets) && (keepAtLeastOne)
        % In this case, all the MNXIDs associated with this met are to be
        % removed, but the user has indicated that they do not want this to
        % happen. Therefore,  NONE will be removed, but the information
        % will be reported in the "removed" structure, so these cases can
        % be manually evaluated by the user.
        removed.mets = [removed.mets; model.mets(i)];
        removed.metMNXID = [removed.metMNXID; {'ALL SHOULD BE REMOVED!'}];
    else
        % remove one or more MNXID associations from the metabolite
        removed.mets = [removed.mets; model.mets(i)];
        removed.metMNXID = [removed.metMNXID; {model.metMNXID{i}(rem_mets)}];
        model.metMNXID{i}(rem_mets) = [];
    end

    waitbar(i/length(model.mets),h);
end
close(h);

if any(cellfun(@(x) ismember({'ALL SHOULD BE REMOVED!'},x),removed.metMNXID))
    fprintf('\n*** NOTE: There was at least one case where ALL the MNXIDs associated\n');
    fprintf('          with a metabolite were not found in any of the involved rxns,\n');
    fprintf('          and therefore NONE were removed. See "removed" structure for\n');
    fprintf('          further information.\n\n');
end

% update filtered model (fmodel) metMNXID field
if ( ignoreComp )
    % if compartment was ignored (and thus mets were merged), distribute
    % the updated metMNXID assignments to all compartment-versions of each
    % metabolite.
    for i = 1:length(model.mets)
        ind = ismember(model.mets_nocomp,model.mets(i));
        fmodel.metMNXID(ind) = model.metMNXID(i);
    end
else
    fmodel.metMNXID = model.metMNXID;
end


function saveaspdf(h,filename)
%SAVEASPDF Save a figure as a clean pdf file ready for publication.
%   saveaspdf saves the current figure to the path defined in the global
%   SAVEASPDF_PATH, or to your Desktop if no path is specified. The
%   figure's 'Name' property is used as the filename.
%
%   saveaspdf(filename) saves the current figure as filename to the path
%   defined in the global SAVEASPDF_PATH, or to your Desktop if no path is
%   specified.
%
%   saveaspdf(h,filename) saves the figure with handle h as filename to the
%   path defined in the global SAVEASPDF_PATH, or to your Desktop if no
%   path is specified.
%
%   saveaspdf defines several globals which affect its output:
%
%      SAVEASPDF_DPI = 300            - The resolution of the pdf.
%      SAVEASPDF_PATH = ~/Desktop     - Where the pdf is saved.
%      SAVEASPDF_SCALELINEWIDTH = 1.8 - All lines and fonts are scaled by
%      SAVEASPDF_SCALEFONTSIZE = 1.1    these factors before printing. The
%                                       figure is returned to its original
%                                       state after printing.
%      SAVEASPDF_SIZE = []            - Set the output dimensions to [width
%                                       height] in centimeters. If empty,
%                                       the figure's dimensions are used.
%      SAVEASPDF_TRANSPARENT = false  - Set to true change the background
%                                       to be transparent (experimental).
%
%   See also saveas, print.

%   Note: add the following lines to Matlab's ghostscript.m for better
%   quality prints of figures with gradients or transparency. Source:
%   www.histed.org/wiki/index.php?title=Controlling_matlab's_pdf_output.
%
%   % In between "fprintf(rsp_fid, '-sOutputFile="%s"\n', pj.GhostName );"
%   % and "fclose(rsp_fid);":
%   if strcmp(pj.GhostDriver, 'pdfwrite')
%       % Uncomment to embed images into file as ZIP not jpeg compressed
%       fprintf(rsp_fid, ['-dAutoFilterColorImages=false ' ...
%           '-dColorImageFilter=/FlateEncode\n']);
%       % control resolution/downsampling of embedded images
%       fprintf(rsp_fid, '-dDownsampleColorImages=false\n');
%       %fprintf(rsp_fid, '-dColorImageDownsampleThreshold=1.5\n');
%       %fprintf(rsp_fid, '-dColorImageResolution=300\n');
%   end

%   Version: 07/07/11
%   Authors: Laurent Sorber (Laurent.Sorber@cs.kuleuven.be)

% Define globals.
global SAVEASPDF_DPI;
global SAVEASPDF_PATH;
global SAVEASPDF_SCALELINEWIDTH;
global SAVEASPDF_SCALEFONTSIZE;
global SAVEASPDF_SIZE;
global SAVEASPDF_TRANSPARENT;

% Set up default values.
if nargin == 1 && ischar(h)
    filename = h;
    h = gcf;
elseif nargin == 0
    h = gcf;
    filename = get(h,'Name');
    if isempty(filename)
        filename = ['Figure ' int2str(gcf)];
    end
end
if ~isempty(SAVEASPDF_DPI)
    dpi = SAVEASPDF_DPI;
else
    dpi = 300;
end
if ~isempty(SAVEASPDF_PATH)
    path = SAVEASPDF_PATH;
else
    if ispc, path = 'USERPROFILE'; else path = 'HOME'; end
    path = fullfile(getenv(path),'Desktop');
end
filename = fullfile(path,regexprep(filename,'\.[a-zA-Z]+','.pdf'));
if ~isempty(SAVEASPDF_SCALELINEWIDTH)
    scalelw = SAVEASPDF_SCALELINEWIDTH;
else
    scalelw = 1.8;
end
if ~isempty(SAVEASPDF_SCALEFONTSIZE)
    scalefs = SAVEASPDF_SCALEFONTSIZE;
else
    scalefs = 1.1;
end
if ~isempty(SAVEASPDF_SIZE)
    size = SAVEASPDF_SIZE;
else
    size = [];
end
if ~isempty(SAVEASPDF_TRANSPARENT)
    trans = SAVEASPDF_TRANSPARENT;
else
    trans = false;
end

% Backup current settings.
oldUnits = get(h,'Units');
oldPaperUnits = get(h,'PaperUnits');
oldPaperPosition = get(h,'PaperPosition');
oldPaperSize = get(h,'PaperSize');
axes = findall(h,'type','axes');
lines = [findall(h,'type','line'); axes];
text = [findall(h,'type','text'); axes];
oldFigColor = get(h,'Color');
oldAxesColor = get(axes,'Color');
oldLineWidth = get(lines,'LineWidth');
oldFontSize = get(text,'FontSize');
if ~iscell(oldAxesColor), oldAxesColor = {oldAxesColor}; end
if ~iscell(oldLineWidth), oldLineWidth = num2cell(oldLineWidth); end
if ~iscell(oldFontSize), oldFontSize = num2cell(oldFontSize); end

% Scale the line width and font size, reset units and update the positions.
set(lines,{'LineWidth'},num2cell(cellfun(@(x)x*scalelw,oldLineWidth)));
set(text,{'FontSize'},num2cell(cellfun(@(x)round(x*scalefs),oldFontSize)));
set(h,'Units','centimeters');
set(h,'PaperUnits','centimeters');
if isempty(size)
    curPosition = get(h,'Position');
    set(h,'PaperPosition',[0,0,curPosition(3:4)]);
    set(h,'PaperSize',curPosition(3:4));
else
    set(h,'PaperPosition',[0,0,size]);
    set(h,'PaperSize',size);
end
if trans
    set(h,'Color','none');
    set(axes,'Color','none');
end

% Print the pdf.
print(h,'-dpdf',sprintf('-r%d',dpi),filename);

% Restore the previous settings.
if trans
    set(h,'Color',oldFigColor);
    set(axes,{'Color'},oldAxesColor);
end
set(h,'Units',oldUnits);
set(h,'PaperUnits',oldPaperUnits);
set(h,'PaperPosition',oldPaperPosition);
set(h,'PaperSize',oldPaperSize);
set(text,{'FontSize'},oldFontSize);
set(lines,{'LineWidth'},oldLineWidth);


function datapt = lvd_StopwatchMassTasks(stateLogEntry, subTask, stopwatch)
%lvd_StopwatchMassTasks Summary of this function goes here
%   Detailed explanation goes here

    switch subTask
        case 'swValue'
            stopwatchStates = stateLogEntry.getAllStopwatchStates();
            stopwatchState = stopwatchStates([stopwatchStates.stopwatch] == stopwatch);

            if(not(isempty(stopwatchState)))
                stopwatchState = stopwatchState(1);
                datapt = stopwatchState.value;
            else
                datapt = -1;
            end
    end
end