'''
Created on Nov 10, 2012

@author: attero
'''


import random, pygame, sys
import subprocess
import flask
import threading
import os
import signal
import json
import traceback
from pygame.locals import *
from Cannon import HackWAWCannonController
from Cannon import Gunpoint
import pygame.camera

app = flask.Flask(__name__)
app.debug = True
pygame.init()
pygame.camera.init()

CAMERA_X = 400
CAMERA_Y = 30

cannon = None

def run_detection():
    p = subprocess.Popen("/home/q3k/balloontracker/facedetect", stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    p.stdin.write("\n")
    p.stdin.write("\n")
    p.stdin.write("\n")
    p.stdin.write("\n")
    p.stdin.flush()
    print("L: " + p.stdout.readline())
    print("L: " + p.stdout.readline())
    print("L: " + p.stdout.readline())
    data = p.communicate()[0]
    lines = data.split("\n")
    print lines
    x, y = [int(i) for i in lines[-2].split(",")]
    global dot_pos
    dot_pos = (x, y)

@app.route("/left/<int:amount>", methods=["POST"])
def move_left(amount):
    cannon.move_left(amount)
    cannon.send_data()
    return "OK"

@app.route("/right/<int:amount>", methods=["POST"])
def move_right(amount):
    cannon.move_right(amount)
    cannon.send_data()
    return "OK"

@app.route("/up/<int:amount>", methods=["POST"])
def move_up(amount):
    cannon.move_up(amount)
    cannon.send_data()
    return "OK"

@app.route("/down/<int:amount>", methods=["POST"])
def move_down(amount):
    cannon.move_down(amount)
    cannon.send_data()
    return "OK"

@app.route("/fire", methods=["POST"])
def fire():
    cannon.fire()
    cannon.send_data()

@app.route("/stop_fire/", methods=["POST"])
def stop_fire():
    pass

@app.route("/status")
def status():
    return json.dumps(cannon.get_data())

@app.route("/")
def root():
    return flask.render_template("status.html", data=cannon.get_data())

dot = None
dot_pos = None

#Camera stuff

try:
    camera = pygame.camera.Camera("/dev/video0", (640, 480))
    camera.start()
except:
    camera = None


def main():
    #create the screen
    window = pygame.display.set_mode((1200, 800))
    global dot
    dot = pygame.image.load("crosshair.png").convert_alpha()
    dot = pygame.transform.scale(dot, (25, 25))

    GRAY = ( 182, 182, 182)
    VIOLET = (150, 100, 190)
    RED = (150, 0, 0)
    GREEN = (0, 150, 0)
    BLUE = (30, 30, 180)
    VERYLIGHT = (210, 210, 210)

    colors = [GRAY, VIOLET, RED, GREEN, BLUE, VERYLIGHT]

    global cannon
    cannon = HackWAWCannonController()


    screen = Screen(window, colors)
    screen.draw_surface(cannon)

    gunpoint = Gunpoint((5, 4), (200, 4), (5, 210))


    keep_running = True
    while keep_running:
        screen.draw_surface(cannon)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keep_running = False
            elif event.type == KEYUP :
                if event.key == K_SPACE:
                    cannon.fire()
                elif event.key == K_RIGHT:
                    cannon.move_right(20)
                elif event.key == K_LEFT:
                    cannon.move_left(20)
                elif event.key == K_UP:
                    cannon.move_up(20)
                elif  event.key == K_DOWN:
                    cannon.move_down(20)
                elif event.key == K_l:
                    if cannon.laser:
                        cannon.disable_laser()
                        if "Laser enabled" in screen.communicates :
                            screen.communicates.remove("Laser enabled")
                        screen.communicates.append("Laser disabled")
                    else:
                        cannon.enable_laser()
                        if "Laser disabled" in screen.communicates :
                            screen.communicates.remove("Laser disabled")
                        screen.communicates.append("Laser enabled")
                elif event.key == K_c:
                    point1 = get_calibration_point(window, "Point1")
                    point2 = get_calibration_point(window, "Point2")
                    point3 = get_calibration_point(window, "Point3")
                    print point1, point2, point3
                    gunpoint.calibrate(point1, point2, point3)
                    print gunpoint.horizontal_angle, gunpoint.vertical_angle
                    screen.communicates.append("Calibrated")
                elif event.key == K_d:
                    global camera
                    startcam = False
                    if camera:
                        camera.stop()
                        run_detection()
                        startcam = True
                    camera = pygame.camera.Camera("/dev/video0", (640, 480))
                    if startcam:
                        camera.start()
                cannon.send_data()

            elif event.type == MOUSEBUTTONUP:
                mousex, mousey = pygame.mouse.get_pos()
                print mousex, mousey
                x =  (mousex  - CAMERA_X)/640.0
                y = (CAMERA_Y + 480 - mousey)/480.0
                print x, y
                position = gunpoint.aim((x, y))
                print position
                cannon.position = position
                screen.change_color()
    print "Pygame thread exited."
    os.kill(os.getpid(), signal.SIGINT)



def get_key():
    while 1:
        event = pygame.event.poll()
        if event.type == KEYDOWN:
            return event.key
        else:
            pass

def display_box(screen, message, xx, yy):
    "Print a message in a box on xx, yy"
    font_object = pygame.font.Font(None,26)
    pygame.draw.rect(screen, (222,222,222),(xx, yy, 300,30), 0)
    pygame.draw.rect(screen, (20,20,20),
                    (xx -2, yy-2, 302,34), 1)

    if len(message) != 0:
        screen.blit(font_object.render(message, 3, (0,0,0)),
                (xx + 6, yy + 6) )

    pygame.display.flip()

def ask(screen, question):
    "ask(screen, question) -> answer"
    pygame.font.init()
    current_string = []
    display_box(screen, question + ": " + "".join(current_string), 20, 400)
    display_box(screen, "Write calibration points below", 20, 360)
    while 1:
        inkey = get_key()
        if inkey == K_BACKSPACE:
            current_string = current_string[0:-1]
        elif inkey == K_RETURN:
            break
        elif inkey == K_MINUS:
            current_string.append("_")
        elif inkey <= 127:
            current_string.append(chr(inkey))
        display_box(screen, "Write calibration points below", 20, 360)
        display_box(screen, question + ": " + "".join(current_string), 20, 400)
    return "".join(current_string)


def get_calibration_point(screen, message):
    point_string = ask(screen, message)
    while(not (is_valid_point(point_string)) ):
        point_string = ask(screen, "Again, " + message)
    return [float(x) for x in point_string.split(",")]

def is_valid_point(string):
    point = string.split(',')
    if (len(point) != 2):
        return False
    if((not point[0].isdigit()) or (not point[1].isdigit())):
        return False
    if(float(point[0]) > 255 or float(point[1]) > 255 or float(point[0])< 0 or float(point[1]) <0 ):
        return False
    return True

class Screen:

    def __init__(self, window, colors):
        self.communicates = []
        self.communicates.append("Press space to fire!")
        self.communicates.append("Use arrows to adjust position")
        self.communicates.append("Press C for calibration")
        self.communicates.append("Press L to enable laser")
        self.window = window
        self.colors = colors
        self.color = random.choice(self.colors)

    def change_color(self):
        self.color = random.choice(self.colors)

    def draw_surface(self,  cannon):
        self.window.fill(self.color)
        for i, text in enumerate(self.communicates):
            self.print_text(text, 20, 20 + i*20, (0, 0, 0), 30, self.window)

        self.print_text([str(x) for x in cannon.get_data()].__str__(), 20, 300, (0, 0, 0), 30, self.window)
        if camera:
            image = camera.get_image()
            self.window.blit(image, (CAMERA_X, CAMERA_Y))

        global dot
        if dot_pos:
            self.window.blit(dot, (dot_pos[0] - 12 + CAMERA_X, dot_pos[1] -  12 + CAMERA_Y))

        pygame.display.flip()

    def print_text(self, text,xx,yy,color,text_size, screen):
        font = pygame.font.SysFont(None,text_size)
        ren = font.render(text,1,color)
        screen.blit(ren, (xx,yy))



if __name__ == '__main__':
    def pygame_runner():
        try:
            main()
        except Exception as e:
            traceback.print_exc()
            os.kill(os.getpid(), signal.SIGINT)
    t = threading.Thread(target=pygame_runner)

    def flask_runner():
        try:
            app.run(use_reloader=False, host="0.0.0.0")
        except Exception as e:
            print "Flask thread raised exception %s" % str(e)
            os.kill(os.getpid(), signal.SIGINT)
    t2 = threading.Thread(target=flask_runner)

    print "Starting pygame..."
    t.start()
    print "Starting flask..."
    t2.start()
