class SpecifyModel(BaseInterface):
	"""Makes a model specification compatible with spm/fsl designers.

	The subject_info field should contain paradigm information in the form of
	a Bunch or a list of Bunch. The Bunch should contain the following
	information::

	 [Mandatory]
	 - conditions : list of names
	 - onsets : lists of onsets corresponding to each condition
	 - durations : lists of durations corresponding to each condition. Should be
	 left to a single 0 if all events are being modelled as impulses.

	 [Optional]
	 - regressor_names : list of str
		 list of names corresponding to each column. Should be None if
		 automatically assigned.
	 - regressors : list of lists
		values for each regressor - must correspond to the number of
		volumes in the functional run
	 - amplitudes : lists of amplitudes for each event. This will be ignored by
	   SPM's Level1Design.

	 The following two (tmod, pmod) will be ignored by any Level1Design class
	 other than SPM:

	 - tmod : lists of conditions that should be temporally modulated. Should
	   default to None if not being used.
	 - pmod : list of Bunch corresponding to conditions
	   - name : name of parametric modulator
	   - param : values of the modulator
	   - poly : degree of modulation

	Alternatively, you can provide information through event files.

	The event files have to be in 1, 2 or 3 column format with the columns
	corresponding to Onsets, Durations and Amplitudes and they have to have the
	name event_name.runXXX... e.g.: Words.run001.txt. The event_name part will
	be used to create the condition names.

	Examples
	--------

	>>> from nipype.algorithms import modelgen
	>>> from nipype.interfaces.base import Bunch
	>>> s = modelgen.SpecifyModel()
	>>> s.inputs.input_units = 'secs'
	>>> s.inputs.functional_runs = ['functional2.nii', 'functional3.nii']
	>>> s.inputs.time_repetition = 6
	>>> s.inputs.high_pass_filter_cutoff = 128.
	>>> evs_run2 = Bunch(conditions=['cond1'], onsets=[[2, 50, 100, 180]], durations=[[1]])
	>>> evs_run3 = Bunch(conditions=['cond1'], onsets=[[30, 40, 100, 150]], durations=[[1]])
	>>> s.inputs.subject_info = [evs_run2, evs_run3]

	Using pmod:

	>>> evs_run2 = Bunch(conditions=['cond1', 'cond2'], onsets=[[2, 50], [100, 180]], \
durations=[[0], [0]], pmod=[Bunch(name=['amp'], poly=[2], param=[[1, 2]]), \
None])
	>>> evs_run3 = Bunch(conditions=['cond1', 'cond2'], onsets=[[20, 120], [80, 160]], \
durations=[[0], [0]], pmod=[Bunch(name=['amp'], poly=[2], param=[[1, 2]]), \
None])
	>>> s.inputs.subject_info = [evs_run2, evs_run3]

	"""
	input_spec = SpecifyModelInputSpec
	output_spec = SpecifyModelOutputSpec

	def _generate_standard_design(self,
								  infolist,
								  functional_runs=None,
								  realignment_parameters=None,
								  outliers=None):
		""" Generates a standard design matrix paradigm given information about
			each run
		"""
		sessinfo = []
		output_units = 'secs'
		if 'output_units' in self.inputs.traits():
			output_units = self.inputs.output_units

		for i, info in enumerate(infolist):
			sessinfo.insert(i, dict(cond=[]))
			if isdefined(self.inputs.high_pass_filter_cutoff):
				sessinfo[i]['hpf'] = \
					np.float(self.inputs.high_pass_filter_cutoff)

			if hasattr(info, 'conditions') and info.conditions is not None:
				for cid, cond in enumerate(info.conditions):
					sessinfo[i]['cond'].insert(cid, dict())
					sessinfo[i]['cond'][cid]['name'] = info.conditions[cid]
					scaled_onset = scale_timings(
						info.onsets[cid], self.inputs.input_units,
						output_units, self.inputs.time_repetition)
					sessinfo[i]['cond'][cid]['onset'] = scaled_onset
					scaled_duration = scale_timings(
						info.durations[cid], self.inputs.input_units,
						output_units, self.inputs.time_repetition)
					sessinfo[i]['cond'][cid]['duration'] = scaled_duration
					if hasattr(info, 'amplitudes') and info.amplitudes:
						sessinfo[i]['cond'][cid]['amplitudes'] = \
							info.amplitudes[cid]

					if hasattr(info, 'tmod') and info.tmod and \
							len(info.tmod) > cid:
						sessinfo[i]['cond'][cid]['tmod'] = info.tmod[cid]

					if hasattr(info, 'pmod') and info.pmod and \
							len(info.pmod) > cid:
						if info.pmod[cid]:
							sessinfo[i]['cond'][cid]['pmod'] = []
							for j, name in enumerate(info.pmod[cid].name):
								sessinfo[i]['cond'][cid]['pmod'].insert(j, {})
								sessinfo[i]['cond'][cid]['pmod'][j]['name'] = \
									name
								sessinfo[i]['cond'][cid]['pmod'][j]['poly'] = \
									info.pmod[cid].poly[j]
								sessinfo[i]['cond'][cid]['pmod'][j]['param'] = \
									info.pmod[cid].param[j]

			sessinfo[i]['regress'] = []
			if hasattr(info, 'regressors') and info.regressors is not None:
				for j, r in enumerate(info.regressors):
					sessinfo[i]['regress'].insert(j, dict(name='', val=[]))
					if hasattr(info, 'regressor_names') and \
							info.regressor_names is not None:
						sessinfo[i]['regress'][j]['name'] = \
							info.regressor_names[j]
					else:
						sessinfo[i]['regress'][j]['name'] = 'UR%d' % (j + 1)
					sessinfo[i]['regress'][j]['val'] = info.regressors[j]
			sessinfo[i]['scans'] = functional_runs[i]

		if realignment_parameters is not None:
			for i, rp in enumerate(realignment_parameters):
				mc = realignment_parameters[i]
				for col in range(mc.shape[1]):
					colidx = len(sessinfo[i]['regress'])
					sessinfo[i]['regress'].insert(colidx, dict(
						name='', val=[]))
					sessinfo[i]['regress'][colidx]['name'] = 'Realign%d' % (
						col + 1)
					sessinfo[i]['regress'][colidx]['val'] = mc[:, col].tolist()

		if outliers is not None:
			for i, out in enumerate(outliers):
				numscans = 0
				for f in ensure_list(sessinfo[i]['scans']):
					shape = load(f, mmap=NUMPY_MMAP).shape
					if len(shape) == 3 or shape[3] == 1:
						iflogger.warning('You are using 3D instead of 4D '
										 'files. Are you sure this was '
										 'intended?')
						numscans += 1
					else:
						numscans += shape[3]

				for j, scanno in enumerate(out):
					colidx = len(sessinfo[i]['regress'])
					sessinfo[i]['regress'].insert(colidx, dict(
						name='', val=[]))
					sessinfo[i]['regress'][colidx]['name'] = 'Outlier%d' % (
						j + 1)
					sessinfo[i]['regress'][colidx]['val'] = \
						np.zeros((1, numscans))[0].tolist()
					sessinfo[i]['regress'][colidx]['val'][int(scanno)] = 1
		return sessinfo

	def _generate_design(self, infolist=None):
		"""Generate design specification for a typical fmri paradigm
		"""
		realignment_parameters = []
		if isdefined(self.inputs.realignment_parameters):
			for parfile in self.inputs.realignment_parameters:
				realignment_parameters.append(
					np.apply_along_axis(
						func1d=normalize_mc_params,
						axis=1,
						arr=np.loadtxt(parfile),
						source=self.inputs.parameter_source))
		outliers = []
		if isdefined(self.inputs.outlier_files):
			for filename in self.inputs.outlier_files:
				try:
					outindices = np.loadtxt(filename, dtype=int)
				except IOError:
					outliers.append([])
				else:
					if outindices.size == 1:
						outliers.append([outindices.tolist()])
					else:
						outliers.append(outindices.tolist())

		if infolist is None:
			if isdefined(self.inputs.subject_info):
				infolist = self.inputs.subject_info
			elif isdefined(self.inputs.event_files):
				infolist = gen_info(self.inputs.event_files)
			elif isdefined(self.inputs.bids_event_file):
				infolist = bids_gen_info(
					self.inputs.bids_event_file,
					self.inputs.bids_condition_column,
					self.inputs.bids_amplitude_column,
					self.inputs.time_repetition,
					)
		self._sessinfo = self._generate_standard_design(
			infolist,
			functional_runs=self.inputs.functional_runs,
			realignment_parameters=realignment_parameters,
			outliers=outliers)

	def _run_interface(self, runtime):
		"""
		"""
		self._sessioninfo = None
		self._generate_design()
		return runtime

	def _list_outputs(self):
		outputs = self._outputs().get()
		if not hasattr(self, '_sessinfo'):
			self._generate_design()
		outputs['session_info'] = self._sessinfo

		return outputs def select_from_datafind_df(df,
	bids_dictionary=False,
	bids_dictionary_override=False,
	output_key='path',
	failsafe=False,
	list_output=False,
	):
	"""
	Function that selects values from a Pandas DataFrame.
	This is useful in the context of nipype workflows, where the function needs to be encapsulated in a node.

	Parameters
	----------

	df : pandas.DataFrame
		Pandas DataFrame object with columns that are BIDS identifiers.
	bids_dictionary: dict
		Dictionary where its keys are BIDS identifiers.
	bids_dictionary_override: dict
		Dictionary where its keys are BIDS identifiers with override values.
	output_key: str
		A string that must be one of the column names of the Pandas DataFrame.
	failsafe : bool
		A boolean value where the function will either return a scalar value or fail.
	list_output : bool
		If True, the function will return a list of values corresponding to the output key.

	Returns
	-------

	list or scalar
	If list_output is True, the function will return a list of values corresponding to the output key.
	Otherwise, the function will return a scalar value corresponding to the first element of the Dataframe (if failsafe = True) or the output_key.
	"""
	if bids_dictionary_override:
		override = [i for i in bids_dictionary_override.keys()]
	else:
		override = []
	override.append(output_key)

	if bids_dictionary:
		for key in bids_dictionary:
			if key in override:
				continue
			elif isinstance(bids_dictionary[key], (float, int, str)):
				df=df[df[key]==bids_dictionary[key]]
			else:
				df=df[df[key].isin(bids_dictionary[key])]
	if bids_dictionary_override:
		for key in bids_dictionary_override:
			if bids_dictionary_override[key] != '':
				try:
					df=df[df[key]==bids_dictionary_override[key]]
				except (KeyError, TypeError):
					path_string = '{}-{}'.format(key, bids_dictionary_override[key])
					#print(path_string)
					#print(df.columns)
					#print(df)
					df=df[df['path'].str.contains(path_string)]

	if list_output:
		selection = df[output_key].tolist()
	else:
		if failsafe:
			df = df.iloc[0]
		selection = df[output_key].item()

	return selection def corresponding_eventfile(timecourse_file, as_list=False):
	"""Based on a BIDS timecourse path, get the corresponding BIDS eventfile."""

	from os import path

	timecourse_dir = path.dirname(timecourse_file)
	timecourse_name = path.basename(timecourse_file)
	stripped_name = timecourse_name.split('.', 1)[0].rsplit('_', 1)[0]
	eventfile = path.join(timecourse_dir,stripped_name+'_events.tsv')

	if as_list:
		return [eventfile,]
	else:
		return eventfile def get_bids_scan(data_selection,
	bids_base="",
	ind_type="",
	selector=None,
	subject=None,
	session=None,
	extra=['acq','run'],
	):

	"""Description...

	Parameters
	----------
	bids_base : str
		Path to the bids base path.
	data_selection : pandas.DataFrame
		A `pandas.DataFrame` object as produced by `samri.preprocessing.extra_functions.get_data_selection()`.
	selector : iterable, optional
		The first method of selecting the subject and scan, this value should be a length-2 list or tuple containing the subject and sthe session to be selected.
	subject : string, optional
		This has to be defined if `selector` is not defined. The subject for which to return a scan directory.
	session : string, optional
		This has to be defined if `selector` is not defined. The session for which to return a scan directory.
	"""
	import os #for some reason the import outside the function fails
	import pandas as pd
	from samri.pipelines.utils import bids_naming

	filtered_data = []

	if selector:
		subject = selector[0]
		session = selector[1]
		filtered_data = data_selection[(data_selection["session"] == session)&(data_selection["subject"] == subject)]
	else:
		filtered_data = data_selection[data_selection.index==ind_type]

	if filtered_data.empty:
		raise Exception("SAMRIError: Does not exist: " + str(selector[0]) + str(selector[1]) + str(ind_type))
	else:
		subject = filtered_data['subject'].item()
		session = filtered_data['session'].item()
		try:
			typ = filtered_data['type'].item()
		except:
			typ = ""
		try:
			task = filtered_data['task'].item()
		except:
			task = ""
		subject_session = [subject, session]
		#scan_path = os.path.join(bids_base, 'sub-' + subject + '/', 'ses-' + session + '/', typ )

		try:
			nii_path = filtered_data['path'].item()
		except KeyError:
			nii_path = filtered_data['measurement'].item()
			nii_path += '/'+filtered_data['scan'].item()
			nii_name = bids_naming(subject_session, filtered_data,
					extra=extra,
					extension='',
					)
			scan_path = nii_path
		else:
			scan_path = os.path.dirname(nii_path)
			nii_name = os.path.basename(nii_path)

		eventfile_name = bids_naming(subject_session, filtered_data,
				extra=extra,
				extension='.tsv',
				suffix='events'
				)
		metadata_filename = bids_naming(subject_session, filtered_data,
				extra=extra,
				extension='.json',
				)

		dict_slice = filtered_data.to_dict('records')[0]

		return scan_path, typ, task, nii_path, nii_name, eventfile_name, subject_session, metadata_filename, dict_slice, ind_type def physiofile_ts(in_file, column_name,
	save=True,
	):
	"""Based on a BIDS timecourse path and a physiological regressor name, get the corresponding timecourse."""

	from os import path
	import json
	import nibabel as nib

	img = nib.load(in_file)
	nii_dir = path.dirname(in_file)
	nii_name = path.basename(in_file)
	stripped_name = nii_name.split('.', 1)[0].rsplit('_', 1)[0]
	physiofile = path.join(nii_dir,stripped_name+'_physio.tsv')
	physiofile_meta = path.join(nii_dir,stripped_name+'_physio.json')

	with open(physiofile_meta, 'r') as json_file:
		metadata = json.load(json_file)
	columns = metadata['Columns']
	with open(physiofile, 'r') as f:
		physios = f.read().split('\n')
	physios = [i.split('\t') for i in physios if i != '']
	physios = [list(map(float, sublist)) for sublist in physios]
	physios = [list(i) for i in zip(*physios)]
	ts_index = columns.index(column_name)
	ts = physios[ts_index]

	duration_img = img.header['dim'][4]
	duration_physios = len(ts)

	if duration_physios > duration_img:
		ts = ts[:duration_img]
	elif duration_img > duration_physios:
		data = img.get_data()
		data = data[:,:,:,:duration_physios]
		img = nib.Nifti1Image(data, img.affine, img.header)

	if save:
		nib.save(img, nii_name)
		nii_file = path.abspath(path.expanduser(nii_name))
	elif isinstance(save, str):
		save = path.abspath(path.expanduser(save))
		nib_save(img, save)
		nii_file = save
	else:
		nii_file = img

	return nii_file, ts def eventfile_add_habituation(in_file,
	amplitude_column='',
	discriminant_column='samri_l1_regressors',
	original_stimulation_value='stim',
	habituation_value='habituation',
	out_file="events.tsv",
	):
	"""Add habituation events to be used as regressors in BIDS file.

	Parameters
	----------

	in_file : str
		Path to TSV file with columns including 'onset' (formatted e.g. according to the BIDS specification).
	out_file : str, optional
		Path to which to write the adjusted events file

	Returns
	-------

	str : Path to which the adjusted events file was saved.
	"""

	import pandas as pd
	from copy import deepcopy
	from os import path

	in_file = path.abspath(path.expanduser(in_file))
	out_file = path.abspath(path.expanduser(out_file))

	df = pd.read_csv(in_file, sep="\t")
	# We need to ascertain events are listed in progressive chronological order.
	df = df.sort_values(by=['onset'], ascending=True)

	df[discriminant_column] = ""
	df[discriminant_column] = original_stimulation_value
	df_ = deepcopy(df)
	df_[discriminant_column] = habituation_value
	total_events=len(df)
	habituation_amplitudes = [i for i in range(total_events)[::-1]]
	if not amplitude_column:
		amplitude_column='samri_l1_amplitude'
	df_[amplitude_column] = habituation_amplitudes
	df[amplitude_column] = [1]*total_events

	df = pd.concat([df,df_], sort=False)

	df.to_csv(out_file, sep=str('\t'), index=False)

	return out_file def regressor(timecourse,
	scan_path='',
	name='regressor',
	hpf=225,
	):
	"""
	Create a regressor dictionary appropriate for usage as a `nipype.interfaces.fsl.model.Level1Design()` input value for the `session_info` parameter.

	Parameters
	----------
	timecourse : list or numpy.ndarray
		List or NumPy array containing the extracted regressor timecourse.
	scan_path : str, opional
		Path to the prospective scan to be analyzed, should have a temporal (4th NIfTI) dimension equal to the length of the timecourse parameter.
	name : str, optional
		Name for the regressor.
	hpf : high-pass filter used for the model
	"""

	regressor = {}
	regressor['name'] = name
	regressor['val'] = timecourse
	regressors = [regressor]
	my_dict = {}
	my_dict['cond'] = []
	my_dict['hpf'] = hpf
	my_dict['regress'] = regressors
	my_dict['scans'] = scan_path

	output = [my_dict]
	return output def bids_dict_to_source(bids_dictionary, source_format):
	"""Generate a string from the source format with inserted corresponding values from the given dictionary.

	Parameters
	----------

	bids_dictionary : dict
		A dictionary with keys that are BIDS identifiers.
	source_format : str
		A Python format string with identifiers which are keys of the bids_dictionary parameter.
	"""
	from os import path

	source = source_format.format(**bids_dictionary)

	return source def copy_bids_files(bids_in, bids_out):
	"""Copy BIDS metadata files.

	Parameters
	----------

	bids_in : str
		Input BIDS data directory.
	out_out : str
		Output BIDS data directory.
	"""

	bids_file_list = [
		'participants.tsv',
		'participants.json',
		'dataset_description.json',
		]
	for i in bids_file_list:
		in_file = os.path.join(bids_in,i)
		out_file = os.path.join(bids_out,i)
		try:
			copyfile(in_file,out_file)
		except:
			print('Copying {} to {} failed.'.format(in_file,out_file))
			pass def ss_to_path(subject_session):
	"""Concatenate a (subject, session) or (subject, session, scan) tuple to a BIDS-style path"""
	subject = "sub-" + subject_session[0]
	session = "ses-" + subject_session[1]
	return "/".join([subject,session]) def iterfield_selector(iterfields, selector, action):
	"""Include or exclude entries from iterfields based on a selector dictionary

	Parameters
	----------

	iterfields : list
	A list of lists (or tuples) containing entries fromatted at (subject_id,session_id,task_id)

	selector : dict
	A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values.

	action : "exclude" or "include"
	Whether to exclude or include (and exclude all the other) matching entries from the output.
	"""
	name_map = {"subjects": 0, "sessions": 1, "tasks":2}
	keep = []
	for ix, iterfield in enumerate(iterfields):
		for key in selector:
			selector[key] = [str(i) for i in selector[key]]
			if iterfield[name_map[key]] in selector[key]:
				keep.append(ix)
				break
	if action == "exclude":
		iterfields = [iterfields[i] for i in range(len(iterfields)) if i not in keep]
	elif action == "include":
		iterfields = [iterfields[i] for i in keep]
	return iterfields def datasource_exclude(in_files, excludes, output="files"):
	"""Exclude file names from a list that match a BIDS-style specifications from a dictionary.

	Parameters
	----------

	in_files : list
	A list of flie names.

	excludes : dictionary
	A dictionary with keys which are "subjects", "sessions", or "scans", and values which are lists giving the subject, session, or scan identifier respectively.

	output : string
	Either "files" or "len". The former outputs the filtered file names, the latter the length of the resulting list.
	"""

	if not excludes:
		out_files = in_files
	else:
		exclude_criteria=[]
		for key in excludes:
			if key in "subjects":
				for i in excludes[key]:
					exclude_criteria.append("sub-"+str(i))
			if key in "sessions":
				for i in excludes[key]:
					exclude_criteria.append("ses-"+str(i))
			if key in "scans":
				for i in excludes[key]:
					exclude_criteria.append("task-"+str(i))
		out_files = [in_file for in_file in in_files if not any(criterion in in_file for criterion in exclude_criteria)]
	if output == "files":
		return out_files
	elif output == "len":
		return len(out_files) def bids_dict_to_dir(bids_dictionary):
	"""Concatenate a (subject, session) or (subject, session, scan) tuple to a BIDS-style path"""
	subject = "sub-" + bids_dictionary['subject']
	session = "ses-" + bids_dictionary['session']
	return "/".join([subject,session]) def ts(img_path,
	mask=False,
	substitution={},
	top_voxel='',
	):
	"""
	Return the mean and median of a Region of Interest (ROI) time course.

	Parameters
	----------

	img_path : str
		Path to NIfTI file from which the ROI is to be extracted.
	maks : nilearn.NiftiMasker or str, optional
		Nilearn `nifti1.Nifti1Image` object to use for masking the desired ROI, or string specifying the path of a mask file.
	substitution : dict, optional
		A dictionary with keys which include 'subject' and 'session'.
	top_voxel : str or list, optional
		Path to NIfTI file or files based on the within-mask top-value voxel of which to create a sub-mask for time course extraction.
		Note that this file *needs* to be in the exact same affine space as the mask file.
	"""
	# Imports are needed for usage as nipype nodes.
	import nibabel as nib
	import numpy as np
	from nilearn.input_data import NiftiMasker
	from os import path

	if substitution:
		img_path = img_path.format(**substitution)
		top_voxel= top_voxel.format(**substitution)
	img_path = path.abspath(path.expanduser(img_path))
	img = nib.load(img_path)
	if top_voxel:
		top_voxel_path = path.abspath(path.expanduser(top_voxel))
		top_img = nib.load(top_voxel_path)
		header = top_img.header
		affine = top_img.affine
		top_data = top_img.get_data()
		shape = top_data.shape
		top_data = top_data.flatten()
		try:
			mask_img = mask.mask_img_
		except:
			mask = path.abspath(path.expanduser(mask))
			mask_img = nib.load(mask)
		mask_data = mask_img.get_data()
		mask_data = mask_data.flatten()
		mask_data = mask_data.astype(bool)
		top_data[mask_data == False] = 0
		top_data_ = top_data[~np.isnan(top_data)]
		voxel = np.in1d(top_data,np.max(top_data_))
		voxel = voxel.astype(int)
		voxel = voxel.reshape(shape)
		voxel_mask_img = nib.Nifti1Image(voxel, affine, header)
		voxel_masker = NiftiMasker(mask_img=voxel_mask_img)
		masked_data = voxel_masker.fit_transform(img).T
	else:
		try:
			masked_data = mask.fit_transform(img).T
		except:
			mask = path.abspath(path.expanduser(mask))
			mask = NiftiMasker(mask_img=mask)
			masked_data = mask.fit_transform(img).T

	ts_means = np.mean(masked_data, axis=0)
	ts_medians = np.mean(masked_data, axis=0)
	return ts_means, ts_medians N_PROCS=max(mp.cpu_count()-2,2)
