from __future__ import division
from numpy import round
from threading import Thread
from time import sleep
from win32gui import (InitCommonControls, CallWindowProc, CreateWindowEx,
                      CreateWindow, SetWindowLong, SendMessage, ShowWindow,
                      PumpWaitingMessages, PostQuitMessage, DestroyWindow,
                      MessageBox, EnumWindows, GetClassName)
from win32api import GetModuleHandle, GetSystemMetrics  # @UnresolvedImport
from win32api import SetWindowLong as api_SetWindowLong  # @UnresolvedImport
from commctrl import (TOOLTIPS_CLASS, TTM_GETDELAYTIME, TTM_SETDELAYTIME,
                      TTDT_INITIAL, TTDT_AUTOPOP)
import win32con

WM_USER = win32con.WM_USER
PBM_SETRANGE = (WM_USER + 1)
PBM_SETPOS = (WM_USER + 2)
PBM_DELTAPOS = (WM_USER + 3)
PBM_SETSTEP = (WM_USER + 4)
PBM_STEPIT = (WM_USER + 5)
PBM_SETRANGE32 = (WM_USER + 6)
PBM_GETRANGE = (WM_USER + 7)
PBM_GETPOS = (WM_USER + 8)
PBM_SETBARCOLOR = (WM_USER + 9)
PBM_SETMARQUEE = (WM_USER + 10)
PBM_GETSTEP = (WM_USER + 13)
PBM_GETBKCOLOR = (WM_USER + 14)
PBM_GETBARCOLOR = (WM_USER + 15)
PBM_SETSTATE = (WM_USER + 16)
PBM_GETSTATE = (WM_USER + 17)
PBS_SMOOTH = 0x01
PBS_VERTICAL = 0x04
PBS_MARQUEE = 0x08
PBS_SMOOTHREVERSE = 0x10
PBST_NORMAL = 1
PBST_ERROR = 2
PBST_PAUSED = 3
WC_DIALOG = 32770
WM_SETTEXT = win32con.WM_SETTEXT


def MAKELPARAM(a, b):
    return (a & 0xffff) | ((b & 0xffff) << 16)


def _get_tooltip_handles(hwnd, resultList):
    '''
    Adds a window handle to resultList if its class-name is 'tooltips_class32',
    i.e. the window is a tooltip.
    '''
    if GetClassName(hwnd) == TOOLTIPS_CLASS:
        resultList.append(hwnd)


def set_max_pop_delay_on_tooltip(tooltip):
    '''
    Sets maximum auto-pop delay (delay before hiding) on an instance of
    wx.ToolTip.
    NOTE: The tooltip's SetDelay method is used just to identify the correct
    tooltip.
    '''
    test_value = 12345
    # Set initial delay just to identify tooltip.
    tooltip.SetDelay(test_value)
    handles = []
    EnumWindows(_get_tooltip_handles, handles)
    for hwnd in handles:
        if SendMessage(hwnd, TTM_GETDELAYTIME, TTDT_INITIAL) == test_value:
            SendMessage(hwnd, TTM_SETDELAYTIME, TTDT_AUTOPOP, 32767)
    tooltip.SetDelay(500)   # Restore default value


class Waitbar(Thread):

    def __init__(self, title='Waitbar', can_abort=True, max_val=100):
        Thread.__init__(self)     # Initialize thread
        self.title = title
        self.can_abort = can_abort
        self.max_val = max_val
        InitCommonControls()
        self.hinst = GetModuleHandle(None)
        self.started = False
        self.position = 0
        self.do_update = False
        self.start()                        # Run the thread
        while not self.started:
            sleep(0.1)                      # Wait until the dialog is ready

    def DlgProc(self, hwnd, uMsg, wParam, lParam):
        if uMsg == win32con.WM_DESTROY:
            api_SetWindowLong(self.dialog,
                              win32con.GWL_WNDPROC,
                              self.oldWndProc)
        if uMsg == win32con.WM_CLOSE:
            self.started = False
        if uMsg == win32con.WM_COMMAND and self.can_abort:
            self.started = False
        return CallWindowProc(self.oldWndProc, hwnd, uMsg, wParam, lParam)

    def BuildWindow(self):
        width = 400
        height = 100
        self.dialog = CreateWindowEx(
            win32con.WS_EX_TOPMOST,
            WC_DIALOG,
            self.title + ' (0%)',
            win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW,
            int(round(
                GetSystemMetrics(win32con.SM_CXSCREEN) * .5 - width * .5)),
            int(round(
                GetSystemMetrics(win32con.SM_CYSCREEN) * .5 - height * .5)),
            width,
            height,
            0,
            0,
            self.hinst,
            None)
        self.progbar = CreateWindow(
            #                             win32con.WS_EX_DLGMODALFRAME,
            'msctls_progress32',
            '',
            win32con.WS_VISIBLE | win32con.WS_CHILD,
            10,
            10,
            width - 30,
            20,
            self.dialog,
            0,
            0,
            None)
        if self.can_abort:
            self.button = CreateWindow(
                #                             win32con.WS_EX_DLGMODALFRAME,
                'BUTTON',
                'Cancel',
                win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.BS_PUSHBUTTON,  # @IgnorePep8
                int(width / 2.75),
                40,
                100,
                20,
                self.dialog,
                0,
                0,
                None)
        self.oldWndProc = SetWindowLong(
            self.dialog,
            win32con.GWL_WNDPROC,
            self.DlgProc)
        SendMessage(self.progbar, PBM_SETRANGE, 0, MAKELPARAM(0, self.max_val))
