#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3
#pragma version = 1.00
#pragma IgorVersion = 6.36
#pragma ModuleName = PearlMatrixImport

// author: matthias.muntwiler@psi.ch
// Copyright (c) 2016 Paul Scherrer Institut

// Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0

/// @file
/// @brief data file import for omicron matrix (STM) files
///
/// the matrix file import requires the matrix file reader XOP by thomas braun
/// (http://www.igorexchange.com/project/matrixFileReader)
/// which in turn requires an installation of vernissage by omicron nanotechnology.
///
/// @warning EXPERIMENTAL
/// the matrix import module and its interface may change radically in future revisions!
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2016 Paul Scherrer Institut @n
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
///   you may not use this file except in compliance with the License. @n
///   You may obtain a copy of the License at
///   http://www.apache.org/licenses/LICENSE-2.0

/// @namespace PearlMatrixImport
/// @brief data file import for omicron matrix (STM) files
///
/// PearlMatrixImport is declared in @ref pearl-matrix-import.ipf.

static strconstant package_name = "pearl_matrix_import"
static strconstant package_path = "root:packages:pearl_matrix_import:"

static strconstant ks_filematch_mtrx = "*_mtrx"

/// initialize the package data folder.
///
///
static function init_package()
	
	dfref savedf = getdatafolderdfr()
	
	setdatafolder root:
	newdatafolder /o/s packages
	newdatafolder /o/s $package_name
	
	variable /g loglevel = 3
	string /g dataFilePath = ""
	string /g resultFilePath = ""
	variable /g runCycle = 0
	variable /g scanCycle = 0
	string /g channelName = ""
	variable /g brickletID = 0
	variable /g V_MatrixFileReaderOverwrite = 1
	variable /g V_MatrixFileReaderFolder = 0
	variable /g V_MatrixFileReaderDouble = 0

	setdatafolder savedf
	return 0
end

/// check that the package data folder exists
///
/// initialize the package if the folder does not exist.
///
static function check_package_folder()
	dfref df_pack = $(package_path)
	if (DataFolderRefStatus(df_pack))
		svar /sdfr=df_pack /z resultFilePath
		if (!svar_exists(resultFilePath))
			init_package()
		endif
	else
		init_package()
	endif
end

/// initialize the package and reload preferences after an experiment is loaded.
static function AfterFileOpenHook(refNum,file,pathName,type,creator,kind)
	Variable refNum,kind
	String file,pathName,type,creator
	if( (kind >= 1) && (kind <= 2))
		init_package()
		//load_prefs()
	endif
	return 0
end

/// open a matrix file that was dropped into Igor.
///
/// preliminary implementation.
/// this should rather load the entire file and display a preview.
/// graph windows should be reused by subsequent loads.
/// also decide on a data saving location.
///
static function BeforeFileOpenHook(refNum,fileName,path,type,creator,kind)
	Variable refNum,kind
	String fileName,path,type,creator

	Variable handledOpen = 0
	if (StringMatch(fileName, ks_filematch_mtrx))
		setdatafolder root:
		newdatafolder /o /s matrix
		mtrx_load_preview("matrix", path, fileName)
		handledOpen = 1
	endif
	return handledOpen
End

/// generate elog message from bricklet metadata
///
/// @param metadata		two-column text wave
///
function /s matrix_format_elog_message(metadata)
	wave /t metadata

	string key
	string value
	variable nkeys = dimsize(metadata, 0)
	variable ikey
	string message_keys
	message_keys = "resultFileName;sampleName;channelName;"
	message_keys += "XYScanner.Points.value;XYScanner.Raster_Time.value;XYScanner.Raster_Time.unit;XYScanner.Width.value;XYScanner.Width.unit;XYScanner.Height.value;XYScanner.Height.unit;"
	message_keys += "GapVoltageControl.Voltage.value;GapVoltageControl.Voltage.unit;"
	message_keys += "Regulator.Loop_Gain_1_I.value;Regulator.Loop_Gain_1_I.unit;Regulator.Setpoint_1.value;Regulator.Setpoint_1.unit;"
	message_keys += "Spectroscopy.Device_1_Start.value;Spectroscopy.Device_1_Start.unit;Spectroscopy.Spectroscopy_Mode.value;"
	string message
	
	message_keys = ""
	for (ikey = 0; ikey < nkeys; ikey += 1)
		key = metadata[ikey][0]
		value = metadata[ikey][1]
		if (WhichListItem(key, message_keys) >= 0)
			message += key + " = " + value + "\r"
		endif
	endfor
