import axdebug, gateways
from util import _wrap, _wrap_remove, RaiseNotImpl
import cStringIO, traceback
from pprint import pprint
from win32com.server.exception import COMException
import winerror
import string
import sys

# Given an object, return a nice string
def MakeNiceString(ob):
	stream = cStringIO.StringIO()
	pprint(ob, stream)
	return string.strip(stream.getvalue())

class ProvideExpressionContexts(gateways.ProvideExpressionContexts):
	pass

class ExpressionContext(gateways.DebugExpressionContext):
	def __init__(self, frame):
		self.frame = frame
	def ParseLanguageText(self, code, radix, delim, flags):
		return _wrap(Expression(self.frame, code, radix, delim, flags), axdebug.IID_IDebugExpression)
	def GetLanguageInfo(self):
#		print "GetLanguageInfo"
		return "Python", "{DF630910-1C1D-11d0-AE36-8C0F5E000000}"
	
class Expression(gateways.DebugExpression):
	def __init__(self, frame, code, radix, delim, flags):
		self.callback = None
		self.frame = frame
		self.code = str(code)
		self.radix = radix
		self.delim = delim
		self.flags = flags
		self.isComplete = 0
		self.result=None
		self.hresult = winerror.E_UNEXPECTED
	def Start(self, callback):
		try:
			try:
				try:
					self.result = eval(self.code, self.frame.f_globals, self.frame.f_locals)
				except SyntaxError:
					exec self.code in self.frame.f_globals, self.frame.f_locals
					self.result = ""
				self.hresult = 0
			except:
				l = traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1])
				# l is a list of strings with trailing "\n"
				self.result = string.join(map(lambda s:s[:-1], l), "\n")
				self.hresult = winerror.E_FAIL
		finally:
			self.isComplete = 1
			callback.onComplete()
	def Abort(self):
		print "** ABORT **"
		
	def QueryIsComplete(self):
		return self.isComplete
		
	def GetResultAsString(self):
#		print "GetStrAsResult returning", self.result
		return self.hresult, MakeNiceString(self.result)
	
	def GetResultAsDebugProperty(self):
		result = _wrap(DebugProperty(self.code, self.result, None, self.hresult), axdebug.IID_IDebugProperty)
		return self.hresult, result

def MakeEnumDebugProperty(object, dwFieldSpec, nRadix, iid, stackFrame = None):
	name_vals = []
	if hasattr(object, "has_key"): # If it is a dict.
		name_vals = object.items()
		dictionary = object
	elif hasattr(object, "__dict__"):  #object with dictionary, module
		name_vals = object.__dict__.items()
		dictionary = object.__dict__
	infos = []
	for name, val in name_vals:
		infos.append(GetPropertyInfo(name, val, dwFieldSpec, nRadix, 0, dictionary, stackFrame))
	return _wrap(EnumDebugPropertyInfo(infos), axdebug.IID_IEnumDebugPropertyInfo)

def GetPropertyInfo(obname, obvalue, dwFieldSpec, nRadix, hresult=0, dictionary = None, stackFrame = None):
	# returns a tuple
	name = typ = value = fullname = attrib = dbgprop = None
	if dwFieldSpec & axdebug.DBGPROP_INFO_VALUE:
		value = MakeNiceString(obvalue)
	if dwFieldSpec & axdebug.DBGPROP_INFO_NAME:
		name = obname
	if dwFieldSpec & axdebug.DBGPROP_INFO_TYPE:
		if hresult:
			typ = "Error"
		else:
			try:
				typ = type(obvalue).__name__
			except AttributeError:
				typ = str(type(obvalue))
	if dwFieldSpec & axdebug.DBGPROP_INFO_FULLNAME:
		fullname = obname
	if dwFieldSpec & axdebug.DBGPROP_INFO_ATTRIBUTES:
		if hasattr(obvalue, "has_key") or hasattr(obvalue, "__dict__"): # If it is a dict or object
			attrib = axdebug.DBGPROP_ATTRIB_VALUE_IS_EXPANDABLE
		else:
			attrib = 0
	if dwFieldSpec & axdebug.DBGPROP_INFO_DEBUGPROP:
		dbgprop = _wrap(DebugProperty(name, obvalue, None, hresult, dictionary, stackFrame), axdebug.IID_IDebugProperty)
	return name, typ, value, fullname, attrib, dbgprop

from win32com.server.util import ListEnumeratorGateway
class EnumDebugPropertyInfo(ListEnumeratorGateway):
	"""A class to expose a Python sequence as an EnumDebugCodeContexts

	Create an instance of this class passing a sequence (list, tuple, or
	any sequence protocol supporting object) and it will automatically
	support the EnumDebugCodeContexts interface for the object.

	"""
	_public_methods_ = ListEnumeratorGateway._public_methods_ + ["GetCount"]
	_com_interfaces_ = [ axdebug.IID_IEnumDebugPropertyInfo]
	def GetCount(self):
		return len(self._list_)
	def _wrap(self, ob):
		return ob

class DebugProperty:
	_com_interfaces_ = [axdebug.IID_IDebugProperty]
	_public_methods_ = ['GetPropertyInfo', 'GetExtendedInfo', 'SetValueAsString', 
	                    'EnumMembers', 'GetParent'
	]
	def __init__(self, name, value, parent = None, hresult = 0, dictionary = None, stackFrame = None):
		self.name = name
		self.value = value
		self.parent = parent
		self.hresult = hresult
		self.dictionary = dictionary
		self.stackFrame = stackFrame

	def GetPropertyInfo(self, dwFieldSpec, nRadix):
		return GetPropertyInfo(self.name, self.value, dwFieldSpec, nRadix, self.hresult, dictionary, stackFrame)

	def GetExtendedInfo(self): ### Note - not in the framework.
		RaiseNotImpl("DebugProperty::GetExtendedInfo")

	def SetValueAsString(self, value, radix):
		if self.stackFrame and self.dictionary:
			self.dictionary[self.name]= eval(value,self.stackFrame.f_globals, self.stackFrame.f_locals)
		else:
			RaiseNotImpl("DebugProperty::SetValueAsString")

	def EnumMembers(self, dwFieldSpec, nRadix, iid):
		# Returns IEnumDebugPropertyInfo
		return MakeEnumDebugProperty(self.value, dwFieldSpec, nRadix, iid, self.stackFrame)

	def GetParent(self):
		# return IDebugProperty
		RaiseNotImpl("DebugProperty::GetParent")
