# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved.
# License: New BSD License.
# Website: http://code.google.com/p/cefpython/

import win32gui
import win32con
import win32api
import time
import math
import os
import sys
import re

if sys.version_info.major == 2:
    from urllib import pathname2url as urllib_pathname2url
else:
    from urllib.request import pathname2url as urllib_pathname2url

g_debug = False
g_windows = {} # windowID(int): className
g_registeredClasses = {}

def Debug(msg):

    if not g_debug:
        return
    msg = "cefwindow: "+str(msg)
    print(msg)
    with open(GetRealPath("debug.log"), "a") as file:
        file.write(msg+"\n")

def GetRealPath(file=None, encodeURL=False):

    # This function is defined in 2 files: cefpython.pyx and cefwindow.py, if you make changes edit both files.
    # If file is None return current directory, without trailing slash.

    # encodeURL param - will call urllib.pathname2url(), only when file is empty (current dir)
    # or is relative path ("test.html", "some/test.html"), we need to encode it before passing
    # to CreateBrowser(), otherwise it is encoded by CEF internally and becomes (chinese characters):
    # >> %EF%BF%97%EF%BF%80%EF%BF%83%EF%BF%A6
    # but should be:
    # >> %E6%A1%8C%E9%9D%A2

    if file is None: file = ""
    if file.find("/") != 0 and file.find("\\") != 0 and not re.search(r"^[a-zA-Z]+:[/\\]?", file):
        # Execute this block only when relative path ("test.html", "some\test.html") or file is empty (current dir).
        # 1. find != 0 >> not starting with / or \ (/ - linux absolute path, \ - just to be sure)
        # 2. not re.search >> not (D:\\ or D:/ or D: or http:// or ftp:// or file://),
        #     "D:" is also valid absolute path ("D:cefpython" in chrome becomes "file:///D:/cefpython/")
        if hasattr(sys, "frozen"): path = os.path.dirname(sys.executable)
        elif "__file__" in globals(): path = os.path.dirname(os.path.realpath(__file__))
        else: path = os.getcwd()
        path = path + os.sep + file
        path = re.sub(r"[/\\]+", re.escape(os.sep), path)
        path = re.sub(r"[/\\]+$", "", path) # directory without trailing slash.
        if encodeURL:
            return urllib_pathname2url(path)
        else:
            return path
    return file

def CreateWindow(title, className, width, height, xpos=None, ypos=None, icon=None, windowProc=None):

    """
    for key in g_windows:
        if g_windows[key] == className:
            raise Exception("There was already created a window with that className: %s."
                "Each created window must have an unique className." % className)
    """

    if not windowProc:
        windowProc = {win32con.WM_CLOSE: WM_CLOSE}

    bigIcon = ""
    smallIcon = ""

    if icon:
        icon = GetRealPath(icon)

        # Load small and big icon.
        # WNDCLASSEX (along with hIconSm) is not supported by pywin32,
        # we need to use WM_SETICON message after window creation.

        # http://stackoverflow.com/questions/2234988/how-to-set-hicon-on-a-window-ico-with-multiple-sizes
        # http://blog.barthe.ph/2009/07/17/wmseticon/

        bigX = win32api.GetSystemMetrics(win32con.SM_CXICON)
        bigY = win32api.GetSystemMetrics(win32con.SM_CYICON)
        bigIcon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, bigX, bigY, win32con.LR_LOADFROMFILE)
        smallX = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
        smallY = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
        smallIcon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, smallX, smallY, win32con.LR_LOADFROMFILE)

    wndclass = win32gui.WNDCLASS()
    wndclass.hInstance = win32api.GetModuleHandle(None)
    wndclass.lpszClassName = className
    wndclass.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
    # win32con.CS_GLOBALCLASS
    wndclass.hbrBackground = win32con.COLOR_WINDOW
    wndclass.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
    wndclass.lpfnWndProc = windowProc

    #noinspection PyUnusedLocal
    global g_registeredClasses
    if not className in g_registeredClasses:
        g_registeredClasses[className] = True
        atomclass = win32gui.RegisterClass(wndclass)
        Debug("win32gui.RegisterClass(%s)" % className)

    if xpos is None or ypos is None:
        # Center window on the screen.
        Debug("Centering window on the screen.")
        screenx = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
        screeny = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
        xpos = int(math.floor((screenx - width) / 2))
        ypos = int(math.floor((screeny - height) / 2))
        if xpos < 0: xpos = 0
        if ypos < 0: ypos = 0

    windowID = win32gui.CreateWindow(className, title,
            win32con.WS_OVERLAPPEDWINDOW | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE,
            xpos, ypos, width, height, # xpos, ypos, width, height
            0, 0, wndclass.hInstance, None)
    g_windows[windowID] = className

    if icon:
        if bigIcon:
            win32api.SendMessage(windowID, win32con.WM_SETICON, win32con.ICON_BIG, bigIcon)
        if smallIcon:
            win32api.SendMessage(windowID, win32con.WM_SETICON, win32con.ICON_SMALL, smallIcon)

    Debug("windowID = %s" % windowID)
    return windowID


# Memory error when calling win32gui.DestroyWindow()
# after we called cefpython.CloseBrowser()

def DestroyWindow(windowID):

    win32gui.DestroyWindow(windowID)
    #className = GetWindowClassName(windowID)
    #win32gui.UnregisterClass(className, None)
    #del g_windows[windowID] # Let window with this className be created again.


def GetWindowClassName(windowID):

    for key in g_windows:
        if key == windowID:
            return g_windows[key]

def MoveWindow(windowID, xpos=None, ypos=None, width=None, height=None, center=None):

    (left, top, right, bottom) = win32gui.GetWindowRect(windowID)
    if xpos is None and ypos is None:
        xpos = left
        ypos = top
    if width is None and height is None:
        width = right - left
        height = bottom - top
    # Case: only ypos provided
    if xpos is None and ypos is not None:
        xpos = left
    if ypos is None and xpos is not None:
        ypos = top
    # Case: only height provided
    if not width:
        width = right - left
    if not height:
        height = bottom - top
    if center:
        screenx = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
        screeny = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
        xpos = int(math.floor((screenx - width) / 2))
        ypos = int(math.floor((screeny - height) / 2))
        if xpos < 0: xpos = 0
        if ypos < 0: ypos = 0
    win32gui.MoveWindow(windowID, xpos, ypos, width, height, 1)


#noinspection PyUnusedLocal
def WM_CLOSE(windowID, msg, wparam, lparam):

    DestroyWindow(windowID)
    win32gui.PostQuitMessage(0)


def GetLastError():

    code = win32api.GetLastError()
    return "(%d) %s" % (code, win32api.FormatMessage(code))

#noinspection PyUnusedLocal
def MessageLoop(className):

    while not win32gui.PumpWaitingMessages():
        time.sleep(0.001)


if __name__ == "__main__":

    g_debug = True
    hwnd = CreateWindow("Test window", "testwindow", 800, 600)
    MessageLoop("testwindow")