end

function matrix_preview_2d(data, metadata)
	wave data
	wave /t metadata
	
	Display
	AppendImage data
	ModifyImage data ctab= {*,*,Mud,0}
	ModifyGraph margin(left)=30,margin(bottom)=30,margin(top)=5,margin(right)=5,height={Plan,1,left,bottom}
	ModifyGraph mirror=2
	ModifyGraph nticks=3
	ModifyGraph axThick=0.5
	ModifyGraph btLen=4

end

/// load the preview of a Matrix data file
///
/// the preview is loaded to the preview_image wave in the pearl_explorer data folder.
///
/// the s_file_info string is updated with information about the scan dimensions.
///
/// @param	filename		name of a file in the directory specified by the pearl_explorer_filepath path object.
///
/// @return	wave reference of the preview image
///
static function /wave preview_matrix_file(filename)
	string filename

	dfref saveDF = GetDataFolderDFR()
	setdatafolder $package_path
	svar s_preview_file
	svar s_preview_source
	mtrx_load_preview("preview_image", "pearl_explorer_filepath", filename)
	s_preview_file = filename
	s_preview_source = ""
	wave /z preview_image
	
	svar /z s_file_info
	if (! svar_exists(s_file_info))
		string /g s_file_info
	endif
	if (strlen(s_preview_file) > 0)
		s_file_info = mtrx_load_info("pearl_explorer_filepath", filename)
	else
		s_file_info = ""
	endif
	
	setdatafolder saveDF
	return preview_image
end

/// from matrixfilereader help
Structure errorCode
	int32	 SUCCESS 
	int32	 UNKNOWN_ERROR
	int32	 ALREADY_FILE_OPEN
	int32	 EMPTY_RESULTFILE
	int32	 FILE_NOT_READABLE
	int32	 NO_NEW_BRICKLETS
	int32	 WRONG_PARAMETER
	int32	 INTERNAL_ERROR_CONVERTING_DATA
	int32	 NO_FILE_OPEN
	int32	 INVALID_RANGE
	int32	 WAVE_EXIST
EndStructure

/// from matrixfilereader help
static Function initStruct(errorCode)
	Struct errorCode &errorCode

	errorCode.SUCCESS =0
	errorCode.UNKNOWN_ERROR=10001
	errorCode.ALREADY_FILE_OPEN=10002
	errorCode.EMPTY_RESULTFILE=10004
	errorCode.FILE_NOT_READABLE=10008
	errorCode.NO_NEW_BRICKLETS=10016
	errorCode.WRONG_PARAMETER=10032
	errorCode.INTERNAL_ERROR_CONVERTING_DATA=10064
	errorCode.NO_FILE_OPEN=10128
	errorCode.INVALID_RANGE=10256  
	errorCode.WAVE_EXIST=10512
	
end

/// load all data from a Matrix data file.
///
function mtrx_load_all()

	struct errorCode errorCode
	initStruct(errorCode)

#if exists("MFR_OpenResultFile")
	MFR_OpenResultFile
	if(V_flag != errorCode.SUCCESS)
		MFR_GetXOPErrorMessage
		return -1
	endif

	MFR_GetBrickletData
	if(V_flag != errorCode.SUCCESS)
		MFR_GetXOPErrorMessage
		return -1
	endif

	MFR_GetBrickletMetaData
	if(V_flag != errorCode.SUCCESS)
		MFR_GetXOPErrorMessage
		return -1
	endif

	return 0
#else
	return -1
#endif
end

