#--------------------------------------------------------------------
#
#   PyGUI - Window - Win32
#
#--------------------------------------------------------------------

import win32con as wc, win32ui as ui, win32gui as gui, win32api as api
from GUI import export
from GUI import WinUtils 
from GUI.Geometry import rect_size, sub_pt
from GUI import application
from GUI.Exceptions import Cancel
from GUI.WinEvents import win_message_to_event
from GUI.WinMenus import MenuBar, win_id_to_command
from GUI.GMenus import search_list_for_key
from GUI import Component
from GUI.GWindows import Window as GWindow

#--------------------------------------------------------------------

capabilities = ('hidable', 'zoomable', 'resizable', 'movable', 'closable')

win_defaults = {
    'standard':        (1, 1, 1, 1, 1),
    'nonmodal_dialog': (1, 0, 0, 1, 1),
    'modal_dialog':    (0, 0, 0, 1, 1),
    'alert':           (0, 0, 0, 1, 1),
    'fullscreen':      (0, 0, 0, 0, 0),
}

win_base_flags = wc.WS_CLIPCHILDREN
win_border_flags = wc.WS_DLGFRAME

win_capability_flags = {
    'hidable': wc.WS_MINIMIZEBOX | wc.WS_SYSMENU,
    'zoomable': wc.WS_MAXIMIZEBOX | wc.WS_SYSMENU,
    'resizable': wc.WS_THICKFRAME,
    'movable': wc.WS_BORDER,
    'closable': wc.WS_SYSMENU,
}

win_no_menu_styles = ('nonmodal_dialog', 'modal_dialog',
    'alert', 'fullscreen')

win_ex_flags = 0 #wc.WS_EX_WINDOWEDGE
win_no_ex_flags = wc.WS_EX_CLIENTEDGE

def win_calculate_flags(style, kwds):
    #  Calculate window flags from the options present in kwds, and
    #  fill in kwds with default values for missing options that need
    #  to be passed to the base class constructor.
    flags = win_base_flags
    if style != 'fullscreen':
        flags |= win_border_flags
    try:
        defaults = win_defaults[style]
    except KeyError:
        raise ValueError("Invalid window style '%s'" % style)
    for name, default in zip(capabilities, defaults):
        value = kwds.pop(name, default)
        if name == 'closable':
            kwds[name] = value
        if value:
            flags |= win_capability_flags[name]
    return flags

#def win_adjust_flags(flags, kwds, option_name, opt_flags):
#	option = kwds.pop(option_name, None)
#	if option is not None:
#		if option:
#			flags |= opt_flags
#		else:
#			flags &= ~opt_flags
#	return flags

def win_next_wnd(wnd):
    wnd = getattr(wnd, '_win', wnd)
    #print "win_next_wnd:", wnd ###
    return  wnd.GetWindow(wc.GW_HWNDNEXT)

#--------------------------------------------------------------------

class Window(GWindow):

    _win_hooks_events = True
    _win_has_menubar = True
    _win_captures_mouse = True

    _win_need_menubar_update = True
    _win_saved_target = False
    _win_fullscreen = False

    def __init__(self, **kwds):
        style = kwds.get('style', 'standard')
        flags = win_calculate_flags(style, kwds)
        #if style == 'fullscreen':
        #	rect = WinUtils.win_screen_rect
        #else:
        rect = (0, 0, self._default_width, self._default_height)
        frame = ui.CreateFrame()
        frame.CreateWindow(None, "New Window", 0, rect)
        hwnd = frame.GetSafeHwnd()
        #api.SetClassLong(hwnd, wc.GCL_HBRBACKGROUND, win_bg_hbrush)
        api.SetClassLong(hwnd, wc.GCL_HBRBACKGROUND, 0)
#		print "Window: Setting style:" ###
#		win_deconstruct_style(flags) ###
        frame.ModifyStyle(-1, flags)