#        win32gui.SendMessage(self.progbar, PBM_SETSTEP, 0, 10)
#        win32gui.SendMessage(self.progbar, PBM_SETMARQUEE, 0, 0)
        ShowWindow(self.progbar, win32con.SW_SHOW)

    def run(self):
        self.BuildWindow()
        self.started = True
        while self.started:
            PumpWaitingMessages()
            if self.do_update:
                SendMessage(self.progbar, PBM_SETPOS,
                            int(self.position % self.max_val), 0)
                percentage = int(round(100.0 * self.position / self.max_val))
                SendMessage(self.dialog, WM_SETTEXT, 0,
                            self.title + ' (%d%%)' % percentage)
    #            SendMessage(self.progbar, PBM_STEPIT, 0, 0)
                self.do_update = False
            sleep(0.1)
        PostQuitMessage(0)
        DestroyWindow(self.dialog)

    def update(self, pos):
        if self.started:
            if not self.do_update:
                self.position = pos
                self.do_update = True
            return True
        return False

    def close(self):
        self.started = False


# class Waitbar2(Dialog, Thread):
#    def __init__(self, title='Waitbar'):
#        template = [[title, (0, 0, 215, 36),
#                     (win32con.DS_MODALFRAME | win32con.WS_POPUP |
#                      win32con.WS_VISIBLE | win32con.WS_CAPTION |
#                      win32con.WS_SYSMENU | win32con.DS_SETFONT |
#                      win32con.WS_GROUP | win32con.WS_EX_TOPMOST),
# | win32con.DS_SYSMODAL),
#                      None, (8, "MS Sans Serif")], ]
#        Dialog.__init__(self, id=template)
# Thread.__init__(self)     # Initialize thread
#        self.started = False
# self.start()                        # Run the thread
#        while not self.started:
# sleep(0.1)                      # Wait until the dialog is ready
#
#    def OnInitDialog(self):
#        rc = Dialog.OnInitDialog(self)
#        self.pbar = CreateProgressCtrl()
#        self.pbar.CreateWindow (win32con.WS_CHILD | win32con.WS_VISIBLE,
#                                (10, 10, 310, 24), self, 1001)
#        self.started = True
#        return rc
#
#    def run(self):
#        self.DoModal()
#
#    def update(self, pos):
#        self.pbar.SetPos(int(pos))
#
#    def close(self):
#        self.OnCancel()


class WarnDlg(Thread):

    def __init__(self, message='', title='Warning!'):
        Thread.__init__(self)     # Initialize thread
        self.title = title
        self.message = message
        self.start()              # Run the thread

    def run(self):
        # MessageBox(self.message, self.title, win32con.MB_ICONWARNING)
        MessageBox(0, self.message, self.title,
                   win32con.MB_ICONWARNING | win32con.MB_SYSTEMMODAL)


class ErrorDlg(Thread):

    def __init__(self, message='', title='Error!', blocking=False):
        Thread.__init__(self)     # Initialize thread
        self.title = title
        self.message = message
        if blocking:
            self.run()              # Run without threading
        else:
            self.start()            # Run in thread

    def run(self):
        # MessageBox(self.message, self.title, win32con.MB_ICONERROR)
        MessageBox(0, self.message, self.title,
                   win32con.MB_ICONERROR | win32con.MB_SYSTEMMODAL)


if __name__ == '__main__':
    WarnDlg('This is an example of a warning', 'Warning!')
    ErrorDlg('This is an example of an error message')
    wb = Waitbar('Waitbar example')
#    wb2 = Waitbar2('Waitbar example')
    for i in xrange(20):
        print wb.update(i * 5)
#        wb2.update(i)
        sleep(0.1)
    wb.close()
#    wb2.close()