from os import path, listdir, getcwd, remove
from shutil import copyfile
from copy import deepcopy
from itertools import product
from nipype.interfaces import fsl
from nipype.interfaces.fsl.model import Level1Design
from samri.pipelines.extra_interfaces import SpecifyModel
from samri.pipelines.extra_functions import select_from_datafind_df, corresponding_eventfile, get_bids_scan, physiofile_ts, eventfile_add_habituation, regressor
from samri.pipelines.utils import bids_dict_to_source, copy_bids_files, ss_to_path, iterfield_selector, datasource_exclude, bids_dict_to_dir
from samri.report.roi import ts
from samri.utilities import N_PROCS
	from samri.pipelines.utils import bids_data_selection
		from bids.grabbids import BIDSLayout
		from bids.layout import BIDSLayout
	from samri.pipelines.utils import bids_data_selection
		from bids.grabbids import BIDSLayout
		from bids.layout import BIDSLayout
	from samri.pipelines.utils import bids_data_selection
		from samri.report.roi import erode
		from bids.grabbids import BIDSLayout
		from bids.layout import BIDSLayout
	from samri.pipelines.utils import bids_data_selection
		from samri.pipelines.extra_functions import extract_volumes
	from samri.pipelines.utils import bids_data_selection
import inspect
import pandas as pd
import re
import shutil
import multiprocessing as mp
import nipype.interfaces.io as nio
import nipype.interfaces.utility as util
import nipype.pipeline.engine as pe
import os
		If selected, this overrides the `convolution` option and sets it to "custom".
	convolution : str or dict, optional
		Select convolution method.
	exclude : dict
		A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values.
		If this is specified matching entries will be excluded in the analysis.
	debug : bool, optional
		Whether to enable nipype debug mode.
		This increases logging.
	habituation : {"", "confound", "separate_contrast", "in_main_contrast"}, optional
		How the habituation regressor should be handled.
		Anything which evaluates as False (though we recommend "") means no habituation regressor will be introduced.
	highpass_sigma : int, optional
		Highpass threshold (in seconds).
	include : dict
		A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values.
		If this is specified only matching entries will be included in the analysis.
	invert : bool
		If true the values will be inverted with respect to zero.
		This is commonly used for iron nano-particle Cerebral Blood Volume (CBV) measurements.
	keep_work : bool, optional
		Whether to keep the work directory (containing all the intermediary workflow steps, as managed by nipypye).
		This is useful for debugging and quality control.
	out_base : str, optional
		Path to the directory inside which both the working directory and the output directory will be created.
	mask : str, optional
		Path to the brain mask which shall be used to define the brain volume in the analysis.
		This has to point to an existing NIfTI file containing zero and one values only.
	n_jobs_percentage : float, optional
		Percentage of the cores present on the machine which to maximally use for deploying jobs in parallel.
	temporal_derivatives : int, optional
		Whether to add temporal derivatives of the main regressors in the model. This only applies if the convolution parameter is set to 'dgamma' or 'gamma'.
	tr : int, optional
		Repetition time, in seconds.
	workflow_name : str, optional
		Name of the workflow; this will also be the name of the final output directory produced under `out_dir`.
	"""


	preprocessing_dir = path.abspath(path.expanduser(preprocessing_dir))
	out_base = path.abspath(path.expanduser(out_base))

	data_selection = bids_data_selection(preprocessing_dir, structural_match=False, functional_match=match, subjects=False, sessions=False)
	if exclude:
		for key in exclude:
			data_selection = data_selection[~data_selection[key].isin(exclude[key])]
	ind = data_selection.index.tolist()

	out_dir = path.join(out_base,workflow_name)
	workdir_name = workflow_name+'_work'
	workdir = path.join(out_base,workdir_name)
	if not os.path.exists(workdir):
		os.makedirs(workdir)
	data_selection.to_csv(path.join(workdir,'data_selection.csv'))

	get_scan = pe.Node(name='get_scan', interface=util.Function(function=get_bids_scan,input_names=inspect.getargspec(get_bids_scan)[0], output_names=['scan_path','scan_type','task', 'nii_path', 'nii_name', 'events_name', 'subject_session', 'metadata_filename', 'dict_slice', 'ind_type']))
	get_scan.inputs.ignore_exception = True
	get_scan.inputs.data_selection = data_selection
	get_scan.inputs.bids_base = preprocessing_dir
	get_scan.iterables = ("ind_type", ind)

	eventfile = pe.Node(name='eventfile', interface=util.Function(function=corresponding_eventfile,input_names=inspect.getargspec(corresponding_eventfile)[0], output_names=['eventfile']))

	if invert:
		invert = pe.Node(interface=fsl.ImageMaths(), name="invert")
		invert.inputs.op_string = '-mul -1'

	specify_model = pe.Node(interface=SpecifyModel(), name="specify_model")
	specify_model.inputs.input_units = 'secs'
	specify_model.inputs.time_repetition = tr
	specify_model.inputs.high_pass_filter_cutoff = highpass_sigma

	level1design = pe.Node(interface=Level1Design(), name="level1design")
	level1design.inputs.interscan_interval = tr
	if bf_path:
		convolution = 'custom'
	if convolution == 'custom':
		bf_path = path.abspath(path.expanduser(bf_path))
		level1design.inputs.bases = {"custom": {"bfcustompath":bf_path}}
	elif convolution == 'gamma':
		# We are not adding derivatives here, as these conflict with the habituation option.
		# !!! This is not difficult to solve, and would only require the addition of an elif condition to the habituator definition, which would add multiple column copies for each of the derivs.
		level1design.inputs.bases = {'gamma': {'derivs':temporal_derivatives, 'gammasigma':30, 'gammadelay':10}}
	elif convolution == 'dgamma':
		# We are not adding derivatives here, as these conflict with the habituation option.
		# !!! This is not difficult to solve, and would only require the addition of an elif condition to the habituator definition, which would add multiple column copies for each of the derivs.
		level1design.inputs.bases = {'dgamma': {'derivs':temporal_derivatives,}}
	elif isinstance(convolution, dict):
		level1design.inputs.bases = convolution
	else:
		raise ValueError('You have specified an invalid value for the "convoltion" parameter of.')
	level1design.inputs.model_serial_correlations = True

	modelgen = pe.Node(interface=fsl.FEATModel(), name='modelgen')

	glm = pe.Node(interface=fsl.GLM(), name='glm', iterfield='design')
	if mask == 'mouse':
		mask = '/usr/share/mouse-brain-templates/dsurqec_200micron_mask.nii'
		glm.inputs.mask = path.abspath(path.expanduser(mask))
	else:
		glm.inputs.mask = path.abspath(path.expanduser(mask))
	glm.interface.mem_gb = 6

	try:
	except ModuleNotFoundError:
		out_file_name_base = 'sub-{{subject}}_ses-{{session}}_task-{{task}}_acq-{{acquisition}}_run-{{run}}_desc-{}_{{suffix}}.{}'
	else:
		out_file_name_base = 'sub-{{subject}}_ses-{{session}}_task-{{task}}_acq-{{acquisition}}_run-{{run}}_desc-{}_{{modality}}.{}'


	betas_filename = pe.Node(name='betas_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	betas_filename.inputs.source_format = out_file_name_base.format('betas','nii.gz')
	cope_filename = pe.Node(name='cope_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	cope_filename.inputs.source_format = out_file_name_base.format('cope','nii.gz')
	varcb_filename = pe.Node(name='varcb_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	varcb_filename.inputs.source_format = out_file_name_base.format('varcb','nii.gz')
	tstat_filename = pe.Node(name='tstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	tstat_filename.inputs.source_format = out_file_name_base.format('tstat','nii.gz')
	zstat_filename = pe.Node(name='zstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))
	zstat_filename.inputs.source_format = out_file_name_base.format('zstat','nii.gz')

	pstat_filename = pe.Node(name='pstat_filename', interface=util.Function(function=bids_dict_to_source,input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename']))