/// parse matrix file names
///
/// parse matrix file names for result name, run cycle, scan cycle, and channel.
///
/// @param fileName			matrix result or data file name (without path).
///
/// @param resultFile			(out) base name of the result file.
///							append "_%04u.mtrx" to get the actual result file.
///							we do not know the chain link number at this stage.
///
/// @param runCycle			(out) run cycle number. necessary to look up the bricklet ID.
///
/// @param scanCycle		(out) scan cycle number. necessary to look up the bricklet ID.
///
/// @param channel			(out) channel name.
///
/// @return file type
///	@arg 0 result file (logbook)
///	@arg 1 result data file (bricklet)
///
/// result file names look like:
/// default_2015Apr20-124353_STM-STM_AtomManipulation_0001.mtrx,
/// default_2015Apr20-124353_STM-STM_AtomManipulation_0002.mtrx, etc.
/// the function returns the first part up to the experiment name ("AtomManipulation" in the examples).
/// all other return values set to defaults and must not be regarded.
///
/// result data files look like:
/// default_2015Apr20-124353_STM-STM_AtomManipulation--136_1.Aux1(V)_mtrx,
/// default_2015Apr20-124353_STM-STM_AtomManipulation--136_1.I(V)_mtrx,
/// default_2015Apr20-124353_STM-STM_AtomManipulation--14_1.I_mtrx,
/// default_2015Apr20-124353_STM-STM_AtomManipulation--14_1.Z_mtrx, etc.
/// the function returns all results as described in the parameter list.
///
function mtrx_parse_filename(fileName, resultFile, runCycle, scanCycle, channel)
	string fileName
	string &resultFile
	variable &runCycle
	variable &scanCycle
	string &channel
	
	variable fileType = 0
	resultFile = ""
	channel = ""
	runCycle = 0
	scanCycle = 0

	string regexp = ""
	string index1 = ""
	string index2 = ""
	string extension = ""
	
	if (StringMatch(fileName, "*.mtrx"))
		regexp = "(.+)_([[:digit:]]+)\.(.+)"
		SplitString /E=regexp fileName, resultFile, index1, extension
		fileType = 0
	else
		regexp = "(.+)--([[:digit:]]+)_([[:digit:]]+)\.((.+)_mtrx)"
		SplitString /E=regexp fileName, resultFile, index1, index2, extension, channel
		fileType = 1
		runCycle = str2num(index1)
		scanCycle = str2num(index2)
	endif
	
	return fileType
end

/// split a matrix filename and return the first three parts
///
/// we assume that the second (third) part contains the date (time).
/// the parts are separated by dash or underscore.
///
function /s mtrx_split_filename(fileName, prefix, datepart, timepart)
	string fileName
	string &prefix
	string &datepart
	string &timepart
	
	string regexp
	regexp = "([[:alpha:][:digit:]]+)[-_]([[:alpha:][:digit:]]+)[-_]([[:alpha:][:digit:]]+)[-_].+"
	SplitString /E=regexp fileName, prefix, datepart, timepart
	return datepart
end

/// create or look up a data folder based on a matrix file name.
///
/// the name of the folder is mtrx_date_time, where date and time are parsed from the file name.
/// for this to work, the file name must consist of at least three parts that are separated by dash or underscore.
/// the second (third) part contains the date (time).
/// date and time are copied as strings.
///
/// if the data folder exists, a reference to the existing folder is returned.
///
/// @param fileName		name of the result or data file.
///
/// @param df_base		(optional) base data folder. 
///						default: current folder.
///
/// @return				reference of the newly created or existing data folder.
///
function /df mtrx_create_folder(fileName, [df_base])
	string fileName
	dfref df_base
	
	if (ParamIsDefault(df_base))
		df_base = GetDataFolderDFR()
	endif

	string prefix
	string datepart
	string timepart
	string folderName
	
	mtrx_split_filename(fileName, prefix, datepart, timepart)
	folderName = "mtrx_" + datepart + "_" + timepart
	folderName = CleanupName(folderName, 0)
	
	dfref df_save = GetDataFolderDFR()
	setdatafolder root:
	newdatafolder /o /s $foldername
	dfref df = GetDataFolderDFR()

	setdatafolder df_save
	return df
end

