import socket
import math
import subprocess
from IssmConfig import IssmConfig
from EnumToString import EnumToString
from issmdir import *
from pairoptions import *
from issmssh import *
from issmscpin import *
from issmscpout import *
from MatlabFuncs import *

class generic(object):
	"""
	GENERIC cluster class definition
 
	   Usage:
	      cluster=generic('name','astrid','np',3);
	      cluster=generic('name',oshostname(),'np',3,'login','username');
	"""

	def __init__(self,*args):    # {{{

		self.name=''
		self.login=''
		self.np=1
		self.port=0
		self.interactive=1
		self.codepath=issmdir()+'/bin'
		self.executionpath=issmdir()+'/execution'
		self.valgrind=issmdir()+'/externalpackages/valgrind/install/bin/valgrind'
		self.valgrindlib=issmdir()+'/externalpackages/valgrind/install/lib/libmpidebug.so'
		self.valgrindsup=issmdir()+'/externalpackages/valgrind/issm.supp'

		#use provided options to change fields
		options=pairoptions(*args)

		#get name
		self.name=options.getfieldvalue('name',socket.gethostname().lower().split('.')[0])

		#initialize cluster using user settings if provided
		if os.path.exists(self.name+'_settings.py'):
			execfile(self.name+'_settings.py',globals())

		#OK get other fields
		self=options.AssignObjectFields(self)
	# }}}

	def __repr__(self):    # {{{
		#  display the object
		s ="class '%s' object '%s' = \n" % (type(self),'self')
		s+="    name: %s\n" % self.name
		s+="    login: %s\n" % self.login
		s+="    np: %i\n" % self.np
		s+="    port: %i\n" % self.port
		s+="    codepath: %s\n" % self.codepath
		s+="    executionpath: %s\n" % self.executionpath
		s+="    valgrind: %s\n" % self.valgrind
		s+="    valgrindlib: %s\n" % self.valgrindlib
		s+="    valgrindsup: %s\n" % self.valgrindsup
		return s
	# }}}

	def checkconsistency(self,md,solution,analyses):    # {{{
		if self.np<1:
			md = checkmessage(md,'number of processors should be at least 1')
		if math.isnan(self.np):
			md = checkmessage(md,'number of processors should not be NaN!')

		return md
	# }}}

	def BuildQueueScript(self,dirname,modelname,solution,io_gather,isvalgrind,isgprof):    # {{{

		#write queuing script 
		if not ispc():

			fid=open(modelname+'.queue','w')
			fid.write('#!/bin/sh\n')
			if not isvalgrind:
				if self.interactive: 
					if IssmConfig('_HAVE_MPI_')[0]:
						fid.write('mpiexec -np %i %s/issm.exe %s %s/%s %s ' % (self.np,self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname))
					else:
						fid.write('%s/issm.exe %s %s/%s %s ' % (self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname))
				else:
					if IssmConfig('_HAVE_MPI_')[0]:
						fid.write('mpiexec -np %i %s/issm.exe %s %s/%s %s 2> %s.errlog >%s.outlog ' % (self.np,self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname,modelname,modelname))
					else:
						fid.write('%s/issm.exe %s %s/%s %s 2> %s.errlog >%s.outlog ' % (self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname,modelname,modelname))
			elif isgprof:
				fid.write('\n gprof %s/issm.exe gmon.out > %s.performance' % (self.codepath,modelname))
			else:
				#Add --gen-suppressions=all to get suppression lines
				fid.write('LD_PRELOAD=%s \\\n' % self.valgrindlib)
				if IssmConfig('_HAVE_MPI_')[0]:
					fid.write('mpiexec -np %i %s --leak-check=full --suppressions=%s %s/issm.exe %s %s/%s %s 2> %s.errlog >%s.outlog ' % \
							(self.np,self.valgrind,self.valgrindsup,self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname,modelname,modelname))
				else:	
					fid.write('%s --leak-check=full --suppressions=%s %s/issm.exe %s %s/%s %s 2> %s.errlog >%s.outlog ' % \
							(self.valgrind,self.valgrindsup,self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname,modelname,modelname))

			if not io_gather:    #concatenate the output files:
				fid.write('\ncat %s.outbin.* > %s.outbin' % (modelname,modelname))
			fid.close()

		else:    # Windows

			fid=open(modelname+'.bat','w')
			fid.write('@echo off\n')
			if self.interactive:
				fid.write('"%s/issm.exe" %s "%s/%s" %s ' % (self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname))
			else:
				fid.write('"%s/issm.exe" %s "%s/%s" %s 2> %s.errlog >%s.outlog' % \
					(self.codepath,EnumToString(solution)[0],self.executionpath,dirname,modelname,modelname,modelname))
			fid.close()

		#in interactive mode, create a run file, and errlog and outlog file
		if self.interactive:
			fid=open(modelname+'.errlog','w')
			fid.close()
			fid=open(modelname+'.outlog','w')
			fid.close()
	# }}}

	def BuildKrigingQueueScript(self,modelname,solution,io_gather,isvalgrind,isgprof):    # {{{

		#write queuing script 
		if not ispc():

			fid=open(modelname+'.queue','w')
			fid.write('#!/bin/sh\n')
			if not isvalgrind:
				if self.interactive:
					fid.write('mpiexec -np %i %s/kriging.exe %s/%s %s ' % (self.np,self.codepath,self.executionpath,modelname,modelname))
				else:
					fid.write('mpiexec -np %i %s/kriging.exe %s/%s %s 2> %s.errlog >%s.outlog ' % (self.np,self.codepath,self.executionpath,modelname,modelname,modelname,modelname))
			elif isgprof:
				fid.write('\n gprof %s/kriging.exe gmon.out > %s.performance' & (self.codepath,modelname))
			else:
				#Add --gen-suppressions=all to get suppression lines
				fid.write('LD_PRELOAD=%s \\\n' % self.valgrindlib)
				fid.write('mpiexec -np %i %s --leak-check=full --suppressions=%s %s/kriging.exe %s/%s %s 2> %s.errlog >%s.outlog ' % \
					(self.np,self.valgrind,self.valgrindsup,self.codepath,self.executionpath,modelname,modelname,modelname,modelname))
			if not io_gather:    #concatenate the output files:
				fid.write('\ncat %s.outbin.* > %s.outbin' % (modelname,modelname))
			fid.close()

		else:    # Windows

			fid=open(modelname+'.bat','w')
			fid.write('@echo off\n')
			if self.interactive:
				fid.write('"%s/issm.exe" %s "%s/%s" %s ' % (self.codepath,EnumToString(solution)[0],self.executionpath,modelname,modelname))
			else:
				fid.write('"%s/issm.exe" %s "%s/%s" %s 2> %s.errlog >%s.outlog' % \
					(self.codepath,EnumToString(solution)[0],self.executionpath,modelname,modelname,modelname,modelname))
			fid.close()

		#in interactive mode, create a run file, and errlog and outlog file
		if self.interactive:
			fid=open(modelname+'.errlog','w')
			fid.close()
			fid=open(modelname+'.outlog','w')
			fid.close()
	# }}}

	def LaunchQueueJob(self,modelname,dirname,filelist):    # {{{

		#compress the files into one zip.
		compressstring='tar -zcf %s.tar.gz ' % dirname
		for file in filelist:
			compressstring += ' %s' % file
		if self.interactive:
			compressstring += ' %s.errlog %s.outlog ' % (modelname,modelname)
		subprocess.call(compressstring,shell=True)

		print 'uploading input file and queueing script'
		issmscpout(self.name,self.executionpath,self.login,self.port,[dirname+'.tar.gz'])

		print 'launching solution sequence on remote cluster'
		launchcommand='cd %s && rm -rf ./%s && mkdir %s && cd %s && mv ../%s.tar.gz ./ && tar -zxf %s.tar.gz  && chmod 777 %s.queue && ./%s.queue' % \
			(self.executionpath,dirname,dirname,dirname,dirname,dirname,modelname,modelname)
		issmssh(self.name,self.login,self.port,launchcommand)
	# }}}

	def Download(self,dirname,filelist):     # {{{

		if ispc():
			#do nothing
			return

		#copy files from cluster to current directory
		directory='%s/%s/' % (self.executionpath,dirname)
		issmscpin(self.name,self.login,self.port,directory,filelist)
	# }}}

