import regutil, os
import hierlist
import win32con, win32ui, win32api
import commctrl
from pywin.mfc import dialog
import glob
import pyclbr
import pywin.framework.scriptutils
import afxres

class HLIErrorItem(hierlist.HierListItem):
	def __init__(self, text):
		self.text = text
		hierlist.HierListItem.__init__(self)
	def GetText(self):
		return self.text

class HLICLBRItem(hierlist.HierListItem):
	def __init__(self, name, file, lineno, suffix = ""):
		# If the 'name' object itself has a .name, use it.  Not sure
		# how this happens, but seems pyclbr related. 
		# See PyWin32 bug 817035
		self.name = getattr(name, "name", name)
		self.file = file
		self.lineno = lineno
		self.suffix = suffix
	def __lt__(self, other):
		return self.name < other.name
	def __eq__(self, other):
		return self.name == other.name
	def GetText(self):
		return self.name + self.suffix
	def TakeDefaultAction(self):
		if self.file:
			pywin.framework.scriptutils.JumpToDocument(self.file, self.lineno, bScrollToTop=1)
		else:
			win32ui.SetStatusText("The source of this object is unknown")
	def PerformItemSelected(self):
		if self.file is None:
			msg = "%s - source can not be located." % (self.name, )
		else:
			msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file)
		win32ui.SetStatusText(msg)

class HLICLBRClass(HLICLBRItem):
	def __init__(self, clbrclass, suffix = ""):
		try:
			name = clbrclass.name
			file = clbrclass.file
			lineno = clbrclass.lineno
			self.super = clbrclass.super
			self.methods = clbrclass.methods
		except AttributeError:
			name = clbrclass
			file = lineno = None
			self.super = []; self.methods = {}
		HLICLBRItem.__init__(self, name, file, lineno, suffix)
	def GetSubList(self):
		 ret = []
		 for c in self.super:
			 ret.append(HLICLBRClass(c, " (Parent class)"))
		 for meth, lineno in self.methods.iteritems():
			 ret.append(HLICLBRMethod(meth, self.file, lineno, " (method)"))
		 return ret
	def IsExpandable(self):
		return len(self.methods) + len(self.super)
	def GetBitmapColumn(self):
		return 21

class HLICLBRFunction(HLICLBRClass):
	def GetBitmapColumn(self):
		return 22
class HLICLBRMethod(HLICLBRItem):
	def GetBitmapColumn(self):
		return 22

class HLIModuleItem(hierlist.HierListItem):
	def __init__(self, path):
		hierlist.HierListItem.__init__(self)
		self.path = path
	def GetText(self):
		return os.path.split(self.path)[1] + " (module)"
	def IsExpandable(self):
		return 1
	def TakeDefaultAction(self):
		win32ui.GetApp().OpenDocumentFile( self.path )
	def GetBitmapColumn(self):
		col = 4 # Default
		try:
			if win32api.GetFileAttributes(self.path) & win32con.FILE_ATTRIBUTE_READONLY:
				col = 5
		except win32api.error:
			pass
		return col
	def GetSubList(self):
		mod, path = pywin.framework.scriptutils.GetPackageModuleName(self.path)
		win32ui.SetStatusText("Building class list - please wait...", 1)
		win32ui.DoWaitCursor(1)
		try:
			try:
				reader = pyclbr.readmodule_ex # Post 1.5.2 interface.
				extra_msg = " or functions"
			except AttributeError:
				reader = pyclbr.readmodule
				extra_msg = ""
			data = reader(mod, [path])
			if data:
				ret = []
				for item in data.itervalues():
					if item.__class__ != pyclbr.Class: # ie, it is a pyclbr Function instance (only introduced post 1.5.2)
						ret.append(HLICLBRFunction( item, " (function)" ) )
					else:
						ret.append(HLICLBRClass( item, " (class)") )
				ret.sort()
				return ret
			else:
				return [HLIErrorItem("No Python classes%s in module." % (extra_msg,))]
		finally:
			win32ui.DoWaitCursor(0)
			win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))

def MakePathSubList(path):
	ret = []
	for filename in glob.glob(os.path.join(path,'*')):
		if os.path.isdir(filename) and os.path.isfile(os.path.join(filename, "__init__.py")):
			ret.append(HLIDirectoryItem(filename, os.path.split(filename)[1]))
		else:
			if os.path.splitext(filename)[1].lower() in ['.py', '.pyw']:
				ret.append(HLIModuleItem(filename))
	return ret

