import win32com.axscript.axscript
import winerror
from win32com.axscript import axscript
from win32com.server import exception, util
import pythoncom

class AXEngine:
  def __init__(self, site, engine):
    self.eScript = self.eParse = self.eSafety = None
    if type(engine) == type(''):
      engine = pythoncom.CoCreateInstance(engine,
                                          None,
                                          pythoncom.CLSCTX_SERVER,
                                          pythoncom.IID_IUnknown)

    self.eScript = engine.QueryInterface(axscript.IID_IActiveScript)
    self.eParse = engine.QueryInterface(axscript.IID_IActiveScriptParse)
    self.eSafety = engine.QueryInterface(axscript.IID_IObjectSafety)

    self.eScript.SetScriptSite(site)
    self.eParse.InitNew()
  def __del__(self):
    self.Close()
  def GetScriptDispatch(self, name = None):
    return self.eScript.GetScriptDispatch(name)
  def AddNamedItem(self, item, flags):
    return self.eScript.AddNamedItem(item, flags)
  # Some helpers.  
  def AddCode(self, code, flags=0):
    self.eParse.ParseScriptText(code, None, None, None, 0, 0, flags)
  def EvalCode(self, code):
    return self.eParse.ParseScriptText(code, None, None, None, 0, 0, axscript.SCRIPTTEXT_ISEXPRESSION)
  def Start(self):
    # Should maybe check state?
    # Do I need to transition through?
    self.eScript.SetScriptState(axscript.SCRIPTSTATE_STARTED) 
#    self.eScript.SetScriptState(axscript.SCRIPTSTATE_CONNECTED)

  def Close(self):
    if self.eScript:
      self.eScript.Close()
    self.eScript = self.eParse = self.eSafety = None
  def SetScriptState(self, state):
    self.eScript.SetScriptState(state)

IActiveScriptSite_methods = [
  'GetLCID',
  'GetItemInfo',
  'GetDocVersionString',
  'OnScriptTerminate',
  'OnStateChange',
  'OnScriptError',
  'OnEnterScript',
  'OnLeaveScript',
  ]

class AXSite:
  """An Active Scripting site.  A Site can have exactly one engine.
  """
  _public_methods_ = IActiveScriptSite_methods
  _com_interfaces_ = [ axscript.IID_IActiveScriptSite ]

  def __init__(self, objModel={}, engine = None, lcid=0):
    self.lcid = lcid
    self.objModel = { }
    for name, object in objModel.iteritems():
      # Gregs code did string.lower this - I think that is callers job if he wants!
      self.objModel[name] = object

    self.engine = None
    if engine:
      self._AddEngine(engine)

  def AddEngine(self, engine):
    """Adds a new engine to the site.
    engine can be a string, or a fully wrapped engine object.
    """
    if type(engine)==type(''):
      newEngine = AXEngine(util.wrap(self), engine)
    else:
      newEngine = engine
    self.engine = newEngine
    flags = axscript.SCRIPTITEM_ISVISIBLE | axscript.SCRIPTITEM_NOCODE | axscript.SCRIPTITEM_GLOBALMEMBERS | axscript.SCRIPTITEM_ISPERSISTENT
    for name in self.objModel.iterkeys():
      newEngine.AddNamedItem(name, flags)
      newEngine.SetScriptState(axscript.SCRIPTSTATE_INITIALIZED)
    return newEngine

  # B/W compat
  _AddEngine = AddEngine

  def _Close(self):
    self.engine.Close()
    self.objModel = {}

  def GetLCID(self):
    return self.lcid

  def GetItemInfo(self, name, returnMask):
    if name not in self.objModel:
      raise exception.Exception(scode=winerror.TYPE_E_ELEMENTNOTFOUND, desc='item not found')

    ### for now, we don't have any type information

    if returnMask & axscript.SCRIPTINFO_IUNKNOWN:
      return (self.objModel[name], None)

    return (None, None)

  def GetDocVersionString(self):
    return 'Python AXHost version 1.0'

  def OnScriptTerminate(self, result, excepInfo):
    pass

  def OnStateChange(self, state):
    pass

  def OnScriptError(self, errorInterface):
    return winerror.S_FALSE  

  def OnEnterScript(self):
    pass

  def OnLeaveScript(self):
    pass