#		print "Window: Style is now:" ###
#		win_deconstruct_style(frame.GetStyle()) ###
        frame.ModifyStyleEx(win_no_ex_flags, win_ex_flags)
        if style == 'fullscreen':
            self._win_fullscreen = True
        frame.HookMessage(self._win_wm_initmenu, wc.WM_INITMENU)
        self._win = frame
        if style in win_no_menu_styles:
            self._win_has_menubar = False
        else:
            self._win_set_empty_menubar()
        kwds['closable'] = flags & wc.WS_CAPTION <> 0
        GWindow.__init__(self, _win = frame, **kwds)
    
    def OnPaint(self):
        win = self._win
        dc, ps = win.BeginPaint()
        rect = win.GetClientRect()
        dc.FillSolidRect(rect, WinUtils.win_bg_color)
        if self._win_has_menubar:
            l, t, r, b = rect
            dc.Draw3dRect((l, t, r + 1, t + 2),
                WinUtils.win_color3dshadow, WinUtils.win_color3dhilight)
        win.EndPaint(ps)

    def _win_install_event_hooks(self): 
        self._win.HookMessage(self._wm_activate, wc.WM_ACTIVATE)
        #self._win.HookMessage(self._wm_setfocus, wc.WM_SETFOCUS)
        self._win.HookMessage(self._wm_windowposchanging, wc.WM_WINDOWPOSCHANGING)
        self._win.HookMessage(self._wm_windowposchanged, wc.WM_WINDOWPOSCHANGED)
        GWindow._win_install_event_hooks(self)
    
    def _wm_activate(self, msg):
        wParam = msg[2]
        #print "Window._wm_activate:", msg ###
        #print "...wParam =", wParam ###
        if wParam == wc.WA_INACTIVE:
            #print "Window: Deactivating:", self ###
            try:
                target = ui.GetFocus()
                #print "...target =", target ###
            except ui.error, e:
                #print "...no target", e ###
                target = None
            if isinstance(target, Component) and target is not self:
                #print "...saving target", target ###
                self._win_saved_target = target

    def _win_wm_setfocus(self, msg):
        #print "Window._win_wm_setfocus:", self ###
        target = self._win_saved_target
        if target and target.window == self:
            #print "...restoring target", target ###
            target._win.SetFocus()
            self._win_saved_target = None
        else:
            GWindow._win_wm_setfocus(self, msg)
    
    def get_target(self):
        if self._win_is_active():
            try:
                target = ui.GetFocus()
            except ui.error:
                target = None
            if target and isinstance(target, Component):
                return target
        return self._saved_target or self
    
    def _win_is_active(self):
        try:
            active_win = ui.GetActiveWindow()
        except ui.error:
            active_win = None
        return active_win is self

#	def _wm_setfocus(self, *args):
#		print "Window._wm_setfocus:", args ###

    def _wm_windowposchanging(self, message):
        #print "Window._wm_windowposchanging"
        self._win_old_size = rect_size(self._bounds)
        #print "...old size =", self._win_old_size
    
    def _wm_windowposchanged(self, message):
        #print "Window._wm_windowposchanged"
        old_size = self._win_old_size
        new_bounds = self._win_get_actual_bounds()
        self._bounds = new_bounds
        new_size = rect_size(new_bounds)
        #print "...new size =", new_size
        if old_size != new_size:
            self._resized(sub_pt(new_size, old_size))
    
    def _win_set_empty_menubar(self):
        #  A completely empty menu bar collapses to zero height, and
        #  controlling the window bounds is too complicated if the
        #  menu bar comes and goes, so we add a dummy item to it.
        menubar = MenuBar()
        menubar.win_menu.AppendMenu(0, 0, "")
        self._win.SetMenu(menubar.win_menu)
        self._win_menubar = menubar
    
    def get_title(self):
        return self._win.GetWindowText()
    
    def set_title(self, x):
        self._win.SetWindowText(x)

    def get_visible(self):
        return self._win.IsWindowVisible()
    
    def set_visible(self, x):
        #print "Window.set_visible:", x, self ###
        if x:
            self._win_update_menubar()
            self._win.ShowWindow()
        else:
            self._win.ShowWindow(wc.SW_HIDE)
    
    def _show(self):
        self._win_update_menubar()
        win = self._win
        if self._win_fullscreen:
            win.ShowWindow(wc.SW_SHOWMAXIMIZED)
