import sys
from win32com.axscript.server.error import Exception
from win32com.axscript import axscript
from win32com.axscript.server import axsite
import pythoncom
from win32com.server import util, connect
import win32com.server.policy

class MySite(axsite.AXSite):

  def OnScriptError(self, error):
    exc = error.GetExceptionInfo()
    context, line, char = error.GetSourcePosition()
    print(" >Exception:", exc[1])
    try:
      st = error.GetSourceLineText()
    except pythoncom.com_error:
      st = None
    if st is None: st = ""
    text = st + "\n" + (" " * (char-1)) + "^" + "\n" + exc[2]
    for line in text.splitlines():
      print("  >" + line)

class MyCollection(util.Collection):
	def _NewEnum(self):
		print("Making new Enumerator")
		return util.Collection._NewEnum(self)

class Test:
  _public_methods_ = [ 'echo' ]
  _public_attrs_ = ['collection', 'verbose']
  def __init__(self):
    self.verbose = 0
    self.collection = util.wrap( MyCollection( [1,'Two',3] ))
    self.last = ""
#    self._connect_server_ = TestConnectServer(self)

  def echo(self, *args):
    self.last = ''.join(map(str, args))
    if self.verbose:
      for arg in args:
        print(arg, end=' ')
      print()
#    self._connect_server_.Broadcast(last)


#### Connections currently wont work, as there is no way for the engine to
#### know what events we support.  We need typeinfo support.

IID_ITestEvents = pythoncom.MakeIID("{8EB72F90-0D44-11d1-9C4B-00AA00125A98}")

class TestConnectServer(connect.ConnectableServer):
	_connect_interfaces_ = [IID_ITestEvents]
	# The single public method that the client can call on us
	# (ie, as a normal COM server, this exposes just this single method.
	def __init__(self, object):
		self.object = object
		
	def Broadcast(self,arg):
		# Simply broadcast a notification.
		self._BroadcastNotify(self.NotifyDoneIt, (arg,))

	def NotifyDoneIt(self, interface, arg):
		interface.Invoke(1000, 0, pythoncom.DISPATCH_METHOD, 1, arg)

VBScript = """\
prop = "Property Value"

sub hello(arg1)
   test.echo arg1
end sub
  
sub testcollection
   test.verbose = 1
   for each item in test.collection
     test.echo "Collection item is", item
   next
end sub
"""
if sys.version_info < (3,):
    PyScript = """print "PyScript is being parsed..."\n"""
else:
    PyScript = """print("PyScript is being parsed...")\n"""
PyScript += """\
prop = "Property Value"
def hello(arg1):
   test.echo(arg1)
   pass
   
def testcollection():
   test.verbose = 1
#   test.collection[1] = "New one"
   for item in test.collection:
     test.echo("Collection item is", item)
   pass
"""

ErrScript = """\
bad code for everyone!
"""


def TestEngine(engineName, code, bShouldWork = 1):
  echoer = Test()
  model = {
    'test' : util.wrap(echoer),
    }

  site = MySite(model)
  engine = site._AddEngine(engineName)
  engine.AddCode(code, axscript.SCRIPTTEXT_ISPERSISTENT)
  try:
    engine.Start()
  finally:
    if not bShouldWork:
      engine.Close()
      return
  doTestEngine(engine, echoer)
  # re-transition the engine back to the UNINITIALIZED state, a-la ASP.
  engine.eScript.SetScriptState(axscript.SCRIPTSTATE_UNINITIALIZED)
  engine.eScript.SetScriptSite(util.wrap(site))
  print("restarting")
  engine.Start()
  # all done!
  engine.Close()

def doTestEngine(engine, echoer):    
  # Now call into the scripts IDispatch
  from win32com.client.dynamic import Dispatch
  ob = Dispatch(engine.GetScriptDispatch())
  try:
    ob.hello("Goober")
  except pythoncom.com_error as exc:
    print("***** Calling 'hello' failed", exc)
    return
  if echoer.last != "Goober":
    print("***** Function call didnt set value correctly", repr(echoer.last))
    
  if str(ob.prop) != "Property Value":
    print("***** Property Value not correct - ", repr(ob.prop))

  ob.testcollection()

  # Now make sure my engines can evaluate stuff.
  result = engine.eParse.ParseScriptText("1+1", None, None, None, 0, 0, axscript.SCRIPTTEXT_ISEXPRESSION)
  if result != 2:
    print("Engine could not evaluate '1+1' - said the result was", result)

def dotestall():
  for i in range(10):
    TestEngine("Python", PyScript)
    print(sys.gettotalrefcount())
##  print "Testing Exceptions"
##  try:
##    TestEngine("Python", ErrScript, 0)
##  except pythoncom.com_error:
##    pass
   

def testall():
  dotestall()
  pythoncom.CoUninitialize()
  print("AXScript Host worked correctly - %d/%d COM objects left alive." % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))

if __name__ == '__main__':
	testall()