/// create a data folder for bricklet data.
///
/// the name of the folder is, for example "r23s2" where the first (second) number is the run (scan) cycle.
/// run cycle and scan cycle numbers are taken from the open matrix file unless overridden by optional arguments.
///
/// if the data folder exists, a reference to the existing folder is returned.
/// if one of the run or scan cycle numbers is lower than 1, the base folder is returned.
///
/// @param df_base		(optional) base data folder. 
///						default: current folder.
///
/// @param runCycle		(optional) run cycle number. must be >= 1.
///						default: from last mtrx_open_file call.
///
/// @param scanCycle	(optional) scan cycle number. must be >= 1.
///						default: from last mtrx_open_file call.
///
/// @return				reference of the newly created or existing data folder.
///
function /df mtrx_get_cycle_folder([df_base, runCycle, scanCycle])
	dfref df_base
	variable runCycle
	variable scanCycle

	dfref df_save = GetDataFolderDFR()
	dfref df_pack = $(package_path)
	if (ParamIsDefault(df_base))
		df_base = GetDataFolderDFR()
	endif
	if (ParamIsDefault(runCycle))
		nvar /sdfr=df_pack defRunCycle = runCycle
		runCycle = defRunCycle
	endif
	if (ParamIsDefault(scanCycle))
		nvar /sdfr=df_pack defScanCycle = scanCycle
		scanCycle = defScanCycle
	endif

	string dfname
	if ((runCycle >= 1) && (scanCycle >= 1))
		sprintf dfname, "r%us%u", runCycle, scanCycle
		setdatafolder df_base
		dfref df = $dfname
		if (DataFolderRefStatus(df) == 0)
			newdatafolder $dfname
			dfref df = $dfname
		endif
	else
		dfref df = df_base
	endif
	
	setdatafolder df_save
	return df
end

/// find out bricklet ID of a file
///
/// @warning EXPERIMENTAL
/// the code of this function is inefficient.
/// the function may be removed in a later version.
///
/// @param resultFile			base name of result file without chain link number and extension.
///							as returned by mtrx_parse_filename.
///
/// @param runCycle			requested run cycle.
///							0 = first available.
///
/// @param scanCycle		requested scan cycle.
///							0 = first available.
///
/// @param channel			channel name. for example: "I", "Z", "Aux(V)", etc.
///							empty string: first available.
///
/// @return bricklet ID, or -1 if an error occurred.
///
function mtrx_file_brickletID(resultFile, runCycle, scanCycle, channel)
	string resultFile
	variable runCycle
	variable scanCycle
	string channel

	dfref df_overview = NewFreeDataFolder()
	variable link = 1
	variable id = -1
	variable idx = 0
	
	string resultFileName
#if exists("MFR_OpenResultFile")
	struct errorCode errorCode
	initStruct(errorCode)
	do
		sprintf resultFileName, "%s_%04u.mtrx", resultFile, link
		MFR_OpenResultFile /K resultFileName
		if(V_flag != errorCode.SUCCESS)
			return -1
		endif
		MFR_CreateOverviewTable /DEST=df_overview
		// dimension labels are: brickletID, scanCycleCount, runCycleCount, sequenceID, dimension, channelName
		wave /t /sdfr=df_overview overviewTable
		make /n=(dimsize(overviewTable, 0)) /i /u /free runcycles, scancycles, ids, match
		make /n=(dimsize(overviewTable, 0)) /t /free channels
		ids = str2num(overviewtable[p][%brickletID])
		if (runcycle > 0)
			runcycles = str2num(overviewtable[p][%runCycleCount])
		else
			runcycles = runcycle
		endif
		if (scancycle > 0)
			scancycles = str2num(overviewtable[p][%scanCycleCount])
		else
			scancycles = scancycle
		endif
		if (strlen(channel) > 0)
			channels = overviewTable[p][%channelName]
		else
			channels = channel
		endif
		Extract /FREE ids, match_ids, (scancycles == scanCycle) && (runcycles == runCycle) && (cmpstr(channels, channel) == 0)
		if (numpnts(match_ids) > 0)
			id = match_ids[0]
		else
			link += 1
		endif
	while (id < 0)
#endif
	return id
end

