from VideoCapture import Device
import ImageDraw
import sys
import pygame
from pygame.locals import *
from pgu import gui, text

import Image
import time
from math import *

import win32api
import win32con
import win32gui

from ctypes import *

import keymap





PUL = POINTER(c_ulong)
class KeyBdInput(Structure):
    _fields_ = [("wVk", c_ushort),
                ("wScan", c_ushort),
                ("dwFlags", c_ulong),
                ("time", c_ulong),
                ("dwExtraInfo", PUL)]

class HardwareInput(Structure):
    _fields_ = [("uMsg", c_ulong),
                ("wParamL", c_short),
                ("wParamH", c_ushort)]

class MouseInput(Structure):
    _fields_ = [("dx", c_long),
                ("dy", c_long),
                ("mouseData", c_ulong),
                ("dwFlags", c_ulong),
                ("time",c_ulong),
                ("dwExtraInfo", PUL)]
               
class Input_I(Union):
    _fields_ = [("ki", KeyBdInput),
                 ("mi", MouseInput),
                 ("hi", HardwareInput)]

class Input(Structure):
    _fields_ = [("type", c_ulong),
                ("ii", Input_I)]


DOWNRIGHT = 0
UPRIGHT = 90
UPLEFT = 180
DOWNLEFT = 270



class Region:
    def __init__(self, region):
        self.start_x, self.start_y, self.finish_x, self.finish_y = region
        self.error = 5
        self.rect = Rect(self.start_x, self.start_y, self.finish_x, self.finish_y)
    def get(self):
        return (self.start_x,
                self.start_y,
                self.finish_x,
                self.finish_y)            
    def normalize(self):
        if self.start_x <= self.finish_x:
            if self.start_y <= self.finish_y:
                self.rect = Rect(self.start_x, self.start_y, self.finish_x, self.finish_y)
                return 0
            else:
                (self.start_y, self.finish_y) = (self.finish_y, self.start_y)
                self.rect = Rect(self.start_x, self.start_y, self.finish_x, self.finish_y)
                return 90 
        else:
            if self.start_y <= self.finish_y:
                (self.start_x, self.finish_x) = (self.finish_x, self.start_x)
                self.rect = Rect(self.start_x, self.start_y, self.finish_x, self.finish_y)
                return 270
            else:
                (self.start_x, self.finish_x) = (self.finish_x, self.start_x)
                (self.start_y, self.finish_y) = (self.finish_y, self.start_y)
                self.rect = Rect(self.start_x, self.start_y, self.finish_x, self.finish_y)
                return 180
            
    def movepoint(self, pointID, diff):
        (diff_x, diff_y) = diff
        
        if pointID == UPLEFT:
            self.start_x += diff_x
            self.start_y += diff_y
        if pointID == UPRIGHT:
            self.finish_x += diff_x
            self.start_y += diff_y
        if pointID == DOWNRIGHT:
            self.finish_x += diff_x
            self.finish_y += diff_y
        if pointID == DOWNLEFT:
            self.start_x += diff_x
            self.finish_y += diff_y
            
        rotation = self.normalize()
        if pointID == rotation:
            return 0
        else:
            tmp = pointID + rotation
            if tmp <= 270:
                return tmp
            else:
                return 540 - tmp

    def is_inside(self, point):
        (x, y) = point
        return (self.start_x < x) and (x < self.finish_x) and (self.start_y < y) and (y < self.finish_y)

    def move(self, diff):
        (diff_x, diff_y) = diff
        self.start_x += diff_x
        self.start_y += diff_y
        self.finish_x += diff_x
        self.finish_y += diff_y
        self.rect = Rect(self.start_x, self.start_y, self.finish_x, self.finish_y)

    
    def points(self, point):
        (x, y) = point        
        if self.start_x - self.error < x and x < self.start_x + self.error:
            if self.start_y - self.error < y and y < self.start_y + self.error:
                return UPLEFT
            elif self.finish_y - self.error < y and y < self.finish_y + self.error:
                return DOWNLEFT
            else:
                return None
        elif self.finish_x - self.error < x and x < self.finish_x + self.error:
            if self.start_y - self.error < y and y < self.start_y + self.error:
                return UPRIGHT
            elif self.finish_y - self.error < y and y < self.finish_y + self.error:
                return DOWNRIGHT
            else:
                return None
        else:
            return None


            
