from tidal import *
from app import TextApp
from math import sqrt
from random import choice, randrange

from st7789_passthrough import tidal_display
import st7789

import accelerometer

BLOCK = 0
LEFT = 1
RIGHT = 2
UP = 3
DOWN = 4

MOVEMENTS = [LEFT, RIGHT, UP, DOWN]

def mean(r):
    return sum(r) / len(r)

def speed(hist):
    return mean(hist[:10]) - mean(hist[10:])

def random_game():
    result = {4: UP}
    for i in range(5,50):
        if randrange(5) == 1:
            result[i] = choice(MOVEMENTS)
    return result


class Move(TextApp):
    TITLE = "Top of screen"
    BG = BLACK # The background colour
    FG = WHITE
    arrow_down = [(-5,-20),(-5,5),(-15,5),(0,15),(15,5),(5,5),(5,-20)]
    arrow_up = [(x,-y) for (x,y) in arrow_down]
    arrow_left = [(-y,x) for (x,y) in arrow_down]
    arrow_right = [(y,x) for (x,y) in arrow_down]
    block = [(20,0),(0,20),(-20,0),(0,-20)]

    def on_activate(self):
        super().on_activate()
        # Moving averages for determining moves
        self.x_slow = 0.0
        self.y_slow = 0.0
        self.z_slow = 0.0
        self.x_fast = 0.0
        self.y_fast = 0.0
        self.z_fast = 0.0

        # set up game
        self.getmove = self.periodic(50, self.checkmove)
        self.dodraw = self.periodic(300, self.draw)
        self.draw_next = BLOCK
        self.draw_last = -1
        self.score = 0

        # timing variables
        self.count = 0
        self.count_scale = 10 # the count goes up when we call checkmove. How many counts to 1 game in the dance scale?
        self.dance = random_game()

        #Closing and reseting
        self.buttons.on_press(BUTTON_A, self.reset)

    def on_deactivate(self):
        super().on_deactivate()
        self.getmove.cancel()
        self.dodraw.cancel()

    def reset(self):
        self.count = 0
        self.dance = random_game()
        self.score = 0


    def checkmove(self):
        self.count = self.count + 1
        (x,y,z) = accelerometer.get_xyz()

        if x == 0:
            # Sometimes returns 0 if I query to fast
            return

        self.x_slow = self.x_slow * 0.8 + x * 0.2
        self.y_slow = self.y_slow * 0.8 + y * 0.2
        self.z_slow = self.z_slow * 0.8 + z * 0.2
        self.x_fast = self.x_fast * 0.3 + x * 0.7
        self.y_fast = self.y_fast * 0.3 + y * 0.7
        self.z_fast = self.z_fast * 0.3 + z * 0.7

        x_speed = self.x_fast - self.x_slow
        y_speed = self.y_fast - self.y_slow

        if abs(x_speed) > abs(y_speed):
            if x_speed > 0.1:
                if self.draw_last == BLOCK:
                    self.draw_next = LEFT
                    self.check_add_score()
            elif x_speed < -0.1:
                if self.draw_last == BLOCK:
                    self.draw_next = RIGHT
                    self.check_add_score()
            else:
                self.draw_next = BLOCK
        else:
            if y_speed > 0.1:
                if self.draw_last == BLOCK:
                    self.draw_next = DOWN
                    self.check_add_score()
            elif y_speed < -0.1:
                if self.draw_last == BLOCK:
                    self.draw_next = UP
                    self.check_add_score()
            else:
                self.draw_next = BLOCK

    def check_add_score(self):
        times_offset = sorted([(abs(k*self.count_scale - self.count),k) for k in self.dance])
        (closest_time, dance_key) = times_offset[0]

        if self.draw_next == self.dance[dance_key]:
            if closest_time < 4:
                self.dance.pop(dance_key) # remove from list
                self.score += 1
            if closest_time < 2:
                self.score += 4
            if closest_time == 0:
                self.score += 5

    def draw(self):
        self.window.redraw()
        self.window.println(str(self.score))
        self.draw_current()
        self.draw_dots()
        self.draw_instruction()

    def draw_instruction(self):
        h = tidal_display.height()
        w = tidal_display.width()
        b = 5 # pixels from bottom for finish
        h1 = h - b

        # draw the "finish line"
        tidal_display.hline(w-10,h1,10,st7789.WHITE)

        next_moves = sorted([k for k in self.dance if k > self.count//self.count_scale])
        if len(next_moves) == 0:
            self.window.println(f"Final score: {self.score}")
            self.window.println("press A to\nrestart")

        for move_t in next_moves:
            pixels_up = move_t*self.count_scale-self.count
            if pixels_up < h:
                #draw the arrow
                tidal_display.hline(w-20,h1-pixels_up,10,st7789.YELLOW)
                next_move = self.dance[move_t]
                poly = Move.block
                if next_move == LEFT:
                    poly = Move.arrow_left
                if next_move == RIGHT:
                    poly = Move.arrow_right
                if next_move == UP:
                    poly = Move.arrow_up
                if next_move == DOWN:
                    poly = Move.arrow_down

                tidal_display.fill_polygon(poly, w//3, h1-pixels_up, st7789.GREEN)

    def draw_current(self):
        # draw the arrow for "currently measured"
        dn = self.draw_next
        self.draw_last = dn
        if dn == BLOCK:
            self.draw_poly(Move.block, small=True)
        if dn == LEFT:
            self.draw_poly(Move.arrow_left, small=True)
        if dn == RIGHT:
            self.draw_poly(Move.arrow_right, small=True)
        if dn == UP:
            self.draw_poly(Move.arrow_up, small=True)
        if dn == DOWN:
            self.draw_poly(Move.arrow_down, small=True)

    def draw_dots(self):
        y = tidal_display.height() // 5
        x = (tidal_display.width()*4) // 5
        count = self.count//self.count_scale
        color = st7789.BLUE

        if count % 2 == 0:
            color = st7789.RED

        tidal_display.circle(x,y,2,color)
        tidal_display.circle(x,y,3,color)


    def draw_poly(self, poly, small = False, color = st7789.RED):
        p = poly.copy()
        x = tidal_display.width()//2
        y = tidal_display.height()//2
        if small:
            p = [(x//2,y//2) for (x,y) in p]
            x = tidal_display.width()//5
            y = tidal_display.height()//5
        tidal_display.fill_polygon(
            p,
            x,
            y,
            st7789.RED)

main = Move