/// open a matrix result or data file
///
/// this function opens a matrix result file (.mtrx) or data file (.*_mtrx).
///
/// if a data file is selected, the function locates the corresponding result file, opens it,
/// and looks up the bricklet ID of the data file.
/// if a result file is selected, the function opens it but does not look up bricklet IDs.
///
/// the result file remains open and can be accessed using the mtrx_ functions or MFR_ operations.
/// once a result file is open, you can easily access any bricklets linked to it,
/// i.e., any run cycle, scan cycle, and channel.
///
/// the function stores information about the opened file in a global package data folder.
/// if the same result file is opened again later, the information is reused and the file not read again.
/// this may cause problems if the file has been modified in the meantime,
/// or if the cached data become corrupt for some reason.
/// the function detects if a data file is not linked in the open result file, and updates the cache.
/// in other situations it may be necessary to force a reload.
///
/// @todo fix possible cache issues, add an option to override the cache.
///
/// @param pathName			igor path name or empty string.
///
/// @param fileName			file name, with or without path, or empty string.
///
/// @return file type
///	@arg 0 result file (logbook)
///	@arg 1 result data file (bricklet)
///	@arg -1 error, no data loaded
///	@arg -2 matrixfilereader.xop not installed
///
function mtrx_open_file(pathName, fileNameOrPath)
	string pathName
	string fileNameOrPath

	check_package_folder()
	dfref df_save = GetDataFolderDFR()
	dfref df_pack = $(package_path)
	svar /sdfr=df_pack dataFilePath
	svar /sdfr=df_pack resultFilePath
	nvar /sdfr=df_pack runCycle
	nvar /sdfr=df_pack scanCycle
	svar /sdfr=df_pack channelName
	nvar /sdfr=df_pack brickletID

	string loc_resultFileName
	string loc_resultFilePath
	variable loc_runCycle
	variable loc_scanCycle
	string loc_channelName

	// make sure we have a valid and complete file path
	GetFileFolderInfo /P=$pathName /Q /Z=2 fileNameOrPath
	string filePath
	if ((v_flag == 0) && (v_isFile))
		filePath = s_path
	else
		return -1
	endif

	// get base file name
	string fileName
	string fileDir
	string baseFileName
	variable fileType
	fileName = ParseFilePath(0, filePath, ":", 1, 0)
	fileDir = ParseFilePath(1, filePath, ":", 1, 0)
	fileType = mtrx_parse_filename(fileName, baseFileName, loc_runCycle, loc_scanCycle, loc_channelName)
	
	variable link = 1
	variable id = -1
	variable result = -1
	variable using_cache = 0
	
	struct errorCode errorCode
	initStruct(errorCode)
	do
		sprintf loc_resultFileName, "%s_%04u.mtrx", baseFileName, link
		loc_resultFilePath = fileDir + loc_resultFileName
#if exists("MFR_OpenResultFile")
		if ((strlen(resultFilePath) == 0) || (cmpstr(loc_resultFilePath, resultFilePath) != 0))
			MFR_OpenResultFile /K loc_resultFilePath
			if(V_flag != errorCode.SUCCESS)
				MFR_GetXOPErrorMessage
				result = -1
				break
			endif
			resultFilePath = loc_resultFilePath
			if (fileType == 1)
				dataFilePath = filePath
			else
				dataFilePath = ""
			endif
			runCycle = 0
			scanCycle = 0
			channelName = ""
			brickletID = 0
			MFR_CreateOverviewTable /DEST=df_pack
			if(V_flag != errorCode.SUCCESS)
				MFR_GetXOPErrorMessage
				result = -1
				break
			endif
			using_cache = 0
		else
			using_cache = 1
		endif
#else
		print "matrixfilereader.xop not installed"
		result = -2
		break
#endif
		// dimension labels are: brickletID, scanCycleCount, runCycleCount, sequenceID, dimension, channelName
		wave /t /sdfr=df_pack overviewTable
		make /n=(dimsize(overviewTable, 0)) /i /u /o df_pack:runCycles, df_pack:scanCycles, df_pack:ids
		make /n=(dimsize(overviewTable, 0)) /t /o df_pack:channels
		wave /sdfr=df_pack ids, runCycles, scanCycles
		wave /t /sdfr=df_pack channels
		ids = str2num(overviewtable[p][%brickletID])
		runCycles = str2num(overviewtable[p][%runCycleCount])
		scanCycles = str2num(overviewtable[p][%scanCycleCount])
		channels = overviewTable[p][%channelName]
		result = fileType

		// if a data file is opened, make sure we found the right result file
		if ((loc_runCycle > 0) && (loc_scanCycle > 0))
			Extract /FREE ids, match_ids, (runCycles == loc_runCycle) && (scanCycles == loc_scanCycle) && (cmpstr(channels, loc_channelName) == 0)
			if (numpnts(match_ids) > 0)
				id = match_ids[0]
				runCycle = loc_runCycle
				scanCycle = loc_scanCycle
				channelName = loc_channelName
				brickletID = id
				break
			elseif (using_cache)
				resultFilePath = ""
			else
				link += 1
			endif
		else
			break
		endif
	while (id < 0)
	
	return result