class HLIDirectoryItem(hierlist.HierListItem):
	def __init__(self, path, displayName = None, bSubDirs = 0):
		hierlist.HierListItem.__init__(self)
		self.path = path
		self.bSubDirs = bSubDirs
		if displayName:
			self.displayName = displayName
		else:
			self.displayName = path
	def IsExpandable(self):
		return 1
	def GetText(self):
		return self.displayName

	def GetSubList(self):
		ret = MakePathSubList(self.path)
		if os.path.split(self.path)[1] == "win32com": # Complete and utter hack for win32com.
			try:
				path = win32api.GetFullPathName(os.path.join(self.path, "..\\win32comext"))
				ret = ret + MakePathSubList(path)
			except win32ui.error:
				pass
		return ret

class HLIProjectRoot(hierlist.HierListItem):
	def __init__(self, projectName, displayName = None):
		hierlist.HierListItem.__init__(self)
		self.projectName = projectName
		self.displayName = displayName or projectName
	def GetText(self):
		return self.displayName
	def IsExpandable(self):
		return 1
	def GetSubList(self):
		paths = regutil.GetRegisteredNamedPath(self.projectName)
		pathList = paths.split(";")
		if len(pathList)==1: # Single dir - dont bother putting the dir in
			ret = MakePathSubList(pathList[0])
		else:
			ret = list(map( HLIDirectoryItem, pathList ))
		return ret

class HLIRoot(hierlist.HierListItem):
	def __init__(self):
		hierlist.HierListItem.__init__(self)
	def IsExpandable(self):
		return 1
	def GetSubList(self):
		keyStr = regutil.BuildDefaultPythonKey() + "\\PythonPath"
		hKey = win32api.RegOpenKey(regutil.GetRootKey(), keyStr)
		try:
			ret = []
			ret.append(HLIProjectRoot("", "Standard Python Library")) # The core path.
			index = 0
			while 1:
				try:
					ret.append(HLIProjectRoot(win32api.RegEnumKey(hKey, index)))
					index = index + 1
				except win32api.error:
					break
			return ret
		finally:
			win32api.RegCloseKey(hKey)

class dynamic_browser (dialog.Dialog):
    style = win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE
    cs = (
        win32con.WS_CHILD           |
        win32con.WS_VISIBLE         |
        commctrl.TVS_HASLINES       |
        commctrl.TVS_LINESATROOT    |
        commctrl.TVS_HASBUTTONS
        )

    dt = [
        ["Python Projects", (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
        ["SysTreeView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), cs]
        ]

    def __init__ (self, hli_root):
        dialog.Dialog.__init__ (self, self.dt)
        self.hier_list = hierlist.HierListWithItems (
            hli_root,
            win32ui.IDB_BROWSER_HIER
            )
        self.HookMessage (self.on_size, win32con.WM_SIZE)

    def OnInitDialog (self):
        self.hier_list.HierInit (self)
        return dialog.Dialog.OnInitDialog (self)

    def on_size (self, params):
        lparam = params[3]
        w = win32api.LOWORD(lparam)
        h = win32api.HIWORD(lparam)
        self.GetDlgItem (win32ui.IDC_LIST1).MoveWindow((0,0,w,h))

def BrowseDialog():
    root = HLIRoot()
    if not root.IsExpandable():
        raise TypeError("Browse() argument must have __dict__ attribute, or be a Browser supported type")
        
    dlg = dynamic_browser (root)
    dlg.CreateWindow()

def DockableBrowserCreator(parent):
	root = HLIRoot()
	hl = hierlist.HierListWithItems (
            root,
            win32ui.IDB_BROWSER_HIER
            )

	style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS

	control = win32ui.CreateTreeCtrl()
	control.CreateWindow(style, (0, 0, 150, 300), parent, win32ui.IDC_LIST1)
	list = hl.HierInit (parent, control)
	return control

def DockablePathBrowser():
	import pywin.docking.DockingBar
	bar = pywin.docking.DockingBar.DockingBar()
	bar.CreateWindow(win32ui.GetMainFrame(), DockableBrowserCreator, "Path Browser", 0x8e0a)
	bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
	bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
	win32ui.GetMainFrame().DockControlBar(bar)

# The "default" entry point
Browse = DockablePathBrowser
