'''This file is part of AToMPM - A Tool for Multi-Paradigm Modelling
Copyright 2011 by the AToMPM team and licensed under the LGPL
See COPYING.lesser and README.md in the root of this project for full details'''

import sys
from .tconstants import TConstants as TC
from .dapi import DesignerAPI

try :
	import spidermonkey
except ImportError as ex :
	pass


''' 
	this class abstracts away the fact that designer code may be specified in 
	more than one programming language '''
class DesignerCodeAbstractionLayer :
	def __init__(self,username,aswid,mtwid) :
		self._dAPI 			 = DesignerAPI(username,aswid,mtwid)
		self._execContexts = {}
		self._execContext  = None


	'''
		configure this instance of DesignerCodeAbstractionLayer and its instance
		of DesignerAPI

		1. lazy load an ExecutionContext given the specified programming language
		2. configure DesignerAPI instance '''
	def configure(
			self,lang,graph,type,pl2gi,ex,pLabel=None,attr=None,journal=None) :
		if lang not in self._execContexts :
			if lang == TC.PYTHON :
				self._execContexts[lang] = PythonExecutionContext(self._dAPI)
			elif lang == TC.JAVASCRIPT and 'spidermonkey' in sys.modules :
				self._execContexts[lang] = JavaScriptExecutionContext(self._dAPI)
			else :
				assert False, 'unsupported designer code language :: '+str(lang)
		self._execContext = self._execContexts[lang]

		self._dAPI.configure(graph,type,pl2gi,ex,pLabel,attr,journal)


	''' 
		have the given code evaluated by the current ExecutionContext and return
		its output '''
	def eval(self,code) :
		return self._execContext.eval(code)


	'''
		identify the language in which the given snippet of designer code is 
		written in '''
	@staticmethod
	def identifyLanguage(code) :
		if code.startswith('"[JAVASCRIPT]"\n') :
			return TC.JAVASCRIPT
		elif code.startswith('"[PYTHON]"\n') :
			return TC.PYTHON
		return None



''' 
	this class provides means to evaluate javascript code that makes use of the
  	DesignerAPI '''
class JavaScriptExecutionContext :
	'''
  		setup a spidermonkey javascript execution context such that evaluated 
		javascript code will have access to DesignerAPI functions '''
	def __init__(self,dAPI) :
		self._context = spidermonkey.Runtime().new_context()
		self._context.bind_callable("getAttr",dAPI._getAttr)
		self._context.bind_callable("hasAttr",dAPI._hasAttr)
		self._context.bind_callable("setAttr",dAPI._setAttr)
		self._context.bind_callable("getAllNodes",dAPI._getAllNodes)
		self._context.bind_callable("getNeighbors",dAPI._getNeighbors)
		self._context.bind_callable("isConnectionType",dAPI._isConnectionType)
		self._context.bind_callable("httpReq",dAPI._httpReq)
		self._context.bind_callable("print",dAPI._print)
		self._context.bind_callable("printToDevCon",dAPI._printToDevCon)
		self._context.bind_callable("session_get",dAPI._session_get)
		self._context.bind_callable("session_put",dAPI._session_put)
		self._context.bind_callable("sys_call",dAPI._sys_call)
		self._context.bind_callable("sys_mkdir",dAPI._sys_mkdir)
		self._context.bind_callable("sys_readf",dAPI._sys_readf)
		self._context.bind_callable("sys_writef",dAPI._sys_writef)
		self._context.bind_callable("pauseTransformation",dAPI._pauseTransformation)
		self._context.bind_callable("resumeTransformation",dAPI._resumeTransformation)
		self._context.bind_callable("stopTransformation",dAPI._stopTransformation)

	''' 
		evaluate a string of javascript code and return its output '''
	def eval(self,code) :
		return self._context.eval_script(code)




''' 
	this class provides means to evaluate python code that makes use of the
  	DesignerAPI '''
class PythonExecutionContext :
	'''
  		setup a python execution context such that evaluated python code will have
	  	access to DesignerAPI class '''
	def __init__(self,dAPI) :
		self._context = \
			{'getAttr' 				: dAPI._getAttr,
			 'hasAttr'				: dAPI._hasAttr,
			 'getAttrNames'		    : dAPI._getAttrNames,
			 'setAttr' 				: dAPI._setAttr,
			 'getAllNodes' 			: dAPI._getAllNodes,
			 'getNodesFromLabels'	: dAPI._getNodesFromLabels,
			 'getNeighbors' 		: dAPI._getNeighbors,
			 'isConnectionType' 	: dAPI._isConnectionType,
			 'httpReq'		 		: dAPI._httpReq,
			 'printToDevCon'		: dAPI._printToDevCon,
			 'session_get' 			: dAPI._session_get,
			 'session_put' 			: dAPI._session_put,
			 'sys_call' 			: dAPI._sys_call,
			 'sys_mkdir' 			: dAPI._sys_mkdir,
			 'sys_readf' 			: dAPI._sys_readf,
			 'sys_writef' 			: dAPI._sys_writef,
			 'pauseTransformation'	: dAPI._pauseTransformation,
			 'resumeTransformation'	: dAPI._resumeTransformation,
			 'stopTransformation'	: dAPI._stopTransformation}


	''' 
		evaluate a string of python code and return its output 
		
		NOTE:: before evaluating, we clear past results, if any '''
	def eval(self,code) :
		if 'result' in self._context :
			del self._context['result']

		if sys.version_info[0] < 3:
			exec(code) in self._context
		else:
			exec((code), self._context)

		if 'result' not in self._context :
			return None
		return self._context['result']