#		win.SetWindowPos(wc.HWND_TOP, (0, 0, 0, 0),
#			wc.SWP_NOMOVE | wc.SWP_NOSIZE | wc.SWP_SHOWWINDOW)
        win.ShowWindow(wc.SW_SHOWNORMAL)
        win.SetActiveWindow()
    
#	def get_bounds(self):
#		win = self._win
#		r = win.ClientToScreen(win.GetClientRect())
#		return r

    def _win_get_actual_bounds(self):
        win = self._win
        return win.ClientToScreen(win.GetClientRect())
    
    def _win_move_window(self, rect):
        win = self._win
        l, t, r, b = win.CalcWindowRect(rect)
        if self._win_has_menubar:
            t -= WinUtils.win_menubar_height
        self._win.MoveWindow((l, t, r, b))
    
    def set_menus(self, x):
        GWindow.set_menus(self, x)
        self._win_menus_changed()

    def _stagger(self):
        #print "Window._stagger:", self ###
        win = win_next_wnd(self._win)
        while win and not (isinstance(win, Window) and win.visible):
            #print "...win =", win ###
            win = win_next_wnd(win)
        if win:
            l, t, r, b = win._win.GetWindowRect()
            hwnd = self._win.GetSafeHwnd()
            gui.SetWindowPos(hwnd, 0, l + 20, t + 20, 0, 0,
                wc.SWP_NOSIZE | wc.SWP_NOZORDER)

    def OnClose(self):
        #print "Window:", self, "OnClose"
        try:
            self.close_cmd()
        except Cancel:
            pass
        except:
            application().report_error()
        
    def _win_menus_changed(self):
        self._win_need_menubar_update = True
        if self.visible:
            self._win_update_menubar()
    
    def _win_update_menubar(self):
        #print "Window._win_update_menubar:", self ###
        if self._win_need_menubar_update:
            all_menus = application()._effective_menus_for_window(self)
            self._all_menus = all_menus
            if self._win_has_menubar:
                if all_menus:
                    menubar = MenuBar()
                    for menu in all_menus:
                        menubar.append_menu(menu)
                    self._win.SetMenu(menubar.win_menu)
                    self._win_menubar = menubar
                else:
                    self._win_set_empty_menubar()
            self._win_need_menubar_update = False
    
    def _win_wm_initmenu(self, message):
        #print "Window._win_wm_initmenu:", self ###
        self._win_perform_menu_setup()
    
    def _win_perform_menu_setup(self):
        #print "Window._win_perform_menu_setup:", self ###
        application()._perform_menu_setup(self._all_menus)
    
    def _win_menu_command(self, id):
        command = win_id_to_command(id)
        if command:
            application().dispatch_menu_command(command)
    
    def _win_possible_menu_key(self, key, shift, option):
        self._win_perform_menu_setup()
        command = search_list_for_key(self._all_menus, key, shift, option)
        if command:
            application().dispatch_menu_command(command)
            return True
    
    def _screen_rect(self):
        return WinUtils.win_screen_rect
    
    def modal_event_loop(self):
        disabled = []
        for window in application().windows:
            if window is not self:
                if not window._win.EnableWindow(False):
                    #print "Window.modal_event_loop: disabled", window.title ###
                    disabled.append(window)
        status = self._win.RunModalLoop(0)
        if status:
            print "Window._modal_event_loop:", self, "status =", status ###
            #raise Cancel
        for window in disabled:
            #print "Window.modal_event_loop: enabling", window.title ###
            window._win.EnableWindow(True)
        if status <> 0: ###
            from GUI.Exceptions import InternalError ###
            raise InternalError("RunModalLoop returned %s" % status) ###
    
    def exit_modal_event_loop(self):
        self._win.EndModalLoop(0)

export(Window)