class Button:
    def __init__(self, region, image, virtualkey, scancode):
        self.region = Region(region)
        self.image = image
        self.virtualkey = virtualkey
        self.scancode = scancode
        self.pressed = False
        self.positive = True



def diff_image(img1, img2, pix_threshold=50, img_threshold=10, gap=0.1):
    if not img1 or not img2:
        return False
    img1 = img1.getdata()
    img2 = img2.getdata()
    
    pixel_count = len(img1)
    pixel_count_sqrt = int(sqrt(pixel_count))
    step = int(pixel_count_sqrt * gap)
    pixdiff = 0   

    index = 0    
    for i in range(int(1 / gap)):
        for j in range(int(1 / gap)):
            #print index
            if abs(sum(img1[index]) - sum(img2[index])) > pix_threshold:
                pixdiff += 1
            index += step
        index += pixel_count_sqrt * (step - 1)

    #print 100 * pixdiff, img_threshold * pixel_count * gap * gap
    if 100 * pixdiff > img_threshold * pixel_count * gap * gap:
        return True
    else:
        return False

    

def sendkey(scancode, pressed):

    if scancode == None:
        return
    
    InputBox[0].ii.ki.wScan = scancode

    InputBox[0].ii.ki.dwFlags = 0x8  # KEY_SCANCODE  
    if not(pressed):
        InputBox[0].ii.ki.dwFlags |= 0x2 # released

    windll.user32.SendInput(1, pointer(InputBox), sizeof(InputBox[0]))
    
    if pressed:
        print "%x pressed" % scancode
    else:
        print "%x released" % scancode

        

        
def checkbutton(camshot, draw, button, nowplaying):

    if nowplaying:
        color = (255, 0, 0)
    else:
        color = (0, 0, 255)

    #print button.region.get()    
    new_button = camshot.crop(button.region.get())
    if (state == ISMODIFYING and button == currentButton) or (button.positive == diff_image(button.image, new_button)):

#	hwnd = win32gui.FindWindow('Notepad', None)
#        hwnd = win32gui.FindWindowEx( hwnd, 0, 'Edit', None)
#        win32api.PostMessage(hwnd, win32con.WM_CHAR, 65, 0)

#        hwnd = win32gui.FindWindow(None, 'StepMania')
#        win32gui.SetForegroundWindow(hwnd);
#        win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_ESCAPE, 0)
#        win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_ESCAPE, 0)

        if not(button.pressed):
            if nowplaying:
                sendkey(button.scancode, True)
            button.pressed = True
        draw.rectangle(button.region.get(), fill=color)
    else:
        if button.pressed:
            if nowplaying:
                sendkey(button.scancode, False)
            button.pressed = False
        draw.rectangle(button.region.get(), outline=color)
        

        

def loop(camshot, nowplaying):
        
    
    #camshot = camshot.transpose(Image.FLIP_LEFT_RIGHT)
    draw = ImageDraw.Draw(camshot)

    for x in button:
        checkbutton(camshot, draw, x, nowplaying)

    camshot = pygame.image.fromstring(camshot.tostring(), (320,240), "RGB")

    screen.blit(camshot, (0,0))
    app.paint(screen)

    for i in button:
        
        if i.positive:
            buttonText = str(i.virtualkey) + "\npositive"
        else:
            buttonText = str(i.virtualkey) + "\nnegative"
        text.writepre(screen, smallfont, i.region.rect, (0, 0, 0), buttonText)
        
    pygame.display.flip()


def showmessage(textContent):
    screen.fill((0, 0, 0), textArea)
    text.writepre(screen, font, textArea, (255,255,255), textContent)     
        

        

# init

#pygame.init()zxcvxz
size = width, height = 320, 300
screen = pygame.display.set_mode(size)

pygame.font.init()
font = pygame.font.SysFont("default", 24)
smallfont = pygame.font.SysFont("default", 18)

app = gui.App()
form = gui.Form()
c = gui.Container(align=-1,valign=-1)
#t = StarControl()
e1 = gui.Label("Now playing: ", color=(255, 255, 255))
c.add(e1, 0, 240)
e2 = gui.Switch(value=False,name='nowplaying')
c.add(e2,110,240)

textArea = pygame.Rect(0, 260, 320, 280)
textContent = ""

app.init(c)


# get cam device
cam = Device()
camshot = cam.getImage()
#camshot = camshot.transpose(Image.FLIP_LEFT_RIGHT)


button = []