end

/// load a preview image from a Matrix data file.
///
/// the data wave is loaded into the current data folder.
///
/// @param destName			destination wave name. the wave is created in the current data folder.
///
/// @param pathName			igor symbolic path name. can be empty if the path is specified in FileName or a dialog box should be displayed
///
/// @param fileName			if empty a dialog box shows up
///							the file name must adhere to the format
///							"{prefix}-{date}-{time}-{anything}--{run_cycle}_{scan_cycle}.{extension}".
///							the first three seperators can alternatively be underscores.
///							it may be necessary to change the configuration of the Matrix application.
///
/// @param traces			(currently not used) semicolon-separated list of preferred traces.
///							the items of the list are match strings for the Igor StringMatch function.
///							only the first matching trace is loaded from the file.
///							default: "*Up;*Down;*ReUp;*ReDown;"
///
/// @return					semicolon-separated list of loaded waves including partial path from current data folder.
///
function /s mtrx_load_preview(destName, pathName, fileName, [traces])
	string destName
	string pathName
	string fileName
	string traces
		
	if (ParamIsDefault(traces))
		traces = "*Up;*Down;*ReUp;*ReDown;"
	endif
	
	dfref df_save = GetDataFolderDFR()

	string datanames = ""
	string datapaths = ""
	variable filestatus = mtrx_open_file(pathName, fileName)
	if (filestatus != 1)
		return ""
	endif

	dfref df_pack = $(package_path)
	svar /sdfr=df_pack dataFilePath
	svar /sdfr=df_pack resultFilePath
	nvar /sdfr=df_pack runCycle
	nvar /sdfr=df_pack scanCycle
	svar /sdfr=df_pack channelName
	nvar /sdfr=df_pack brickletID

#if exists("MFR_OpenResultFile")
	dfref df_data = df_save
	variable /g df_data:V_MatrixFileReaderOverwrite = 1
	variable /g df_data:V_MatrixFileReaderFolder = 0
	variable /g df_data:V_MatrixFileReaderDouble = 0
	struct errorCode errorCode
	initStruct(errorCode)
	MFR_GetBrickletData /N=destName /R=(brickletID) /S=2 /DEST=df_data
	if(V_flag == errorCode.SUCCESS)
		datanames = S_waveNames
		variable i
		variable n = ItemsInList(datanames)
		string s
		s = StringFromList(0, datanames)
		wave data = $s
		mtrx_scale_dataset(data)
		if (WaveDims(data) == 2)
			subtract_line_bg(data)
		endif
		datapaths = AddListItem(GetWavesDataFolder(data, 4), datapaths, ";", inf)
		for (i = 1; i < n; i += 1)
			s = StringFromList(i, datanames)
			killwaves /z $s
		endfor
	else
		MFR_GetXOPErrorMessage
	endif
	//MFR_GetBrickletMetaData /N=ANickName /R=(brickletID) // /DEST=dfref
#else
	print "matrixfilereader.xop not installed"
#endif
	
	setdatafolder df_save
	return datapaths
end

/// load all data from a Matrix data file.
///
/// the data wave is loaded into a sub-subfolder the current data folder.
/// the relative path has the format ":mtrx_{date}_{time}:r{run_cycle}s{scan_cycle}",
/// where the parameters {date}, {time}, {run_cycle} and {scan_cycle} are copied from the file name.
/// the file name must be formatted according to the specifications set out below.
///
/// @param pathName			igor symbolic path name. can be empty if the path is specified in FileName or a dialog box should be displayed
///
/// @param fileName			if empty a dialog box shows up
///							the file name must adhere to the format
///							"{prefix}-{date}-{time}-{anything}--{run_cycle}_{scan_cycle}.{extension}".
///							the first three seperators can alternatively be underscores.
///							it may be necessary to change the configuration of the Matrix application.
///
/// @param traces			(currently not used) semicolon-separated list of preferred traces.
///							the items of the list are match strings for the Igor StringMatch function.
///							only matching traces are loaded from the file.
///							default: "*Up;*Down;*ReUp;*ReDown;"
///
/// @return					semicolon-separated list of loaded waves including partial path from current data folder.
///
function /s mtrx_load_file(pathName, fileName, [traces])
	string pathName
	string fileName
	string traces
		
	if (ParamIsDefault(traces))
		traces = "*Up;*Down;*ReUp;*ReDown;"
	endif
	
	dfref df_save = GetDataFolderDFR()

	string datanames = ""
	string datapaths = ""
	variable filestatus = mtrx_open_file(pathName, fileName)
	if (filestatus != 1)
		return ""
	endif

	dfref df_pack = $(package_path)
	svar /sdfr=df_pack dataFilePath
	svar /sdfr=df_pack resultFilePath
	nvar /sdfr=df_pack runCycle
	nvar /sdfr=df_pack scanCycle
	svar /sdfr=df_pack channelName
	nvar /sdfr=df_pack brickletID

