import sys
from launch_multiple_simulations import Launcher

try:
	metafolder = str(sys.argv[1])
	fitparfile = str(sys.argv[2])
	genparfile = str(sys.argv[3])
except IndexError as e:
	metafolder = 'simulations'
	fitparfile = 'parameter_ranges.txt'
	genparfile = 'general_parameters.txt'
	print('no arguments provided, creating in folder "simulations"')
	with open(fitparfile, 'w', buffering=1) as f1, \
	open(genparfile, 'w', buffering=1) as f2:
		f1.writelines(['fun,pgg\n','fb,2\n','b,0.5,0.7,0.1\n','c,0.05,0.07,0.01\n','gamma,0.01\n'])
		f2.writelines('ndemes,10\n','demesize,20\n','ngen,20\n','baseres,1\n','phen,[0.0]*4\n','tech,1\n','mutrate,0.01\n','mutstep,0.02\n','migrate,0.5\n')

l = Launcher(metafolder=metafolder, parfile=fitparfile, launchfile=genparfile)
l.launch()
class TestAutomaticWorkflow(object):

	def test_single_sim_reads_and_writes_from_same_folder(self):
		dirpath = Path('simulations')
		if dirpath.exists() and dirpath.is_dir():
			shutil.rmtree(dirpath)

		shutil.copytree('test/test', 'simulations')
		self.pop = Pop(inst='simulations')
		self.pop.numberOfGenerations = 3
		self.pop.runSimulation()

		self.outputfiles = ["out_consensus.txt", "out_demography.txt", "out_phenotypes.txt", "out_resources.txt", "out_technology.txt"] 

		for file in self.outputfiles:
			assert file in os.listdir('simulations')

		shutil.rmtree('simulations')

	def test_script_reads_arguments_properly(self):
		self.l = Launcher("tralala", "blablabla")

		assert self.l.metafolder == "tralala"
		assert self.l.parfile == "blablabla"

	def test_launcher_can_read_general_parameter_file(self):
		self.l = Launcher(metafolder="tralala", parfile="blablabla", launchfile="test/test/general_parameters.txt")

		assert self.l.strINITFILE == "numberOfDemes,10\ninitialDemeSize,20\nnumberOfGenerations,20\nindividualBaseResources,1"
		assert self.l.strPHENFILE == "0.0\n0.0\n0.0\n0.0"
		assert self.l.strTECHFILE == "1"
		assert self.l.strPARAFILE == "mutationRate,0.01\nmutationStep,0.02\nmigrationRate,0.5"

	def test_script_creates_metafolder(self):
		self.l = Launcher("simulations", "blablabla")
		self.l.createFolder(self.l.metafolder)

		assert os.path.exists('simulations') == 1, "did not create metafolder"
		shutil.rmtree('simulations')

	def test_script_writes_par_files(self):
		self.l = Launcher("simulations", "blablabla")
		self.l.createFolder(self.l.metafolder)
		self.l.writeParameterFilesInFolder(fitfun="func", pname=["first","secnd","third"], pval=[1,2,3])

		self.dirpath = os.getcwd()
		self.fileslist = os.listdir('simulations/func_first1secnd2third3')
		self.inputfiles = [INITIALISATION_FILE, INITIAL_PHENOTYPES_FILE, INITIAL_TECHNOLOGY_FILE, PARAMETER_FILE, FITNESS_PARAMETERS_FILE]

		try:
			for file in self.inputfiles:
				assert file in self.fileslist
			shutil.rmtree('simulations')
		except AssertionError as e:
			shutil.rmtree('simulations')
			assert False, "one or more parameter file(s) missing. Folder contents: {0}".format(self.fileslist)

	def test_script_handles_files_already_exists_issue(self):
		shutil.copytree('test/test', 'simulations')
		self.l = Launcher('simulations', 'blablabla')
		self.l.writeParameterFilesInFolder(fitfun="func", pname=["first","secnd","third"], pval=[1,2,3])
		try:
			with open('simulations/func_first1secnd2third3/'+FITNESS_PARAMETERS_FILE, 'r') as f:
				assert len(f.readlines()) == 3, "file not replaced by correct parameter values"
			shutil.rmtree('simulations')
		except AssertionError as e:
			shutil.rmtree('simulations')
			assert False, "file not replaced by correct parameter values"

	def test_script_reads_parameter_ranges_file(self, createParameterRangesFile):
		# SIMPLE, NO RANGES

		createParameterRangesFile()
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.readParameterInfo()

		#assert False, "names:{0},start:{1},end:{2},step:{3},fit:{4}".format(self.l.parname,self.l.parstart,self.l.parend,self.l.parstep,self.l.fitnessFunction)

		assert self.l.parname == ["first", "secnd", "third"]
		self.l.parstart == [1,2,3]
		self.l.parend == [None] * 3
		self.l.parstep == [None] * 3
		assert self.l.fitnessFunction == 'pgg', self.l.lastLine

		# WITH RANGES
		createParameterRangesFile(multi=True)
		#self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.readParameterInfo()

		assert self.l.parname == ["first", "secnd", "third"]
		self.l.parstart == [1.1,2.3,3.4]
		self.l.parend == [1.3,None,3.6]
		self.l.parstep == [0.1,None,0.1]
		assert self.l.fitnessFunction == 'pgg'

		os.remove('parameter_ranges.txt')

	def test_ranges_creation(self, createParameterRangesFile):
		createParameterRangesFile(multi=True)
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.readParameterInfo()
		assert self.l.parend == ['1.3',None,'3.6']
		assert self.l.parstep == ['0.1', None, '0.1']

		self.l.createRanges()

		assert self.l.parend == ['1.3',None,'3.6']
		assert self.l.parstep == ['0.1', None, '0.1']

		assert len(self.l.ranges) == 3, "wrong number of ranges"
		checkList = [[1.1,1.2],[2.3],[3.4,3.5]]
		for par in range(len(self.l.ranges)):
			assert pytest.approx(self.l.ranges[par]) == checkList[par], "wrong range for {0}: {1} when it should be {2}".format(self.l.parname[par],self.l.ranges[par],checkList[par])

		os.remove('parameter_ranges.txt')

	def test_combinations_creation(self, createParameterRangesFile):
		createParameterRangesFile(multi=True)
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.readParameterInfo()
		self.l.createRanges()
		self.l.createCombinations()

		allCombs = [(1.1,2.3,3.4),(1.1,2.3,3.5),(1.2,2.3,3.4),(1.2,2.3,3.5)]
		for parcomb in allCombs:
			assert pytest.approx(parcomb) in self.l.combinations

		os.remove('parameter_ranges.txt')

	def test_single_par_combination_gets_a_full_folder(self):
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.createFolder(self.l.metafolder)
		self.l.writeParameterFilesInFolder(fitfun='pgg', pname=('small','big'), pval=(0.3,0.5))

		self.subfoldername = 'pgg_small0.3big0.5'
		assert os.path.exists('simulations/'+self.subfoldername), "create subfolder"
		self.fileslist = os.listdir('simulations/'+self.subfoldername)
		self.inputfiles = [INITIALISATION_FILE, INITIAL_PHENOTYPES_FILE, INITIAL_TECHNOLOGY_FILE, PARAMETER_FILE, FITNESS_PARAMETERS_FILE]
		try:
			for file in self.inputfiles:
				assert file in self.fileslist
			pars = fman.extractColumnFromFile('simulations/'+self.subfoldername+'/'+FITNESS_PARAMETERS_FILE, 0, str)
			vals = fman.extractColumnFromFile('simulations/'+self.subfoldername+'/'+FITNESS_PARAMETERS_FILE, 1, float)
			assert pars == ['small','big'], "wrong parameter name"
			assert vals == [0.3,0.5], "wrong parameter value"
			shutil.rmtree('simulations')
		except AssertionError as e:
			shutil.rmtree('simulations')
			assert False, "one or more parameter value(s) missing. File contents: {0},{1}".format(pars,vals) 

	def test_script_reads_parameter_ranges_file_and_writes_files_correctly_in_different_folders(self, createParameterRangesFile):
		createParameterRangesFile(multi=True)
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.writeParameterFilesInFolders()

		self.subfoldernames = ['pgg_first1.1secnd2.3third3.4','pgg_first1.1secnd2.3third3.5','pgg_first1.2secnd2.3third3.4','pgg_first1.2secnd2.3third3.5']
		self.parvalues = [[1.1,2.3,3.4],[1.1,2.3,3.5],[1.2,2.3,3.4],[1.2,2.3,3.5]]
		self.inputfiles = [INITIALISATION_FILE, INITIAL_PHENOTYPES_FILE, INITIAL_TECHNOLOGY_FILE, PARAMETER_FILE, FITNESS_PARAMETERS_FILE]

		for comb in range(len(self.parvalues)):
			folder = self.subfoldernames[comb]
			assert os.path.exists('simulations/'+folder), "did not create specific simulation file: {0}".format(os.listdir('simulations'))
			fileslist = os.listdir('simulations/'+folder)
			try:
				for file in self.inputfiles:
					assert file in fileslist
				pars = fman.extractColumnFromFile('simulations/'+folder+'/'+FITNESS_PARAMETERS_FILE, 0, str)
				vals = fman.extractColumnFromFile('simulations/'+folder+'/'+FITNESS_PARAMETERS_FILE, 1, float)
				assert pars == ['first','secnd','third'], "wrong parameter name"
				assert pytest.approx(vals) == self.parvalues[comb], "wrong parameter value"
			except AssertionError as e:
				shutil.rmtree('simulations')
				os.remove('parameter_ranges.txt')
				assert False, "one or more parameter value(s) missing. File contents: {0},{1}".format(pars,vals) 
		shutil.rmtree('simulations')
		os.remove('parameter_ranges.txt')

	def test_parameter_files_are_not_empty(self, createParameterRangesFile):
		createParameterRangesFile()
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.writeParameterFilesInFolders()

		self.fileslist = os.listdir('simulations')
		try:
			for file in self.fileslist:
				assert os.path.getsize('simulations/'+file) != 0, "file is empty"
			shutil.rmtree('simulations')
			os.remove('parameter_ranges.txt')
		except AssertionError as e:
			shutil.rmtree('simulations')
			os.remove('parameter_ranges.txt')
			assert False, "file is empty"

	def test_script_can_take_parameter_ranges(self, createParameterRangesFile):
		createParameterRangesFile(multi=True)
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		self.l.writeParameterFilesInFolders()

		files = folders = 0

		for _, dirnames, filenames in os.walk('simulations'):
		  # ^ this idiom means "we won't be using this value"
		    files += len(filenames)
		    folders += len(dirnames)

		shutil.rmtree('simulations')
		os.remove('parameter_ranges.txt')

		assert folders == 4, "wrong number of subfolders"
		assert files == 5*4, "wrong total number of parameters files"

	def test_script_can_launch_single_simulation(self):
		self.dir = 'simulations/pgg_test01'
		shutil.copytree('test/test', self.dir)
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		sim = self.l.launchSimulation(path=self.dir)

		assert os.path.exists(self.dir)
		self.fileslist = os.listdir(self.dir)
		self.outputfiles = ['out_phenotypes.txt', 'out_demography.txt', 'out_technology.txt', 'out_resources.txt', 'out_consensus.txt']

		try:
			for file in self.outputfiles:
				assert file in self.fileslist, "file {0} missing from output".format(file)
			shutil.rmtree('simulations')
		except AssertionError as e:
			shutil.rmtree('simulations')
			assert False, "file {0} missing from output".format(file)

	def test_single_simulation_output_not_empty(self):
		self.dir = 'simulations/pgg_test02'
		shutil.copytree('test/test', self.dir)
		assert os.path.isdir(self.dir), "not a directory"
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		sim = self.l.launchSimulation(path=self.dir)

		self.outputfiles = ['out_phenotypes.txt', 'out_demography.txt', 'out_technology.txt', 'out_resources.txt', 'out_consensus.txt']

		for file in self.outputfiles:
			with open(self.dir+'/'+file) as f:
				lines = f.readlines()
				assert len(lines) == 10, "printed {0} generations instead of 10".format(len(lines))
				try:
					floatlines = [float(x.split(',')[0]) for x in lines]
				except AssertionError as e:
					shutil.rmtree('simulations')
					assert False, "{0} are not numbers".format(lines)
		shutil.rmtree('simulations')

	def test_script_can_launch_all_simulations(self):
		self.dirs = ['pgg_test1','pgg_test2','pgg_test3']
		os.mkdir('simulations')
		for fold in self.dirs:
			shutil.copytree('test/test', 'simulations/'+fold)
			assert fold in os.listdir('simulations')
			assert os.path.isdir('simulations/'+fold), "not a directory"

		self.l = Launcher('simulations', 'parameter_ranges.txt')
		sim = self.l.launchSimulations(path='simulations')

		self.outputfiles = ['out_phenotypes.txt', 'out_demography.txt', 'out_technology.txt', 'out_resources.txt', 'out_consensus.txt']

		for fold in self.dirs:
			for file in self.outputfiles:
				try:
					assert file in os.listdir('simulations/'+fold), "file {0} missing from output in folder {1}".format(file,fold)
				except AssertionError as e:
					shutil.rmtree('simulations')
					assert False, "file {0} missing from output in folder {1}".format(file,fold)

		shutil.rmtree('simulations')

	def test_full_workflow(self):
		with open("parameter_ranges.txt", 'w') as f:
			f.writelines(['fun,pgg\n','fb,2\n','b,0.5,0.7,0.1\n','c,0.05,0.07,0.01\n','gamma,0.01\n'])
		self.l = Launcher('simulations', 'parameter_ranges.txt')
		sims = self.l.launch()

		self.dirs = os.listdir('simulations')
		self.infiles = [INITIALISATION_FILE, INITIAL_PHENOTYPES_FILE, INITIAL_TECHNOLOGY_FILE, PARAMETER_FILE, FITNESS_PARAMETERS_FILE]
		self.outfiles = ['out_phenotypes.txt', 'out_demography.txt', 'out_technology.txt', 'out_resources.txt', 'out_consensus.txt']
		self.allfiles = self.infiles + self.outfiles

		for fold in self.dirs:
			for file in self.allfiles:
				try:
					assert file in os.listdir('simulations/'+fold), "file {0} missing from folder {1}".format(file,fold)
				except AssertionError as e:
					shutil.rmtree('simulations')
					os.remove('parameter_ranges.txt')
					assert False, "file {0} missing from output in folder {1}".format(file,fold)
		shutil.rmtree('simulations')
		os.remove('parameter_ranges.txt')