FInputs = Input * 1
extra = c_ulong(0)
ii_ = Input_I()
flag = 0x8 # KEY_SCANCODE
ii_.ki = KeyBdInput( 0, 0, flag, 0, pointer(extra) )
InputBox = FInputs( ( 1, ii_ ) )          


FREE = 0
ISCREATING = 1
ISBINDING = 2
ISMOVING = 3
ISRESIZING = 4
ISMODIFYING = 5

state = FREE

pointing = None
currentButton = None
onceMoved = False


clock = pygame.time.Clock()
while 1:
    #print onceMoved
    for event in pygame.event.get(pygame.QUIT):
        sys.exit()

    camshot = cam.getImage()
        
    if form['nowplaying'].value:
        for event in pygame.event.get():
            app.event(event)
        loop(camshot, form['nowplaying'].value)
    else:
        for event in pygame.event.get():
            #print event
            if event.type == pygame.KEYUP:
                if state == ISBINDING:
                    #print keymap.keymap[event.key]
                    (currentButton.virtualkey, currentButton.scancode) = keymap.keymap[event.key]
                    if currentButton.scancode == 0:
                        showmessage("specify key binding: Unsupported key (Try another)\n")
                    else:
                        showmessage("specify key binding: " + currentButton.virtualkey + " (Registered)\n")
                        currentButton = None
                        state = FREE
            if event.type ==  pygame.MOUSEMOTION:
                (left, central, right) = event.buttons
                if left == 1:
                    if state == ISCREATING or state == ISRESIZING:
                        pointing = currentButton.region.movepoint(pointing, event.rel)
                    elif state == ISMOVING:
                        currentButton.region.move(event.rel)
                        onceMoved = True
                elif right == 1:
                    if state == ISMODIFYING:
                        orig_x, orig_y = orig_pos
                        current_x, current_y = event.pos
                        if abs(orig_x - current_x) > 2 or abs(orig_y - current_y) > 2:
                            onceMoved = True
            elif event.type ==  pygame.MOUSEBUTTONDOWN:
                x, y = event.pos
                if state == FREE:
                    
                    if event.button == 1: #left
                        found = False
                        for i in button:
                            p = i.region.points((x, y))
                            if p != None:
                                state = ISRESIZING
                                i.image = None
                                currentButton = i
                                pointing = p
                                found = True
                                break
                            if i.region.is_inside((x, y)):
                                state = ISMOVING
                                onceMoved = False
                                currentButton = i
                                found = True
                                break

                        if not(found):
                            currentButton = Button((x, y, x, y), None, None, None)
                            button = [currentButton] + button
                            screen.fill((0, 0, 0), textArea)
                            state = ISCREATING
                            pointing = DOWNRIGHT
                            
                    elif event.button == 3: #right
                        for i in button:
                            if i.region.is_inside((x, y)):
                                state = ISMODIFYING
                                orig_pos = event.pos
                                onceMoved = False
                                currentButton = i
                                break                        
                        
            elif event.type ==  pygame.MOUSEBUTTONUP:
                x, y = event.pos
                if event.button == 1: #left
                    if state == ISCREATING:
                        if currentButton.region.start_x == currentButton.region.finish_x or currentButton.region.start_y == currentButton.region.finish_y:
                            button.remove(currentButton)
                            state = FREE
                            pointing = None
                            currentButton = None
                        else:
                            currentButton.image = camshot.crop(currentButton.region.get())
                            showmessage("specify key binding: \n")
                            state = ISBINDING
                            pointing = None
                    if state == ISRESIZING:
                        if currentButton.region.start_x == currentButton.region.finish_x or currentButton.region.start_y == currentButton.region.finish_y:
                            button.remove(currentButton)
                        else:
                            currentButton.image = camshot.crop(currentButton.region.get())
                        state = FREE
                        pointing = None
                        currentButton = None
                    elif state == ISMOVING:
                        if not(onceMoved):
                            currentButton.positive = not(currentButton.positive)
                            #print currentButton.positive
                        currentButton.image = camshot.crop(currentButton.region.get())
                        onceMoved = False
                        currentButton = None
                        state = FREE
                elif event.button == 3: #right
                    if state == ISMODIFYING:
                        if(onceMoved):
                            button.remove(currentButton)
                            onceMoved = False
                            currentButton = None
                            state = FREE
                        else:
                            showmessage("specify key binding: \n")
                            state = ISBINDING
            app.event(event)

        loop(camshot, form['nowplaying'].value)
    clock.tick(30)