#if exists("MFR_OpenResultFile")
	string resultFileName = ParseFilePath(0, resultFilePath, ":", 1, 0)
	dfref df_result = mtrx_create_folder(resultFileName, df_base=df_save)
	dfref df_data = mtrx_get_cycle_folder(df_base = df_result)
	variable /g df_data:V_MatrixFileReaderOverwrite = 1
	variable /g df_data:V_MatrixFileReaderFolder = 0
	variable /g df_data:V_MatrixFileReaderDouble = 0
	
	struct errorCode errorCode
	initStruct(errorCode)
	string name
	name = CleanupName(channelName, 0)
	MFR_GetBrickletData /N=name /R=(brickletID) /DEST=df_data
	if(V_flag == errorCode.SUCCESS)
		datanames = S_waveNames
		variable i
		variable n = ItemsInList(datanames)
		string s
		for (i = 0; i < n; i += 1)
			s = StringFromList(i, datanames)
			wave /sdfr=df_data data = $s
			mtrx_scale_dataset(data)
			datapaths = AddListItem(GetWavesDataFolder(data, 4), datapaths, ";", inf)
		endfor
	else
		MFR_GetXOPErrorMessage
	endif
	//MFR_GetBrickletMetaData /N=ANickName /R=(brickletID) // /DEST=dfref
#else
	print "matrixfilereader.xop not installed"
#endif
	
	setdatafolder df_save
	return datapaths
end

function mtrx_scale_dataset(data)
	wave data

	dfref df_pack = $(package_path)
	nvar /sdfr=df_pack runCycle
	nvar /sdfr=df_pack scanCycle
	svar /sdfr=df_pack channelName
	nvar /sdfr=df_pack brickletID

	string scanDir = StringFromList(2, NameOfWave(data), "_")
	if (WaveDims(data) == 2)
		Note data, "AxisLabelX=X"
		Note data, "AxisLabelY=Y"
	endif
	Note data, "AxisLabelD=" + channelName
	string title
	sprintf title, "%u-%u %s %s", runCycle, scanCycle, channelName, scanDir
	Note data, "Dataset=" + title
end

/// load descriptive info from a Matrix data file.
///
/// the info string lists the following information for each scan contained in the file:
/// - path of the scan group inside the file.
/// - number of scan positions.
/// - dataset names of scan positioners.
/// - dataset names of detectors.
///
/// @param APathName		igor symbolic path name. can be empty if the path is specified in AFileName or a dialog box should be displayed
///
/// @param AFileName		if empty a dialog box shows up
///
/// @return					newline terminated string.
///
function /s mtrx_load_info(APathName, AFileName)
	string APathName
	string AFileName
		
	dfref saveDF = GetDataFolderDFR()
	dfref fileDF = NewFreeDataFolder()
	setdatafolder fileDF

	variable fileID
	string filepath
	string scanpaths
	variable nscans
	variable iscan
	string scanpath
	string info = ""
	  
	
	setdatafolder saveDF
	return info
end

/// remove linear background line-by-line
///
function subtract_line_bg(img)
	wave img
	
	variable nx = dimsize(img, 0)
	variable ny = dimsize(img, 1)
	variable iy
	make /n=(nx) /free line, fit
	for (iy = 0; iy < ny; iy += 1)
		line = img[p][iy]
		if (numtype(sum(line)) == 0)
			CurveFit /N /Q /NTHR=0 line line /D=fit
			img[][iy] = line[p] - fit[p]
		endif
	endfor
end
