import os
import numpy
from taskinit import *

def wvrgcal(vis=None, caltable=None, toffset=None, segsource=None,
	    sourceflag=None, tie=None, nsol=None, disperse=None, 
	    wvrflag=None, statfield=None, statsource=None, smooth=None,
	    scale=None, spw=None, wvrspw=None,
	    reversespw=None,  cont=None, maxdistm=None,
	    minnumants=None, mingoodfrac=None, usefieldtab=None, 
	    refant=None, offsetstable=None):
	"""
	Generate a gain table based on Water Vapour Radiometer data.
	Returns a dictionary containing the RMS of the path length variation
	with time towards that antenna (RMS) and the discrepency between the RMS
	path length calculated separately for different WVR channels (Disc.).

        															
	  vis -- Name of input visibility file											
	              default: none; example: vis='ngc5921.ms'									
																
	  caltable -- Name of output gain calibration table									
	              default: none; example: caltable='ngc5921.wvr'								
																
	  toffset -- Time offset (sec) between interferometric and WVR data							
	             default: 0 (ALMA default for cycle 1, for cycle 0 it was -1)											
																
	  segsource -- Do a new coefficient calculation for each source								
	             default: True												
																
	  tie -- Prioritise tieing the phase of these sources as well as possible						
	         (requires segsource=True)											
	             default: [] example: ['3C273,NGC253', 'IC433,3C279']							
																
	  sourceflag -- Flag the WVR data for these source(s) as bad and do not produce corrections for it			
	               (requires segsource=True)										
	               default: [] (none) example: ['3C273']									
																
	  nsol -- Number of solutions for phase correction coefficients during this observation.				
	          By default only one set of coefficients is generated for the entire observation. 				
	          If more sets are requested, then they will be evenly distributed in time throughout 				
	          the observation. Values > 1 require segsource=False.								
	             default: 1													
																
	  disperse -- Apply correction for dispersion										
	             default: False												
																
	  wvrflag -- Regard the WVR data for these antenna(s) as bad and use interpolated values instead		
	               default: [] (none) example: ['DV03','DA05','PM02']           						
																
	  statfield -- Compute the statistics (Phase RMS, Disc) on this field only						
	               default: '' (all) 											
																
	  statsource -- Compute the statistics (Phase RMS, Disc) on this source only						
	               default: '' (all)             										
																
	  smooth -- Smooth the calibration solution on the given timescale 
	             default: '' (no smoothing), example: '3s' smooth on a timescale of 3 seconds
		     
	  scale -- Scale the entire phase correction by this factor								
	             default: 1. (no scaling)											

          spw -- List of the spectral window IDs for which solutions should be saved into the caltable
	             default: [] (all spectral windows), example [17,19,21,23]

          wvrspw -- List of the spectral window IDs from which the WVR data should be taken
	             default: [] (all WVR spectral windows), example [0]
																
	  reversespw -- Reverse the sign of the correction for the listed SPWs							
	                (only needed for early ALMA data before Cycle 0)								
	             default: '' (none), example: reversespw='0~2,4'; spectral windows 0,1,2,4					
																
	  cont -- Estimate the continuum (e.g., due to clouds)									
                     default: False

          maxdistm -- maximum distance (m) an antenna may have to be considered for being part
	              of the <=3 antenna set for interpolation of a solution for a flagged antenna
		      default: 500

          minnumants -- minimum number of near antennas required for interpolation
	                default: 2

          mingoodfrac -- If the fraction of unflagged data for an antenna is below this value (0. to 1.),
	                 the antenna is flagged.
			 default: 0.8

          usefieldtab -- derive the antenna AZ/EL values from the FIELD rather than the POINTING table
	                 default: False

	  refant -- use the WVR data from this antenna for calculating the dT/dL parameters (can give ranked list)
	              default: '' (use the first good or interpolatable antenna), 
                      examples: 'DA45' - use DA45 
                                ['DA45','DV51'] - use DA45 and if that is not good, use DV51 instead

	  offsetstable -- subtract the temperature offsets in this table from the WVR measurements before
	             using them to calculate the phase corrections
		     default: '' (do not apply any offsets)
		     examples: 'uid___A002_Xabd867_X2277.cloud_offsets' use the given table

        """
	#Python script

	# make ms tool local 
	mst = casac.ms()

	try:
		casalog.origin('wvrgcal')

		if not (type(vis)==str) or not (os.path.exists(vis)):
			raise Exception, 'Visibility data set not found - please verify the name'
		
		if (caltable == ""):
			raise Exception, "Must provide output calibration table name in parameter caltable."            
		
		if os.path.exists(caltable):
			raise Exception, "Output caltable %s already exists - will not overwrite." % caltable

		execute_string=  '--ms ' + vis

		smoothing = -1
		if (type(smooth)==str and smooth!=''):
			smoothing = qa.convert(qa.quantity(smooth), 's')['value']
			execute_string+= ' --output ' + caltable + '_unsmoothed'
		else:
			execute_string+= ' --output ' + caltable

		
		execute_string+= ' --toffset ' + str(toffset)

		if nsol>1:
			if not segsource:
				execute_string+= ' --nsol ' + str(nsol)
			else:
				raise Exception, "In order to use nsol>1, segsource must be set to False." % caltable
		if segsource:
			execute_string+= ' --segsource'

		if segsource and (len(sourceflag)>0):
			for src in sourceflag:
				if not (type(src)==int or type(src)==str):
					raise Exception, "List elements of parameter sourceflag must be int or string."
				if (src != ''):
					execute_string += ' --sourceflag \"'+str(src)+'\"'

		if segsource and (len(tie)>0):
			for i in xrange(0,len(tie)):
				src = tie[i]
				if not (type(src)==str):
					raise Exception, "List elements of parameter tie must be strings."
				if (src != ''):
					execute_string += ' --tie '
					execute_string += '\"'+str(src)+'\"'
					if not (i==len(tie)-1):
						execute_string += ' '
						
		if (len(spw)>0):
			for myspw in spw:
				if not (type(myspw)==int):
					raise Exception, "List elements of parameter spw must be int."
				if (myspw>=0):
					execute_string += ' --spw '+str(myspw)

		if (len(wvrspw)>0):
			for myspw in wvrspw:
				if not (type(myspw)==int):
					raise Exception, "List elements of parameter wvrspw must be int."
				if (myspw>=0):
					execute_string += ' --wvrspw '+str(myspw)
			

		if not (reversespw==''):
			spws = mst.msseltoindex(vis=vis,spw=reversespw)['spw']
			for id in spws:
				execute_string += ' --reversespw '+str(id)

		if disperse:
			dispdirpath = os.getenv('WVRGCAL_DISPDIR', '')
			if not os.path.exists(dispdirpath+'/libair-ddefault.csv'):
				path1 = dispdirpath
				dispdirpath = os.getenv("CASAPATH").split(' ')[0] + "/data/alma/wvrgcal"
				if not os.path.exists(dispdirpath+'/libair-ddefault.csv'):
					raise Exception, "Dispersion table libair-ddefault.csv not found in path "\
					      +"given by WVRGCAL_DISPDIR nor in \""+dispdirpath+"\""
				
				os.putenv('WVRGCAL_DISPDIR', dispdirpath)
				
			execute_string+= ' --disperse'
			casalog.post('Using dispersion table '+dispdirpath+'/libair-ddefault.csv')

		if cont:
			if not segsource:
				execute_string+= ' --cont'
			else:
				raise Exception, "cont and segsource are not permitted to be True at the same time."

		if usefieldtab:
			execute_string+= ' --usefieldtab'
				
		if offsetstable!='' and type(offsetstable)==str:
			execute_string+= ' --offsets '+offsetstable

		if (len(wvrflag)>0):
                        theflagants = ""
			for ant in wvrflag:
				if not (type(ant)==int or type(ant)==str):
					raise Exception, "List elements of parameter wvrflag must be int or string."
				if (ant != ''):
                                        if len(theflagants)>0:
                                                theflagants += ","
                                        theflagants += str(ant)
                        if len(theflagants)>0:
                                execute_string += ' --wvrflag \"'+str(theflagants)+'\"'

		if (type(refant)!=list):
			refant = [refant]
		if (len(refant)>0):
                        therefants = ""
                        for ant in refant:
				if not (type(ant)==int or type(ant)==str):
					raise Exception, "Parameter refant must be int or string or a list of them."
				if (ant != ''):
                                        if len(therefants)>0:
                                                therefants += ","
                                        therefants += str(ant)
                        if len(therefants)>0:
                                execute_string += ' --refant \"'+str(therefants)+'\"'

		if not (statfield==None or statfield=="") and type(statfield)==str:
			execute_string += ' --statfield \"'+ statfield + '\"'

		if not (statsource==None or statsource=="") and type(statsource)==str:
			execute_string += ' --statsource \"'+ statsource + '\"'

		if (scale != 1.):
			execute_string+= ' --scale ' + str(scale)
		
		if (maxdistm>=0.):
			execute_string+= ' --maxdistm ' + str(maxdistm)
		
		if (minnumants>=0):
			execute_string+= ' --minnumants ' + str(minnumants)
		
		if (0.<=mingoodfrac and mingoodfrac<=1.):
			execute_string+= ' --mingoodfrac ' + str(mingoodfrac)

		theexecutable = 'wvrgcal'

		execute_string = theexecutable+' '+execute_string

		casalog.post('Running '+theexecutable+' standalone invoked as:')
		casalog.post(execute_string)
		print execute_string

		templogfile = 'wvrgcal_tmp_'+str(numpy.random.randint(1E6,1E8))
		if not os.access(".", os.W_OK):
			import tempfile
			templogfile = tempfile.gettempdir()+"/"+templogfile
			
        	rval = os.system(execute_string + " > "+ templogfile)

		fp = file(templogfile)
		loglines = fp.readlines()
		fp.close()

		# prepare variables for parsing log lines to extract info table
		hfound = False
		hend = False
		namel = []
		wvrl = []
		flagl = []
		rmsl = []
		discl = []
		parsingok = True
		
		for ll in loglines:
			casalog.post(ll.expandtabs())
			if hfound:
				if "Expected performance" in ll:
					hend = True
				elif not hend:
					vals = ll.split()
					wvrv = False
					flagv = False
					rmsv = 0.
					discv = 0.
					if(len(vals)!=6):
						casalog.post('Error parsing wvrgcal info table.line: '+ll,'WARN')
						parsingok=False
					else:
						if vals[2]=='Yes':
							wvrv=True
						else:
							wvrv=False
						if vals[3]=='Yes':
							flagv=True
						else:
							flagv=False
					try:
						rmsv = float(vals[4])
					except:
						casalog.post('Error parsing RMS value in info table line: '+ll,'WARN')
						rmsv = -1.
						parsingok=False
					try:
						discv = float(vals[5])
					except:
						casalog.post('Error parsing Disc. value in info table line: '+ll,'WARN')
						discv = -1.
						parsingok=False

					namel.append(vals[1])
					wvrl.append(wvrv)
					flagl.append(flagv)
					rmsl.append(rmsv)
					discl.append(discv)

					
			elif (rval==0) and (not hend) and ("Disc (um)" in ll):
				hfound = True

		# end for ll

		os.system('rm -rf '+templogfile)


		taskrval = { 'Name': namel,
			     'WVR': wvrl,
			     'Flag': flagl,
			     'RMS_um': rmsl,
			     'Disc_um': discl,
			     'rval': rval,
			     'success': False}

		for k in range(len(namel)):
			if(flagl[k] and rmsl[k]==0. and discl[k]==0.):
				casalog.post('Solution for flagged antenna '+namel[k]
					     +' could not be interpolated due to insufficient number of near antennas. Was set to unity.',
					     'WARN')

		if (rval==0) and parsingok:
			taskrval['success'] = True
		

		if(rval == 0):
			if (smoothing>0):
				mycb = cbtool()
				mycb.open(filename=vis, compress=False, addcorr=False, addmodel=False)
				mycb.smooth(tablein=caltable+'_unsmoothed', tableout=caltable,
					    smoothtype='mean', smoothtime=smoothing)
				mycb.close()
			return taskrval
		else:
			rcode = os.WEXITSTATUS(rval)
			if(rcode == 127):
				raise Exception, "wvrgcal executable not available."
			elif(rcode == 255):
				casalog.post(theexecutable+' terminated with exit status '+str(rcode),'SEVERE')
				return taskrval
			elif(rcode == 134 or rval==1):
				casalog.post(theexecutable+' terminated with exit status '+str(rcode),'WARN')
				casalog.post("No useful input data.",'SEVERE')
				return taskrval
			else:
				casalog.post(theexecutable+' terminated with exit status '+str(rcode),'WARN')
				return taskrval
	
	except Exception, instance:
		print '*** Error *** ',instance
		raise Exception, instance