import pygame, sys, color
import bike as b
import square as s
import color as c
import powerUps as pu
import json

import pygame
import math
import random
import datetime
from sys import exit

# import tron
from pygame.locals import *

pygame.init()

screen_width=800
screen_height=600

screen=pygame.display.set_mode((screen_width, screen_height))

def text_render(message, textFont, textSize, textColor):
    newFont = pygame.font.Font(textFont, textSize)
    newText = newFont.render(message, 0, textColor)
    return newText

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
ORANGE = (247, 148, 29)
YELLOW = (255, 242, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PURPLE = (102, 45, 145)
GRAY = (128,128,128)

#Font
font = "Assets/TRON.TTF"
timer_font = "Assets/clock.TTF"
arcade_font = "Assets/ARCADE_N.TTF"

#Framerate
clock = pygame.time.Clock()
FPS=30

grid_cell_scl = 20  # width & height (scale) of each grid cell
grid_margin = 1  # amount of space on all sides of cells (must be odd for pygame line drawing)
grid_width = 40  # grid width cell count
grid_height = 32  # grid height cell count

screen_width = ((grid_margin + grid_cell_scl) * grid_width) + grid_margin  # width of the GUI window
screen_height = ((grid_margin + grid_cell_scl) * grid_height) + grid_margin  # height of the GUI window

CLOCK_SPD = 100  # the base clock speed, or arbitrary framerate - keep at 100
current_spd = CLOCK_SPD  # the current speed of the game (may change)
speed_timer = 0  # used to regulate when the current speed is changed
slow_timer = 0 # used to regulate when the user slows their bike
duration_timer = 0 # Timer used when the speed is activated - lasts for 5 seconds
paused = False # Boolean for when the game is paused or not
game_modes = {"1 V 1" : 2, "2 V 2" : 4, "3 V 1" : 4, "Free For All" : 4} # Dictionary for game modes
menu=True #Menu boolean that is set whenever the user is on the main menu
mode_menu=True #Mode menu boolean that is set whenever the user is on the game mode menu
leaderboard=True #leaderboard menu
pause_song=False #Pauses the song when true

# decide on colors
bike_color = c.YELLOW
powerup_color = c.RED

GRID_BG = c.BLACK
GRID_FG = c.GRID_BLUE

# initialize pygame module
# def initialize():

# plays the music
pygame.mixer.music.load('Assets/theme.mp3')
pygame.mixer.music.play(-1)

screen = pygame.display.set_mode([screen_width, screen_height], pygame.NOFRAME)

pygame.display.set_caption('Prototype II')

first = True
pygame.time.set_timer(USEREVENT+1, 1000)

powerups = []

def save_data(highscores, names, file):
    data = {}
    data['leaderboard'] = []
    data['leaderboard'].append({
        'names': names,
        'highscores': highscores

    })

    with open(file, 'w') as outfile:
        json.dump(data, outfile)

def load_data(file):
    with open(file) as json_file:
        data = json.load(json_file)
        global names
        global highscores
        for p in data['leaderboard']:
            names = p['names']
            highscores = p['highscores']

#takes in the time, bikes and when they died and prints to the topbar
def timer(time, timerbikes, finalTimes):
    bikesleft = len(timerbikes)
    posistion = 40 / (bikesleft + 1)
    for bike in timerbikes:
        if(bike.alive):
            screen.blit(text_render(str(time), timer_font, 40, bike.color), (((posistion * bikesleft) * (grid_cell_scl + grid_margin)), 0))
            bikesleft -= 1
        else:
            if(finalTimes[bikesleft - 1] == 0):
                finalTimes[bikesleft - 1] = time
            screen.blit(text_render(str(finalTimes[bikesleft - 1]), timer_font, 40, bike.color), (((posistion * bikesleft) * (grid_cell_scl + grid_margin)), 0))
            bikesleft -= 1

# draw the background, grid, and squares
def draw(endState, inTeams):
    # erase everything
    screen.fill(GRID_BG)

    # grid x lines (vertical)
    for i in range(grid_width + 1):
        pygame.draw.line(screen, GRID_FG, (grid_margin / 2 + (i * (grid_cell_scl + grid_margin)), 0),
                         (grid_margin / 2 + (i * (grid_cell_scl + grid_margin)), screen_height), grid_margin)

    # grid y lines (horizontal)
    for i in range(grid_height + 1):
        pygame.draw.line(screen, GRID_FG, (0, grid_margin/2 + (i * (grid_cell_scl + grid_margin))),
                         (screen_width, grid_margin / 2 + (i * (grid_cell_scl + grid_margin))), grid_margin)

    # bike glow
    for bike in bikes:
        bike.draw_glow(screen)

    # bike squares
    for bike in bikes:
        bike.draw(screen)

    # powerup
    for powerup in powerups:
        for bike in bikes:
            # Stops the power ups from spawning on a line.
            # Removes the power up from the list so it does not spawn the second a player dies.
            if not bike.overlaps(powerup):
                pygame.draw.rect(screen, powerup.color, powerup.to_rect())
            else:
                powerups.remove(powerup)

    # draw the top bar and the timer
    screen.fill(GRAY, (0, 0, grid_cell_scl * (grid_width + 2), grid_cell_scl * 2 + 2))
    timer(time, timerbikes, finalTimes)

    if(endState):
        screen.fill(GRAY, (screen_width/6, screen_height/3, 2*screen_width/3, screen_height/3))
        if(inTeams == 2 or inTeams == 3):
            if(timerbikes[0].color == bikes[0].color):
                winner_text = text_render("Team 1 Wins", arcade_font, 40, bikes[0].color)
            else:
                winner_text = text_render("Team 2 Wins", arcade_font, 40, bikes[0].color)
        else:
            if(timerbikes[0].left_key == bikes[0].left_key):
                winner_text = text_render("Player 1 Wins", arcade_font, 40, bikes[0].color)
            elif(timerbikes[1].left_key == bikes[0].left_key):
                winner_text = text_render("Player 2 Wins", arcade_font, 40, bikes[0].color)
            elif(timerbikes[2].left_key == bikes[0].left_key):
                winner_text = text_render("Player 3 Wins", arcade_font, 40, bikes[0].color)
            elif(timerbikes[3].left_key == bikes[0].left_key):
                winner_text = text_render("Player 4 Wins", arcade_font, 40, bikes[0].color)
        continue_text = text_render("Press Enter/Return to continue", arcade_font, 15, GRID_FG)
        winner_rect = winner_text.get_rect()
        continue_rect = continue_text.get_rect()
        screen.blit(winner_text, (screen_width/2 - (winner_rect[2]/2), 3*screen_height/7))
        screen.blit(continue_text, (screen_width/2 - (continue_rect[2]/2), 3*screen_height/5))
    # flip the screen (? not sure why needed ?)
    pygame.display.flip()

# instantiate a bike object
def generate_bikes(gamemode):
    global bikes
    global powerups
    if(gamemode == 1):
        bikes = [b.Bike(0, (grid_cell_scl * 2) + 2, b.Bike.Direction.RIGHT, c.PLAYER1, pygame.K_q, pygame.K_w, pygame.K_e),
                b.Bike(screen_width - b.Bike.WEIGHT, screen_height - b.Bike.WEIGHT, b.Bike.Direction.LEFT, c.PLAYER4, pygame.K_i, pygame.K_o, pygame.K_p)]
    if(gamemode == 2):
        bikes = [b.Bike(0, (grid_cell_scl * 2) + 2, b.Bike.Direction.RIGHT, c.PLAYER1, pygame.K_q, pygame.K_w, pygame.K_e),
                b.Bike(screen_width - b.Bike.WEIGHT, grid_cell_scl * 2, b.Bike.Direction.DOWN, c.PLAYER1, pygame.K_i, pygame.K_o, pygame.K_p),
                b.Bike(0, screen_height - b.Bike.WEIGHT, b.Bike.Direction.UP, c.PLAYER4, pygame.K_z, pygame.K_x, pygame.K_c),
                b.Bike(screen_width - b.Bike.WEIGHT, screen_height - b.Bike.WEIGHT, b.Bike.Direction.LEFT, c.PLAYER4, pygame.K_LEFT, pygame.K_DOWN, pygame.K_RIGHT)]
    if(gamemode == 3):
        bikes = [b.Bike(0, (grid_cell_scl * 2) + 2, b.Bike.Direction.RIGHT, c.PLAYER1, pygame.K_q, pygame.K_w, pygame.K_e),
                b.Bike(screen_width - b.Bike.WEIGHT, grid_cell_scl * 2, b.Bike.Direction.DOWN, c.PLAYER1, pygame.K_i, pygame.K_o, pygame.K_p),
                b.Bike(0, screen_height - b.Bike.WEIGHT, b.Bike.Direction.UP, c.PLAYER1, pygame.K_z, pygame.K_x, pygame.K_c),
                b.Bike(screen_width - b.Bike.WEIGHT, screen_height - b.Bike.WEIGHT, b.Bike.Direction.LEFT, c.PLAYER4, pygame.K_LEFT, pygame.K_DOWN, pygame.K_RIGHT)]
    if(gamemode == 4):
        bikes = [b.Bike(0, (grid_cell_scl * 2) + 2, b.Bike.Direction.RIGHT, c.PLAYER1, pygame.K_q, pygame.K_w, pygame.K_e),
                b.Bike(screen_width - b.Bike.WEIGHT, grid_cell_scl * 2, b.Bike.Direction.DOWN, c.PLAYER2, pygame.K_i, pygame.K_o, pygame.K_p),
                b.Bike(0, screen_height - b.Bike.WEIGHT, b.Bike.Direction.UP, c.PLAYER3, pygame.K_z, pygame.K_x, pygame.K_c),
                b.Bike(screen_width - b.Bike.WEIGHT, screen_height - b.Bike.WEIGHT, b.Bike.Direction.LEFT, c.PLAYER4, pygame.K_LEFT, pygame.K_DOWN, pygame.K_RIGHT)]

    powerups.clear()

# Random number decides which power up is first
decidesStartingPowerUp = random.randint(0, 3)
if (decidesStartingPowerUp == 1):
    startingPowerUp = pu.PowerUps.Type.SPEED
elif (decidesStartingPowerUp == 2):
    startingPowerUp = pu.PowerUps.Type.MINE
elif (decidesStartingPowerUp == 3):
    startingPowerUp = pu.PowerUps.Type.PHASE
else:
    startingPowerUp = pu.PowerUps.Type.NUKE

powerups = [pu.PowerUps(screen_width, screen_height, startingPowerUp)]

# start the clock (frames)
clock = pygame.time.Clock()

def main_menu():
    selected="start"
    global menu
    global pause_song

    while menu:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.type == pygame.QUIT:
                    pygame.quit()
                    exit()
                if event.key == pygame.K_UP:
                    selected = "start"
                elif event.key == pygame.K_DOWN:
                    selected = "quit"
                if event.key == pygame.K_m:
                    pause_song = not pause_song
                if event.key == pygame.K_RETURN:
                    if selected == "start":
                        game_mode_menu()
                    if selected == "quit":
                        exit()
        if pause_song:
            pygame.mixer.pause()
        else:
            pygame.mixer.unpause()

        screen.fill(BLACK)
        title = text_render("TRON", font, 90, GRID_FG)
        if selected == "start":
            start_text = text_render("> START <", arcade_font, 50, YELLOW)
        else:
            start_text = text_render("START", arcade_font, 50, WHITE)
        if selected == "quit":
            quit_text = text_render("> QUIT <", arcade_font, 50, YELLOW)
        else:
            quit_text = text_render("QUIT", arcade_font, 50, WHITE)

        title_rect = title.get_rect()
        start_rect = start_text.get_rect()
        quit_rect = quit_text.get_rect()

        # Main Menu Text
        screen.blit(title, (screen_width/2 - (title_rect[2]/2), 80))
        screen.blit(start_text, (screen_width/2 - (start_rect[2]/2), 400))
        screen.blit(quit_text, (screen_width/2 - (quit_rect[2]/2), 480))
        pygame.display.update()
        clock.tick(FPS)
        pygame.display.set_caption("Main Menu")

def game_mode_menu():
    selected = "1 V 1"
    global inTeams
    global mode_menu
    #Esc key brings you back to main menu - Chris work on

    while mode_menu:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.type == pygame.QUIT:
                    mode_menu = False
                    pygame.quit()
                    exit()
                if event.key == pygame.K_ESCAPE:
                    main_menu()
                if event.key == pygame.K_UP:
                    selected = "1 V 1"
                elif event.key == pygame.K_RIGHT:
                    selected = "2 V 2"
                elif event.key == pygame.K_DOWN:
                    selected = "Free For All"
                elif event.key == pygame.K_LEFT:
                    selected = "3 V 1"
                if event.key == pygame.K_RETURN:
                    if selected == "1 V 1":
                        generate_bikes(1)
                        inTeams = 1
                        tutorial_menu()
                    if selected == "2 V 2":
                        generate_bikes(2)
                        inTeams = 2
                        tutorial_menu()
                    if selected == "3 V 1":
                        generate_bikes(3)
                        inTeams = 3
                        tutorial_menu()
                    if selected == "Free For All":
                        generate_bikes(4)
                        inTeams = 4
                        tutorial_menu()

        screen.fill(BLACK)
        title = text_render("Game Modes", font, 75, GRID_FG)
        if selected == "1 V 1":
            one_text = text_render("> 1 V 1 <", arcade_font, 28, YELLOW)
        else:
            one_text = text_render("1 V 1", arcade_font, 28, WHITE)
        if selected == "2 V 2":
            two_text = text_render("> 2 V 2 <", arcade_font, 28, YELLOW)
        else:
            two_text = text_render("2 V 2", arcade_font, 28, WHITE)
        if selected == "3 V 1":
            three_text = text_render("> 3 V 1 <", arcade_font, 28, YELLOW)
        else:
            three_text = text_render(" 3 V 1 ", arcade_font, 28, WHITE)
        if selected == "Free For All":
            free_text = text_render("> Free For All <", arcade_font, 24, YELLOW)
        else:
            free_text = text_render(" Free For All ", arcade_font, 24, WHITE)

        title_rect = title.get_rect()
        one_rect = one_text.get_rect()
        two_rect = two_text.get_rect()
        three_rect = three_text.get_rect()
        free_rect = free_text.get_rect()

        # Main Menu Text
        screen.blit(title, (screen_width/2 - (title_rect[2]/2), 80))
        screen.blit(one_text, (screen_width/2 - (one_rect[2]/2), 300))
        screen.blit(two_text, (screen_width/2 - (two_rect[2]/2)+200, 400))
        screen.blit(three_text, (screen_width/2 - (three_rect[2]/2)-200, 400))
        screen.blit(free_text, (screen_width/2 - (free_rect[2]/2), 525))

        pygame.display.update()
        clock.tick(FPS)
        pygame.display.set_caption("Game Mode")

# Chris notes
# Music for menu and game

def tutorial_menu():
    global mode_menu
    while mode_menu:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.type == pygame.QUIT:
                    mode_menu = False
                    pygame.quit()
                    exit()
                if event.key == pygame.K_ESCAPE:
                    main_menu()
                if event.key == pygame.K_RETURN:
                    game_run()

        screen.fill(BLACK)
        title = text_render("Controls", font, 75, GRID_FG)
        player1_text = text_render("Player 1: Q - Turn Left | W - Slow Bike | E - Turn right", arcade_font, 12, bikes[0].color)
        player2_text = text_render("Player 2: I - Turn Left | O - Slow Bike | P - Turn right", arcade_font, 12, bikes[1].color)
        continue_text = text_render("Press Enter/Return to start game...", arcade_font, 15, GRID_FG)
        title_rect = title.get_rect()
        player1_rect = player1_text.get_rect()
        player2_rect = player2_text.get_rect()
        continue_rect = continue_text.get_rect()

        if len(bikes) > 2:
            position1 = 200
        else:
            position1 = 300

        if len(bikes) > 2:
            position2 = 300
        else:
            position2 = 400

        # Main Menu Text
        screen.blit(title, (screen_width/2 - (title_rect[2]/2), 20))
        screen.blit(player1_text, (screen_width/2 - (player1_rect[2]/2), position1))
        screen.blit(player2_text, (screen_width/2 - (player2_rect[2]/2), position2))
        screen.blit(continue_text, (screen_width/2 - (continue_rect[2]/2), 600))

        #Checks to see if the player count is 4
        if len(bikes) > 2:
            player3_text = text_render("Player 3: Z - Turn Left | X - Slow Bike | C - Turn right", arcade_font, 12, bikes[2].color)
            player4_text = text_render("Player 4: L Arrow - Turn Left | D Arrow - Slow Bike | R Arrow - Turn Right", arcade_font, 11, bikes[3].color)
            player3_rect = player3_text.get_rect()
            player4_rect = player4_text.get_rect()
            screen.blit(player3_text, (screen_width/2 - (player3_rect[2]/2), 400))
            screen.blit(player4_text, (screen_width/2 - (player4_rect[2]/2), 500))

        pygame.display.update()
        clock.tick(FPS)
        pygame.display.set_caption("Game Mode")

def Leader_Board(teams, winnerposition, file):
    global leaderboard
    global namenotassigned
    namenotassigned = True
    while leaderboard:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.type == pygame.QUIT:
                    leaderboard = False
                    pygame.quit()
                    exit()
                if event.key == pygame.K_RETURN:
                    names.pop()
                    highscores.pop()
                    save_data(highscores, names, file)
                    main_menu()

        screen.fill(BLACK)
        title = text_render("LeaderBoard", arcade_font, 50, YELLOW)
        title_rect = title.get_rect()
        screen.blit(title, (screen_width/2 - (title_rect[2]/2), screen_height/60))
        if(teams == 1):
            subtitle = text_render("1 V 1", arcade_font, 40, YELLOW)
            subtitle_rect = subtitle.get_rect()
            screen.blit(subtitle, (screen_width/2 - (subtitle_rect[2]/2), (title_rect[2]/9)))
        elif(teams == 2):
            subtitle = text_render("2 V 2", arcade_font, 40, YELLOW)
            subtitle_rect = subtitle.get_rect()
            screen.blit(subtitle, (screen_width/2 - (subtitle_rect[2]/2), (title_rect[2]/9)))
        elif(teams == 3):
            subtitle = text_render("3 V 1", arcade_font, 40, YELLOW)
            subtitle_rect = subtitle.get_rect()
            screen.blit(subtitle, (screen_width/2 - (subtitle_rect[2]/2), (title_rect[2]/9)))
        elif(teams == 4):
            subtitle = text_render("Free For All", arcade_font, 40, YELLOW)
            subtitle_rect = subtitle.get_rect()
            screen.blit(subtitle, (screen_width/2 - (subtitle_rect[2]/2), (title_rect[2]/9)))

        highscoreLines = []
        highscore_rect = title.get_rect()
        if(len(names) < 11):
            names.insert(winnerposition, "AAA")
        if(len(names) > 11):
            names.pop()
        for x in range(10):
            if(x != 9):
                highscoreLines.append(str(x + 1) + ": " + names[x] + "     " + highscores[x])
            else:
                highscoreLines.append(str(x + 1) + ": " + names[x] + "     " + highscores[x] + " ")

        for x in range(len(highscoreLines)):
            highscore = text_render(highscoreLines[x], arcade_font, 20, YELLOW)
            highscore_rect = highscore.get_rect()
            screen.blit(highscore, (screen_width/2 - (highscore_rect[2]/2), (title_rect[2]/9) + (subtitle_rect[3]) + (highscore_rect[3] * x)))

        letter = ''
        settingNameText = ">A< AA"
        while(namenotassigned):
            if(winnerposition != 10):
                screen.fill(BLACK, (0, 3*screen_height/4, screen_width, screen_height))
                screen.blit(text_render(">", arcade_font, 20, GRID_FG), (screen_width/2 - (highscore_rect[2]/2) - 25,
                                                                (title_rect[2]/9) + (subtitle_rect[3]) + (highscore_rect[3] * winnerposition)))
                settingName = text_render(settingNameText, arcade_font, 40, YELLOW)
                settingName_rect = settingName.get_rect()
                screen.blit(settingName, (screen_width/2 - (settingName_rect[2]/2), (8*screen_height/10)))
                toNextLetter = text_render("Hit enter to lock in the letter", arcade_font, 15, YELLOW)
                toNextLetter_rect = toNextLetter.get_rect()
                screen.blit(toNextLetter, (screen_width/2 - (toNextLetter_rect[2]/2), 9*screen_height/10))
                for event in pygame.event.get():
                    if event.type == pygame.KEYDOWN:
                        if event.type == pygame.QUIT:
                            pygame.quit()
                            exit()
                        if event.key == pygame.K_UP:
                            if(settingNameText.index(">") == 0):
                                letter = settingNameText[1]
                                if(letter == "Z"):
                                    settingNameText = settingNameText.replace(">Z", ">A")

                                else:
                                    letter = chr(ord(letter) + 1)
                                    settingNameText = settingNameText.replace(">" + chr(ord(letter) - 1), ">" + letter)

                            elif(settingNameText.index(">") == 2):
                                letter = settingNameText[3]
                                if(letter == "Z"):
                                    settingNameText = settingNameText.replace(">Z", ">A")

                                else:
                                    letter = chr(ord(letter) + 1)
                                    settingNameText = settingNameText.replace(">" + chr(ord(letter) - 1), ">" + letter)

                            elif(settingNameText.index(">") == 3):
                                letter = settingNameText[4]
                                if(letter == "Z"):
                                    settingNameText = settingNameText.replace(">Z", ">A")

                                else:
                                    letter = chr(ord(letter) + 1)
                                    settingNameText = settingNameText.replace(">" + chr(ord(letter) - 1), ">" + letter)

                        elif event.key == pygame.K_DOWN:
                            if(settingNameText.index(">") == 0):
                                letter = settingNameText[1]
                                if(letter == "A"):
                                    settingNameText = settingNameText.replace(">A", ">Z")

                                else:
                                    letter = chr(ord(letter) - 1)
                                    settingNameText = settingNameText.replace(">" + chr(ord(letter) + 1), ">" + letter)

                            elif(settingNameText.index(">") == 2):
                                letter = settingNameText[3]
                                if(letter == "A"):
                                    settingNameText = settingNameText.replace(">A", ">Z")

                                else:
                                    letter = chr(ord(letter) - 1)
                                    settingNameText = settingNameText.replace(">" + chr(ord(letter) + 1), ">" + letter)

                            elif(settingNameText.index(">") == 3):
                                letter = settingNameText[4]
                                if(letter == "A"):
                                    settingNameText = settingNameText.replace(">A", ">Z")

                                else:
                                    letter = chr(ord(letter) - 1)
                                    settingNameText = settingNameText.replace(">" + chr(ord(letter) + 1), ">" + letter)

                        if event.key == pygame.K_RETURN:
                            if(settingNameText.index(">") == 0):
                                settingNameText = settingNameText.replace(">" + settingNameText[1] + "< A", settingNameText[1] +" >A< ", 1)

                            elif(settingNameText.index(">") == 2):
                                settingNameText = settingNameText.replace(" >" + settingNameText[3] + "< A", settingNameText[3] +" >A<", 1)

                            else:
                                names[winnerposition] = settingNameText[0] + settingNameText[1] + settingNameText[4]
                                namenotassigned = False;
            else:
                namenotassigned = False;
            screen.fill(BLACK, (0, 3*screen_height/4, screen_width, screen_height))
            screen.blit(text_render(">", arcade_font, 20, GRID_FG), (screen_width/2 - (highscore_rect[2]/2) - 25,
                                                            (title_rect[2]/9) + (subtitle_rect[3]) + (highscore_rect[3] * winnerposition)))
            settingName = text_render(settingNameText, arcade_font, 40, YELLOW)
            settingName_rect = settingName.get_rect()
            screen.blit(settingName, (screen_width/2 - (settingName_rect[2]/2), (8*screen_height/10)))
            toNextLetter = text_render("Hit enter to lock in the letter", arcade_font, 15, YELLOW)
            toNextLetter_rect = toNextLetter.get_rect()
            screen.blit(toNextLetter, (screen_width/2 - (toNextLetter_rect[2]/2), 9*screen_height/10))

            pygame.display.update()
            clock.tick(FPS)
            pygame.display.set_caption("LeaderBoard")

        settingName = text_render("Your Score: " + highscores[winnerposition], arcade_font, 35, YELLOW)
        settingName_rect = settingName.get_rect()
        screen.blit(settingName, (screen_width/2 - (settingName_rect[2]/2), 8*screen_height/10))
        toMainMenu = text_render("Hit enter to go to the Main Menu", arcade_font, 15, YELLOW)
        toMainMenu_rect = toMainMenu.get_rect()
        screen.blit(toMainMenu, (screen_width/2 - (toMainMenu_rect[2]/2), 9*screen_height/10))

        pygame.display.update()
        clock.tick(FPS)
        pygame.display.set_caption("LeaderBoard")

def game_run():
    # run while not done
    done = False
    mode_menu = False

    pressed_down = False

    global CLOCK_SPD
    global current_spd
    global duration_timer
    global paused
    global slow_timer
    global endState
    endState = False
    global file
    if(inTeams == 1):
        file = "1v1_leaderboard.txt"
    elif(inTeams == 2):
        file = "2v2_leaderboard.txt"
    elif(inTeams == 3):
        file = "3v1_leaderboard.txt"
    elif(inTeams == 4):
        file = "FFA_leaderboard.txt"
    global highscorezeros
    highscorezeros = ""
    global time
    global finalTimes
    global timerbikes
    timerbikes = bikes.copy()
    finalTimes = [0, 0, 0, 0]
    time = 0
    pygame.time.set_timer(USEREVENT+1, 1000)

    while not done:

        for event in pygame.event.get():
            #increments time every second
            if event.type == USEREVENT+1:
                time += 1
            # click the 'X' to close the window
            if event.type == pygame.QUIT:
                done = True

            # key press events
            if event.type == pygame.KEYDOWN:
                # bike controls
                # press right to turn right
                for bike in bikes:
                    if event.key == bike.right_key:
                        bike.turn(1)
                    elif event.key == bike.slow_key:
                        bike.s_multiplier = 0.4
                        duration_timer = 100
                    elif event.key == bike.left_key:
                        bike.turn(-1)

                # pressing esc also closes the window
                if event.key == pygame.K_ESCAPE:
                    done = True
                    main_menu()

            #Pressing space bar pauses the game
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_SPACE:
                    paused = True

            #Waits for user input to unpause the game
            while paused == True:
                for event in pygame.event.get():
                    if event.type == KEYUP:
                        if event.key == pygame.K_SPACE:
                            paused = False

        # advance the bike in the direction it is goings
        for bike in bikes:
            bike.move()

            # make the bike check if it is 'dead' (see method declaration for more info)
            if bike.check_die(0,  (grid_cell_scl * 2), screen_width, screen_height):
                for timerbike in timerbikes:
                    if(bike.left_key == timerbike.left_key):
                        timerbike.alive = False
                bikes.remove(bike)

            for other in bikes:
                if bike is not other:
                    if bike.phase is not True and bike.color is not other.color:
                        if bike.touches(other) & bike.alive:
                            bike.alive = False
                            for bike in timerbikes:
                                if(bike.left_key == other.left_key):
                                    bike.kills += 1

        delay = 10  # every x seconds, create a powerup
        decidesPowerUp = random.randint(0, 3)
        # Uses a random number to pick a random power up
        if (decidesPowerUp == 1):
            randomPowerUp = pu.PowerUps.Type.SPEED
        elif (decidesPowerUp == 2):
            randomPowerUp = pu.PowerUps.Type.MINE
        elif (decidesPowerUp == 3):
            randomPowerUp = pu.PowerUps.Type.PHASE
        else:
            randomPowerUp = pu.PowerUps.Type.NUKE

        if (pygame.time.get_ticks() % (CLOCK_SPD * delay) == 0):
            powerups.append(pu.PowerUps(screen_width, screen_height, randomPowerUp))

        for powerup in powerups:
            for bike in bikes:
                if (powerup.collides(bike)):
                    if (powerup.type is pu.PowerUps.Type.SPEED or
                        powerup.type is pu.PowerUps.Type.NUKE):
                        pu.PowerUps.apply_to_all(bikes, powerup.type)
                        # After x amount of time, powerup affects disappear
                        duration_timer = 500
                    elif (powerup.type is pu.PowerUps.Type.MINE):
                        p = pu.PowerUps(screen_width, screen_height, pu.PowerUps.Type.ACTUALLY_MINE)
                        p.h *= 2
                        p.w *= 2
                        powerups.append(p)
                    elif (powerup.type is pu.PowerUps.Type.ACTUALLY_MINE):
                        bike.alive = False
                    elif (powerup.type is pu.PowerUps.Type.PHASE):
                        bike.phase = True
                        duration_timer = 500

                    powerups.remove(powerup)

                # Pressing the down key closes the window
                # Starts a timer allowing you to only slow down for a specific amount of time

                # slow_timer -= (1 if slow_timer > 0 else 0)

                # if slow_timer == 0:
                #     bike.s_multiplier = 1
                # if pressed_down:
                #     slow_timer = 500
                #     bike.s_multiplier = .2

                # slow_timer -= (1 if slow_timer > 0 else 0)

                # if slow_timer == 0:
                #     bike.s_multiplier = 1

        duration_timer -= (1 if duration_timer > 0 else 0)
        if duration_timer == 0:
            for x in range(len(bikes)):
                bikes[x].s_multiplier = 1
                bikes[x].phase = False

        # calling the draw method after all the positioning and checking is done
        draw(endState, inTeams)

        # Pause the clock for a frame
        clock.tick(current_spd)

        #check for win
        if(len(bikes) < 4):
            endState = False
            if(len(bikes) == 1):
                endState = True
            elif(len(bikes) == 2):
                if(bikes[0].color == bikes[1].color):
                    endState = True
            elif(len(bikes) == 3):
                if(bikes[0].color == bikes[1].color and bikes[1].color == bikes[2].color):
                    endState = True
        if(endState):
            draw(endState, inTeams)
            winnerhighscore = 0
            killmult = 1
            load_data(file)
            place = 0

            if(inTeams == 2 or inTeams == 3):
                if(timerbikes[0].color == bikes[0].color):
                    if(inTeams == 2):
                        winnerhighscore += finalTimes[0]
                        winnerhighscore += finalTimes[1]
                        killmult += timerbikes[0].kills
                        killmult += timerbikes[1].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10
                    else:
                        winnerhighscore += finalTimes[0]
                        winnerhighscore += finalTimes[1]
                        winnerhighscore += finalTimes[2]
                        killmult += timerbikes[0].kills
                        killmult += timerbikes[1].kills
                        killmult += timerbikes[2].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10
                else:
                    if(inTeams == 2):
                        winnerhighscore += finalTimes[2]
                        winnerhighscore += finalTimes[3]
                        killmult += timerbikes[2].kills
                        killmult += timerbikes[3].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10
                    else:
                        winnerhighscore += finalTimes[3]
                        killmult += timerbikes[3].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 2
                        winnerhighscore *= 10
            else:
                if(timerbikes[0].left_key == bikes[0].left_key):
                        winnerhighscore += finalTimes[0]
                        killmult += timerbikes[0].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10
                elif(timerbikes[1].left_key == bikes[0].left_key):
                        winnerhighscore += finalTimes[1]
                        killmult += timerbikes[1].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10
                elif(timerbikes[2].left_key == bikes[0].left_key):
                        winnerhighscore += finalTimes[2]
                        killmult += timerbikes[2].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10
                elif(timerbikes[3].left_key == bikes[0].left_key):
                        winnerhighscore += finalTimes[3]
                        killmult += timerbikes[3].kills
                        winnerhighscore *= killmult
                        winnerhighscore *= 10

            for num in range(10 - len(str(winnerhighscore))):
                highscorezeros = highscorezeros + "0"
            winnerscore = highscorezeros + str(winnerhighscore)
            for highscore in highscores:
                if(highscore >= winnerscore):
                    place += 1
            highscores.insert(place, winnerscore)
            print(place)
            print(highscores[10])

            while(endState):
                for event in pygame.event.get():
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_RETURN:
                            print(place)
                            Leader_Board(inTeams, place, file)

# when the loop is done, quit
#Initialize the Game
main_menu()
pygame.quit()
quit()


from flask import Flask, Blueprint, current_app
from flask_restful import Resource, fields, marshal, Api

from tracker.model import Member
from tracker.strava import Strava

BP_NAME = 'members_api'
MEMBER_ENDPOINT = 'memberapi'

member_fields = {
    'id': fields.String,
    'first_name': fields.String,
    'last_name': fields.String,
    'uri': fields.Url("{}.{}".format(BP_NAME, MEMBER_ENDPOINT))

}

class MemberListAPI(Resource):
    def get(self):
        members = Member.objects.all()

        json = []
        for m in members:
            json.append(m.jsonify())
        print(json)

        return {"members": marshal(json, member_fields)}

class MemberAPI(Resource):
    def get(self, id):
        member = Member.objects.get(id=id)

        return {"member": marshal(member.jsonify(), member_fields)}

class MemberAvatarAPI(Resource):
    def __init__(self):
        self.app = current_app
        self.strava = Strava(self.app.config['STRAVA_CLIENT_ID'], self.app.config['STRAVA_CLIENT_SECRET'])

    def get(self, id):
        url = self.strava.get_member_avatar(id)

        return {"avatar": {"url": url}}

app = Flask(__name__)
members_bp = Blueprint(BP_NAME, __name__)
api = Api(members_bp)

api.add_resource(MemberListAPI, '/members')
api.add_resource(MemberAPI, '/members/<int:id>', endpoint=MEMBER_ENDPOINT)
api.add_resource(MemberAvatarAPI, '/members/<int:id>/avatar')


from decimal import Decimal
from urllib import parse
from datetime import date

from django.urls import reverse
from django.utils import timezone

from utils.test import get_or_create_user
from metering.models import MeteringMonitorWebsite, DailyStatementMonitorWebsite, PaymentStatus
from . import MyAPITestCase

def create_site_metering(
        date_, website_id: str, website_name: str, user_id, username: str, hours: int, tamper_count: int,
        trade_amount: Decimal, original_amount: Decimal,
        creation_time=None, daily_statement_id: str = ''):
    metering = MeteringMonitorWebsite(
        website_id=website_id,
        website_name=website_name,
        date=date_,
        hours=hours,
        detection_count=0,
        tamper_resistant_count=tamper_count,
        security_count=0,
        user_id=user_id,
        username=username,
        creation_time=creation_time if creation_time else timezone.now(),
        trade_amount=trade_amount,
        original_amount=original_amount,
        daily_statement_id=daily_statement_id,
    )
    metering.save(force_insert=True)
    return metering

class MeteringMonitorSiteTests(MyAPITestCase):
    def setUp(self):
        self.user1 = get_or_create_user(username='user1')
        self.user2 = get_or_create_user(username='user2')

    def test_list_metering(self):
        m1_u1 = create_site_metering(
            date_=date(year=2022, month=2, day=16),
            website_id='website_id1',
            website_name='website_name1',
            user_id=self.user1.id,
            username=self.user1.username,
            hours=11,
            tamper_count=1,
            original_amount=Decimal('1.11'),
            trade_amount=Decimal('1.11'),
        )
        m2_u1 = create_site_metering(
            date_=date(year=2022, month=3, day=16),
            website_id='website_id2',
            website_name='website_name2',
            user_id=self.user1.id,
            username=self.user1.username,
            hours=22,
            tamper_count=2,
            original_amount=Decimal('2.22'),
            trade_amount=Decimal('2.22'),
        )
        m3_u2 = create_site_metering(
            date_=date(year=2022, month=2, day=8),
            website_id='website_id3',
            website_name='website_name3',
            user_id=self.user2.id,
            username=self.user2.username,
            hours=33,
            tamper_count=3,
            original_amount=Decimal('3.33'),
            trade_amount=Decimal('3.33'),
        )
        m4_u2 = create_site_metering(
            date_=date(year=2022, month=3, day=8),
            website_id='website_id4',
            website_name='website_name4',
            user_id=self.user2.id,
            username=self.user2.username,
            hours=44,
            tamper_count=4,
            original_amount=Decimal('4.44'),
            trade_amount=Decimal('4.4'),
        )
        m5_u1 = create_site_metering(
            date_=date(year=2022, month=3, day=18),
            website_id='website_id5',
            website_name='website_name5',
            user_id=self.user1.id,
            username=self.user1.username,
            hours=55,
            tamper_count=5,
            original_amount=Decimal('5.55'),
            trade_amount=Decimal('5.55'),
        )
        m6_u1 = create_site_metering(
            date_=date(year=2022, month=3, day=9),
            website_id='website_id6',
            website_name='website_name6',
            user_id=self.user1.id,
            username=self.user1.username,
            hours=66,
            tamper_count=6,
            original_amount=Decimal('6.66'),
            trade_amount=Decimal('6.66'),
        )

        base_url = reverse('api:metering-monitor-site-list')
        r = self.client.get(base_url)
        self.assertErrorResponse(status_code=401, code='NotAuthenticated', response=r)

        # list user1 metering, default current month
        self.client.force_login(self.user1)
        r = self.client.get(base_url)
        self.assertEqual(r.status_code, 200)
        self.assertKeysIn(["count", "page_num", "page_size", "results"], r.data)
        self.assertEqual(r.data["count"], 0)
        self.assertEqual(len(r.data['results']), 0)

        # list user1 metering, date_start - date_end
        query = parse.urlencode(query={
            'date_start': '2022-02-01', 'date_end': '2022-04-06'
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 200)
        self.assertKeysIn(["count", "page_num", "page_size", "results"], r.data)
        self.assertEqual(r.data["count"], 4)
        self.assertEqual(len(r.data['results']), 4)
        self.assertKeysIn([
            "id", "original_amount", "trade_amount",
            "daily_statement_id", "website_id", "website_name", "date",
            "creation_time", "user_id", "username", "hours", "tamper_resistant_count"
        ], r.data['results'][0])

        # list user metering, invalid date_start
        query = parse.urlencode(query={
            'date_start': '2022-02-30'
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 400)

        # list user metering, invalid date_end
        query = parse.urlencode(query={
            'date_end': '2022-2-01'
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 400)

        # list user1 metering, date_start date_end timedelta less than one year
        query = parse.urlencode(query={
            'date_start': '2021-02-01', 'date_end': '2022-02-02'
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 400)

        # list user1 metering, query page_size
        query = parse.urlencode(query={
            'date_start': '2022-02-01', 'date_end': '2022-04-06', 'page_size': 1
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.data["count"], 4)
        self.assertEqual(r.data["page_size"], 1)
        self.assertEqual(len(r.data['results']), 1)

        # no permission
        query = parse.urlencode(query={
            'date_start': '2022-02-01', 'date_end': '2022-04-06', 'as-admin': ''
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 403)

        # federal admin， list all
        self.user1.set_federal_admin()
        query = parse.urlencode(query={
            'date_start': '2022-02-01', 'date_end': '2022-04-06', 'as-admin': ''
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.data["count"], 6)
        self.assertEqual(len(r.data['results']), 6)

        # federal admin, user_id
        query = parse.urlencode(query={
            'date_start': '2022-02-01', 'date_end': '2022-04-06', 'as-admin': '', 'user_id': self.user2.id
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.data["count"], 2)
        self.assertEqual(len(r.data['results']), 2)

        query = parse.urlencode(query={
            'date_start': '2022-02-01', 'date_end': '2022-04-06', 'as-admin': '', 'user_id': self.user1.id
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.data["count"], 4)
        self.assertEqual(len(r.data['results']), 4)

        query = parse.urlencode(query={
            'date_start': '2022-03-01', 'date_end': '2022-04-06', 'as-admin': '', 'user_id': self.user2.id
        })
        r = self.client.get(f'{base_url}?{query}')
        self.assertEqual(r.status_code, 200)
        self.assertEqual(r.data["count"], 1)
        self.assertEqual(len(r.data['results']), 1)
        item = r.data['results'][0]
        self.assertEqual(m4_u2.website_id, item['website_id'])
        self.assertEqual(m4_u2.website_name, item['website_name'])
        self.assertEqual(m4_u2.date, date.fromisoformat(item['date']))
        self.assertEqual('4.44', item['original_amount'])
        self.assertEqual('4.40', item['trade_amount'])
        self.assertEqual(44, item['hours'])
        self.assertEqual(4, item['tamper_resistant_count'])

class StatementMonitorSiteTests(MyAPITestCase):
    def setUp(self):
        self.user1 = get_or_create_user(username='user1')
        self.user2 = get_or_create_user(username='user2')

    def create_statement_site(self):
        # user 2022-1-1 service
        u_st0 = DailyStatementMonitorWebsite(
            original_amount='1.11',
            trade_amount='1.11',
            payable_amount='1.11',
            payment_status=PaymentStatus.PAID.value,
            payment_history_id='',
            date=date(year=2022, month=1, day=1),
            user_id=self.user1.id,
            username=self.user1.username
        )
        u_st0.save(force_insert=True)

        # user service2 2022-1-1
        u_st1 = DailyStatementMonitorWebsite(
            original_amount='2.22',
            trade_amount='0',
            payable_amount='2.22',
            payment_status=PaymentStatus.UNPAID.value,
            payment_history_id='',
            date=date(year=2022, month=1, day=1),
            user_id=self.user1.id,
            username=self.user1.username
        )
        u_st1.save(force_insert=True)

        # user 2022-1-2 service
        u_st2 = DailyStatementMonitorWebsite(
            original_amount='3.33',
            trade_amount='0',
            payable_amount='3.33',
            payment_status=PaymentStatus.UNPAID.value,
            payment_history_id='',
            date=date(year=2022, month=1, day=2),
            user_id=self.user1.id,
            username=self.user1.username
        )
        u_st2.save(force_insert=True)

        # user2 service 2022-1-2
        u_st3 = DailyStatementMonitorWebsite(
            original_amount='4.44',
            trade_amount='0',
            payable_amount='4.44',
            payment_status=PaymentStatus.UNPAID.value,
            payment_history_id='',
            date=date(year=2022, month=1, day=2),
            user_id=self.user2.id,
            username=self.user2.username
        )
        u_st3.save(force_insert=True)

        return u_st0, u_st1, u_st2, u_st3

    def test_list_statement(self):
        base_url = reverse('api:statement-monitor-site-list')
        r = self.client.get(base_url)
        self.assertErrorResponse(status_code=401, code='NotAuthenticated', response=r)

        # list user1 statement-storage
        self.client.force_login(self.user1)
        response = self.client.get(base_url)
        self.assertEqual(response.status_code, 200)
        self.assertKeysIn(['count', 'page_num', 'page_size', 'statements'], response.data)
        self.assertIsInstance(response.data['statements'], list)
        self.assertEqual(response.data['count'], 0)
        self.assertEqual(len(response.data['statements']), 0)
        # create statement
        u_st0, u_st1, u_st2, u_st3 = self.create_statement_site()
        # ------ list user -------
        # no params
        response = self.client.get(base_url)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 3)
        self.assertEqual(len(response.data['statements']), 3)
        self.assertKeysIn(["id", "original_amount", "payable_amount", "trade_amount",
                           "payment_status", "payment_history_id", "date", "creation_time",
                           "user_id", "username"], response.data['statements'][0])
        self.assertEqual(response.data['statements'][0]['original_amount'], u_st2.original_amount)
        self.assertEqual(response.data['statements'][1]['original_amount'], u_st1.original_amount)
        self.assertEqual(response.data['statements'][2]['original_amount'], u_st0.original_amount)

        # date_start - date_end
        query = parse.urlencode(query={
            'date_start': '2022-01-02', 'date_end': '2022-01-02'
        })
        response = self.client.get(f'{base_url}?{query}')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 1)
        self.assertEqual(len(response.data['statements']), 1)
        self.assertEqual(response.data['statements'][0]['user_id'], self.user1.id)
        self.assertEqual(response.data['statements'][0]['user_id'], u_st2.user_id)
        self.assertEqual(response.data['statements'][0]['original_amount'], u_st2.original_amount)

        query = parse.urlencode(query={
            'date_start': '2022-01-01', 'date_end': '2022-01-02'
        })
        response = self.client.get(f'{base_url}?{query}')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 3)
        self.assertEqual(len(response.data['statements']), 3)

        # payment_status
        query = parse.urlencode(query={
            'payment_status': PaymentStatus.PAID.value,
        })
        response = self.client.get(f'{base_url}?{query}')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 1)
        self.assertEqual(len(response.data['statements']), 1)
        self.assertEqual(response.data['statements'][0]['original_amount'], u_st0.original_amount)

        # date_start - date_end  && payment_status
        query = parse.urlencode(query={
            'date_start': '2022-01-01', 'date_end': '2022-01-02',
            'payment_status': PaymentStatus.UNPAID.value,

        })
        response = self.client.get(f'{base_url}?{query}')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 2)
        self.assertEqual(len(response.data['statements']), 2)

    def test_detail_statement_storage(self):
        url = reverse('api:statement-monitor-site-detail', kwargs={'id': '1234567891234567891234'})
        r = self.client.get(url)
        self.assertErrorResponse(status_code=401, code='NotAuthenticated', response=r)

        # list user1 statement-storage
        self.client.force_login(self.user1)
        # not found
        r = self.client.get(url)
        self.assertErrorResponse(status_code=404, code='TargetNotExist', response=r)

        # create statement server
        u_st0, u_st1, u_st2, u_st3 = self.create_statement_site()

        # user statement detail
        metering1 = create_site_metering(
            date_=u_st0.date,
            website_id='website_id1',
            website_name='website_name1',
            user_id=self.user1.id,
            username=self.user1.username,
            hours=11,
            tamper_count=1,
            original_amount=Decimal('2.22'),
            trade_amount=Decimal('0'),
            daily_statement_id=u_st0.id
        )

        url = reverse('api:statement-monitor-site-detail', kwargs={'id': u_st0.id})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertKeysIn(["id", "original_amount", "payable_amount", "trade_amount",
                           "payment_status", "payment_history_id", "date", "creation_time",
                           "user_id", "username", 'meterings'], response.data)
        self.assertIsInstance(response.data['meterings'], list)
        self.assertEqual(len(response.data['meterings']), 1)
        self.assertEqual(response.data['meterings'][0]['id'], metering1.id)
        self.assertKeysIn([
            "id", "original_amount", "trade_amount",
            "daily_statement_id", "website_id", "website_name", "date",
            "creation_time", "user_id", "username", "hours", "tamper_resistant_count"
        ], response.data['meterings'][0])
        self.assert_is_subdict_of(sub={
            "id": u_st0.id,
            "original_amount": u_st0.original_amount,
            "payable_amount": u_st0.payable_amount,
            "trade_amount": u_st0.trade_amount,
            "payment_status": u_st0.payment_status,
            "payment_history_id": u_st0.payment_history_id,
            "user_id": self.user1.id,
            "username": self.user1.username,
        }, d=response.data)


import numpy as np
import cv2

def trkBar_OnChange(num): print(num)

# Identify and name the window for future objects
cv2.namedWindow('image')

# Create trackbars in image window
cv2.createTrackbar('Blue', 'image', 0, 255, trkBar_OnChange)
cv2.createTrackbar('Green', 'image', 0, 255, trkBar_OnChange)
cv2.createTrackbar('Red', 'image', 0, 255, trkBar_OnChange)

# Create a variable switch
switch = 'ON/OFF'
cv2.createTrackbar(switch, 'image', 0, 1, trkBar_OnChange)

# Create a black image of size 512x512x3
img = np.zeros((512, 512, 3), np.uint8)

# Read a local image
# img = cv2.imread('DATA/00-puppy.jpg')

while True:
    b = cv2.getTrackbarPos('Blue', 'image')
    g = cv2.getTrackbarPos('Green', 'image')
    r = cv2.getTrackbarPos('Red', 'image')
    swt = cv2.getTrackbarPos(switch, 'image')

    assert isinstance(img, object)
    if swt == 0:
        img[:] = 0
    else:
        img[:] = [b, g, r]

    cv2.imshow('image', img)
    if cv2.waitKey(1) & 0xFF == 27:
        break

cv2.destroyAllWindows()


import pygame as pg
from settings import *
from random import randint
from imageloader import ImageLoader
vec = pg.math.Vector2

class Player(ImageLoader, pg.sprite.Sprite):

    def __init__(self, game):
        self.game = game
        self.ctrl = game.ctrl
        self.ImageLoader = ImageLoader()
        self.groups = game.all_sprites
        self._layer = 3
        pg.sprite.Sprite.__init__(self, self.groups)
        self.init_graphics()

    def init_graphics(self):
        self.frame_selector_walk = 0
        self.frame_selector_walk_invert = False
        self.frame_selector_stand = 0
        self.frame_selector_stand_invert = False
        self.frame_selector_attack = 0
        self.animation_walk = self.ImageLoader.load_images(7, 'images/walk/walk', 'png', True)
        self.animation_stand = self.ImageLoader.load_images(10, 'images/stand/stand', 'png', True)
        self.animation_attack = self.ImageLoader.load_images(14, 'images/attack/attack', 'png', True)
        self.animation_walk = self.ImageLoader.down_scale_images(self.animation_walk, 2)
        self.animation_stand = self.ImageLoader.down_scale_images(self.animation_stand, 2)
        self.animation_attack = self.ImageLoader.down_scale_images(self.animation_attack, 2)
        self.image = self.animation_stand[0]
        self.rect = self.image.get_rect()
        self.mask = pg.mask.from_surface(self.image)
        self.rect.centerx = screen_width / 2
        self.rect.y = screen_height - (self.rect.height + 10)

    def update(self):
        self.ctrl.Player.get_keys()
        if self.ctrl.Player.jumping:
            if not self.ctrl.Player.falling:
                self.rect.y -= 5
            else:
                self.rect.y += 5
            if self.ctrl.Player.jump_height == 0 and not self.ctrl.Player.falling:
                self.ctrl.Player.falling = True
                self.ctrl.Player.jump_height = jump_height
            elif self.ctrl.Player.jump_height == 0 and self.ctrl.Player.falling:
                self.ctrl.Player.jumping = False
                self.ctrl.Player.falling = False
                self.ctrl.Player.jump_height = jump_height
            self.ctrl.Player.jump_height -= 1
        if self.ctrl.Player.attacking:
            if self.game.frame_counter %2 == 0:
                self.image = self.animation_attack[self.frame_selector_attack]
                if self.ctrl.Player.facing_right:
                    self.image = pg.transform.flip(self.image, True, False)
                self.mask = pg.mask.from_surface(self.image)
                self.frame_selector_attack += 1
                if self.frame_selector_attack + 1 > len(self.animation_attack):
                    self.frame_selector_attack = 0
                    self.ctrl.Player.attacking = False
        elif self.ctrl.Player.move_left:
            if self.game.frame_counter %2 == 0:
                self.image = self.animation_walk[self.frame_selector_walk]
                self.mask = pg.mask.from_surface(self.image)
            if self.rect.centerx > screen_width * 0.2:
                self.game.Background.scroll = False
                self.rect.x -= 5
            else:
                self.game.Background.scroll = True
            if not self.ctrl.Player.jumping:
                self.frame_selector_walk += 1
            if self.frame_selector_walk + 1 > len(self.animation_walk):
                self.frame_selector_walk = 0
        elif self.ctrl.Player.move_right:
            if self.game.frame_counter %2 == 0:
                self.image = self.animation_walk[self.frame_selector_walk]
                self.image = pg.transform.flip(self.image, True, False)
                self.mask = pg.mask.from_surface(self.image)
            if self.rect.centerx < screen_width * 0.8:
                self.game.Background.scroll = False
                self.rect.x += 5
            else:
                self.game.Background.scroll = True
            if not self.ctrl.Player.jumping:
                self.frame_selector_walk += 1
            if self.frame_selector_walk + 1 > len(self.animation_walk):
                self.frame_selector_walk = 0

        elif self.ctrl.Player.standing:
            if not self.frame_selector_stand_invert:
                self.image = self.animation_stand[::-1][self.frame_selector_stand]
            else:
                self.image = self.animation_stand[self.frame_selector_stand]
            if self.ctrl.Player.facing_right:
                self.image = pg.transform.flip(self.image, True, False)
            self.mask = pg.mask.from_surface(self.image)
            self.frame_selector_stand += 1
            if self.frame_selector_stand + 1 > len(self.animation_stand):
                self.frame_selector_stand_invert = not self.frame_selector_stand_invert
                self.frame_selector_stand = 0
        if self.ctrl.Player.health == 0:
            self.kill()


#-*- coding: utf-8 -*
from google.appengine.api import users
from google.appengine.ext import db

class UserForum(db.Model):
	idUser = db.IntegerProperty()
	loginGoogle = db.StringProperty(multiline=True)
	loginForum = db.StringProperty(multiline=True)
	position = db.StringProperty(multiline=True)
	name = db.StringProperty(multiline=True)
	lastName = db.StringProperty(multiline=True)

def getIdUser(googleName):
	usersForum = db.GqlQuery("SELECT * FROM UserForum")
	for tmp_user in usersForum:
		if (tmp_user.loginGoogle == googleName):
			return tmp_user.idUser

def getIdUserForum(loginForum):
	usersForum = db.GqlQuery("SELECT * FROM UserForum")
	for tmp_user in usersForum:
		if (tmp_user.loginForum == loginForum):
			return tmp_user.idUser

def getUserById(idUser):
	usersForum = db.GqlQuery("SELECT * FROM UserForum")
	for tmp_user in usersForum:
		if (tmp_user.idUser == idUser):
			return tmp_user

def isAdmin(googleName):
	usersForum = db.GqlQuery("SELECT * FROM UserForum")
	for tmp_user in usersForum:
		if (tmp_user.loginGoogle == googleName and tmp_user.position == "admin"):
			return True
	return False

def isUser(googleName,idUser):
	usersForum = db.GqlQuery("SELECT * FROM UserForum")
	for tmp_user in usersForum:
		if (tmp_user.loginGoogle == googleName and tmp_user.idUser == idUser):
			return True
	return False

def autorization(path):
		greetings = ""

		if users.get_current_user():

			isUser = False
			usersForum = db.GqlQuery("SELECT * FROM UserForum")
			userForum = UserForum()

			for tmp_user in usersForum:
				if (tmp_user.loginGoogle == users.get_current_user().nickname()):
					isUser = True
			if (isUser == False):
				if (usersForum):
					userForum.idUser = usersForum.count() + 1
				else:
					userForum.idUser = 1
				userForum.loginGoogle = users.get_current_user().nickname()
				userForum.loginForum = None
				userForum.position = 'user'
				userForum.name = None
				userForum.lastName = None
				userForum.put()

			idUser = getIdUser(users.get_current_user().nickname())
			greetings += (" %s   выйти"
			% (idUser, users.get_current_user().nickname(), users.create_logout_url(path)))

		else:
			greetings += ("Войти на сайт" % users.create_login_url(path))

		return greetings


from collections import defaultdict
from copy import copy
from typing import List, Tuple, Set, Dict
from pathlib import Path

INPUT_FILE = Path(__file__).with_suffix(".input")

EXAMPLE_DATA = """0/2
2/2
2/3
3/4
3/5
0/1
10/1
9/10"""

def load_pieces(input_str: str) -> Set[Tuple[int, int]]:
    ret_list = set()
    for line in input_str.split("\n"):
        ret_list.add(tuple(int(i) for i in line.split("/")))
    # ret_list.sort(key=sum, reverse=True)
    return ret_list

def calc_strength(bridge_list: List[Tuple[int, int]]) -> int:
    return sum(sum(p) for p in bridge_list)

def gen_matching(
    prev_con: int, remaining: Set[Tuple[int, int]], cached_mapping: Dict[int, List[Tuple[int, int]]] = defaultdict(list)
):
    if not cached_mapping:  # remaining must contain all possible items first run otherwise cache will be bad
        for part in remaining:
            cached_mapping[part[0]].append(part)
            if part[0] == part[1]:
                continue
            cached_mapping[part[1]].append(part)
    for part in cached_mapping[prev_con]:
        if part in remaining:
            yield part

def build_bridges(
    prev_piece: int,
    so_far: List[Tuple[int, int]],
    remaining: Set[Tuple[int, int]],
    part1: List[List[Tuple[int, int]]],
    part2: List[List[Tuple[int, int]]],
):
    leaf = True
    for possible_next in gen_matching(prev_piece, remaining):
        leaf = False
        new_bridge = copy(so_far)
        reduced = copy(remaining)
        reduced.remove(possible_next)
        next_piece = possible_next[0]
        if next_piece == prev_piece:
            next_piece = possible_next[1]
        new_bridge.append(possible_next)
        build_bridges(next_piece, new_bridge, reduced, part1, part2)

    if leaf:
        if calc_strength(so_far) > calc_strength(part1[0]):
            part1[:] = [so_far]

        if len(so_far) > len(part2[0]):
            part2[:] = [so_far]
        elif len(so_far) == len(part2[0]):
            if calc_strength(so_far) > calc_strength(part2[0]):
                part2[:] = [so_far]

if __name__ == "__main__":
    DATA = INPUT_FILE.read_text().strip()
    piece_list = load_pieces(DATA)
    print(piece_list)
    completed_bridges_p1 = [[]]
    completed_bridges_p2 = [[]]
    build_bridges(0, [], piece_list, completed_bridges_p1, completed_bridges_p2)
    print(completed_bridges_p1)
    print(calc_strength(completed_bridges_p1[0]))
    print(completed_bridges_p2)
    print(calc_strength(completed_bridges_p2[0]))


prezzo=5.5
sconto=1
cliente_pref='giacomo'

print('Inserisci come ti chiami?')
nome=input()

print('Inserisci quanti anni hai?')
anni=int(input())

if nome==cliente_pref and anni>25:
    prezzo-=sconto

print('Prezzo drink: '+ str(prezzo))


import time
import json

class BinaryTree:
    # 1. 先序优先遍历DLR递归版
    def preOrderTraverse(self, tree, result=[]):
        if tree is not None:
            result.append(tree.value)
            self.preOrderTraverse(tree.left, result)
            self.preOrderTraverse(tree.right, result)
        return result

    # 2. 中序优先遍历LDR递归版
    def inOrderTraverse(self, tree, result=[]):
        if tree is not None:
            self.inOrderTraverse(tree.left, result)
            result.append(tree.value)
            self.inOrderTraverse(tree.right, result)
        return result

    # 3. 后序优先遍历LRD递归版
    def postOrderTraverse(self, tree, result=[]):
        if tree is not None:
            self.postOrderTraverse(tree.left, result)
            self.postOrderTraverse(tree.right, result)
            result.append(tree.value)
        return result

    # 4. 广度优先(层级遍历)，自左往右，利用队列暂存数据
    def levelOrder(self, tree):
        result = []
        queue = []

        if (tree is not None):
            queue.append(tree)
            while (len(queue) > 0):
                tree = queue.pop(0)
                result.append(tree.value)
                # append children of current Node
                if tree.left is not None:
                    queue.append(tree.left)
                if tree.right is not None:
                    queue.append(tree.right)
        return result

    # 5. 深度遍历(先序优先非递归版)
    def preOrderUnRecursive(self, tree):
        result = []
        stack = []
        if tree is not None:
            while (len(stack) > 0 or tree is not None):
                if tree is not None:
                    result.append(tree.value)
                    stack.append(tree)
                    tree = tree.left
                else:
                    tree = stack.pop()
                    tree = tree.right
        return result

    # 6. 深度遍历(中序优先非递归版)
    def inOrderUnRecursive(self, tree):
        result = []
        stack = []
        if (tree is not None):
            while (len(stack) > 0 or tree is not None):
                if tree is not None:
                    stack.append(tree)
                    tree = tree.left
                else:
                    tree = stack.pop()
                    result.append(tree.value)
                    tree = tree.right
        return result

    # 7. 深度遍历(后序优先非递归版)
    def postOrderUnRecursive(self, tree):
      result = []
      queue = []
      tmp = None
      if (tree is not None):
        queue.append(tree)
        while len(queue) > 0:
          # 先得到最后一项作为比较项
          tmp = queue[len(queue) - 1]
          # 把左子树按深度全部追加到queue
          if (tmp.left is not None and tree != tmp.left and tree != tmp.right):
            queue.append(tmp.left)
            # 把右子树追加到queue
          elif (tmp.right and tree != tmp.right):
            queue.append(tmp.right)
          else:
            # 左子树到最底层节点，开始打印left与right
            result.append(queue.pop().value)
            #  指向上一个节点，直到queue为空，也就是遍历至根节点
            tree = tmp
      return result

# test
class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right
    def __str__(self):
        return "value: %s" % (self.value)

"""
data =
                 1
            /         \
          2              3
        /   \          /   \
      4      5        6     7
           /   \          /    \
          8     9        10    11
        /   \
      12    13
"""
tree = Node(1)
tree.left = Node(2)
tree.right = Node(3)
node2 = tree.left
node3 = tree.right
node2.left = Node(4)
node2.right = Node(5)
node3.left = Node(6)
node3.right = Node(7)
node5 = node2.right
node5.left = Node(8)
node5.right = Node(9)
node7 = node3.right
node7.left = Node(10)
node7.right = Node(11)
node8 = node5.left
node8.left = Node(12)
node8.right = Node(13)
start_time = time.time()
# print origin
# print('origin tree:', json.dumps(tree.__dict__))
print('origin tree:', tree)
# start
binaryTree = BinaryTree()

# 1.
result = binaryTree.preOrderTraverse(tree)
print('1. preOrderTraverse:', result)
#  [1, 2, 4, 5, 8, 12, 13, 9, 3, 6, 7, 10, 11]

# 2.
result = binaryTree.inOrderTraverse(tree)
print('2. inOrderTraverse:', result)
# 4, 2, 12, 8, 13, 5, 9, 1, 6, 3, 10, 7, 11]

# 3.
result = binaryTree.postOrderTraverse(tree)
# [4, 12, 13, 8, 9, 5, 2, 6, 10, 11, 7, 3, 1]
print('3. postOrderTraverse:', result)

# 4.
result = binaryTree.levelOrder(tree)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
print('4. levelOrder:', result)

# 5.
result = binaryTree.preOrderUnRecursive(tree)
# [1, 2, 4, 5, 8, 12, 13, 9, 3, 6, 7, 10, 11]
print('5. preOrderUnRecursive:', result)

# 6.
result = binaryTree.inOrderUnRecursive(tree)
# 4, 2, 12, 8, 13, 5, 9, 1, 6, 3, 10, 7, 11]
print('6. inOrderUnRecursive:', result)

# 7.
result = binaryTree.postOrderUnRecursive(tree)
# [4, 12, 13, 8, 9, 5, 2, 6, 10, 11, 7, 3, 1]
print('7. postOrderUnRecursive:', result)

print("time:" + str((time.time() - start_time) * 1000) + " ms")

"""
('1. preOrderTraverse:', [1, 2, 4, 5, 8, 12, 13, 9, 3, 6, 7, 10, 11])
('2. inOrderTraverse:', [4, 2, 12, 8, 13, 5, 9, 1, 6, 3, 10, 7, 11])
('3. postOrderTraverse:', [4, 12, 13, 8, 9, 5, 2, 6, 10, 11, 7, 3, 1])
('4. levelOrder:', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
('5. preOrderUnRecursive:', [1, 2, 4, 5, 8, 12, 13, 9, 3, 6, 7, 10, 11])
('6. inOrderUnRecursive:', [4, 2, 12, 8, 13, 5, 9, 1, 6, 3, 10, 7, 11])
('7. postOrderUnRecursive:', [4, 12, 13, 8, 9, 5, 2, 6, 10, 11, 7, 3, 1])
time:0.270128250122 ms
"""


import pandas as pd
import numpy as np
import numpy.polynomial.polynomial as poly
import matplotlib.pyplot as plt
import statsmodels.nonparametric.smoothers_lowess as st
from pykalman import KalmanFilter
from scipy import signal
import re
#import warnings
np.warnings.filterwarnings('ignore')

in_filename = "raw_accel.csv"
out_filename = "move_polys.csv"
col_names = ["move", "x", "y", "z", "time", "err1",  "err2",  "err3"]
name_equiv = {'s': 'standing', 't':'tendu', 'j':'saute', 'r': 'retire', 'a': 'assemble'}

def check_len(m):
    return len(m['x']) > 70

def rename(m):
    match = re.search(r'([a-z]+)', m)
    match = match.group(1)
    string = name_equiv[match]
    return string

def smoothing(m):
    #not used as it did not improve performance, left for referance only
    b, a = signal.butter(3, 0.01, btype='lowpass', analog=False)
    low_x = signal.filtfilt(b, a, m['x'])
    low_y = signal.filtfilt(b, a, m['y'])
    low_z = signal.filtfilt(b, a, m['z'])
    m['x'] = low_x
    m['y'] = low_y
    m['z'] = low_z
    if(np.isnan(m['x']).any() | np.isnan(m['y']).any() | np.isnan(m['x']).any()):
        m['move'] = None
        return
    return m

def polyfitting(m):
    coef_x = poly.polyfit(m['time'], m['x'], 3)
    coef_y = poly.polyfit(m['time'], m['y'], 3)
    coef_z = poly.polyfit(m['time'], m['z'], 3)
    return m['move'], coef_x, coef_y, coef_z

def plot_graph(df, n):
    #plots the x, y, z changes of particualr move
    m = df.iloc[n]
    print(m)
    x = m['time']
    yx = m['x']
    yy = m['y']
    yz = m['z']
    plt.subplot(3, 1, 1)
    plt.plot(x, yx)
    plt.subplot(3, 1, 2)
    plt.plot(x, yy)
    plt.subplot(3, 1, 3)
    plt.plot(x, yz)
    plt.show

def clean_data(in_filename, out_filename, plot):
    moves_raw = pd.read_csv(in_filename, names = col_names)
    moves_raw = moves_raw[pd.isnull(moves_raw['err1'])]
    moves_raw = moves_raw.drop(["err1",  "err2",  "err3"], axis =1)
    moves_raw = moves_raw.dropna(how = 'any')

    moves_raw['x'] = pd.to_numeric(moves_raw['x'], errors='coerce')
    moves_raw['y'] = pd.to_numeric(moves_raw['y'], errors='coerce')
    moves_raw['z'] = pd.to_numeric(moves_raw['z'], errors='coerce')
    moves_raw['time'] = pd.to_numeric(moves_raw['time'], errors='coerce')

    grouped_moves = moves_raw.groupby('move')
    moves = pd.DataFrame(grouped_moves.apply(lambda x: np.sort(x['x'].as_matrix())))
    moves = moves.rename(index=str, columns={0:'x'}).reset_index()

    temp = pd.DataFrame(grouped_moves.apply(lambda x: np.sort(x['y'].as_matrix())))
    temp = temp.rename(index=str, columns={0:'y'}).reset_index()
    moves = moves.merge(temp, on='move')

    temp = pd.DataFrame(grouped_moves.apply(lambda x: np.sort(x['z'].as_matrix())))
    temp = temp.rename(index=str, columns={0:'z'}).reset_index()
    moves = moves.merge(temp, on='move')

    temp = pd.DataFrame(grouped_moves.apply(lambda x: np.sort(x['time'].as_matrix())))
    temp = temp.rename(index=str, columns={0:'time'}).reset_index()
    moves = moves.merge(temp, on='move')

    moves = moves[moves.apply(check_len, axis=1)]

    moves['move'] = moves['move'].apply(rename)
#    moves = moves.apply(smoothing, axis=1)
    moves = moves.dropna(how = 'any')

    if(plot is not None):
        plot_graph(moves, plot)

    moves_coeffs = pd.DataFrame(moves.apply(polyfitting, axis=1), columns = ['conglomerated'])

    moves_coeffs = pd.DataFrame(moves_coeffs['conglomerated'].values.tolist(), columns = ['move', 'x', 'y', 'z'])
    moves_coeffs[['x3', 'x2', 'x1', 'x0']] = pd.DataFrame(moves_coeffs['x'].values.tolist())
    moves_coeffs[['y3', 'y2', 'y1', 'y0']] = pd.DataFrame(moves_coeffs['y'].values.tolist())
    moves_coeffs[['z3', 'z2', 'z1', 'z0']] = pd.DataFrame(moves_coeffs['z'].values.tolist())
    moves_coeffs = moves_coeffs.drop(['x', 'y', 'z'], axis = 1)
    moves_coeffs = moves_coeffs.dropna(how = 'any')

    if(out_filename is not None):
        moves_coeffs.to_csv(out_filename)

    return moves_coeffs


import networkx as nx
import matplotlib.pyplot as plt

MDR = nx.MultiDiGraph()

MDR.add_nodes_from(["D1","D5"])
MDR.add_edges_from([("D1","D2"),("D1","D4"),("D2","D3"),("D3","D2"),("D3","D4"),("D3","D5"),("D4","D5"),
                    ("D5","D5")])

nx.draw(MDR, node_size = 1000, node_color = 'y', node_shape = 'o', with_labels=True)
plt.draw()
plt.savefig("MDR.eps")
plt.show()


"""
Файл с настройкой конфигурации логгера.
"""

import logging
import logging.config

class FilterError(logging.Filter):
    def filter(self, record):
        return record.levelno == logging.ERROR

dict_config = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "base": {
            "format": "%(levelname)s | %(name)s | %(asctime)s | %(filename)s | %(funcName)s | %(lineno)s | %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "base",
        },
        "info": {
            "class": "logging.handlers.TimedRotatingFileHandler",
            "level": "INFO",
            "when": "H",
            "interval": 24,
            "formatter": "base",
            "filename": "debug.log",
        },
    },
    "loggers": {
        "bot_logger": {
            "level": "INFO",
            "handlers": ["console", "info"]
        },
    },
    "filters": {
        "error": {
            "()": FilterError
        }
    }
}

def custom_logger(logger_name: str) -> logging.Logger:
    """
    Функция - для применения конфигураций Логгера по настройкам
    :param logger_name: str
    :return: Logger
    """
    logging.config.dictConfig(dict_config)
    logger = logging.getLogger(logger_name)
    return logger


import numpy as np
from collections import Counter

"""

"""

# ================
# Assignment 6.1
# ================
print("================")
print("Assignment 6.1")
print("================")
# Naive Bayes assumption!
# P(f1, f2 ... fn | C) = P(f1 | C) * P(f2 | C) ..... P(fn | C)
p_pos  = np.log2(0.09) + np.log2(0.07) + np.log2(0.29) + np.log2(0.04) + np.log2(0.08)
p_neg = np.log2(0.16) + np.log2(0.06) +  np.log2(0.06) + np.log2(0.15) + np.log2(0.11)

#print("Pos: ", p_pos , " Neg: ", p_neg)
if p_pos > p_neg:
    print("This is positive!")
else:
    print("This is negative!")

# More probable class has higher value!

# ================
# Assignment 6.2
# ================
print("================")
print("Assignment 6.2")
print("================")
prior_comedy = 2 / 5
prior_action = 3 / 5

action_sentances = ["fast", "furious", "shoot", "furious", "shoot", "shoot", "fun", "fly", "fast", "shoot", "love"]
comdey_sentances = ["fun", "couple", "love", "love", "couple", "fly", "fast", "fun", "fun"]

# Calculating unigrams!
action_unigram = Counter(action_sentances)
comedy_unigram = Counter(comdey_sentances)
total_unigram = Counter(action_sentances + comdey_sentances)

document = ["fast", "couple", "shoot", "fly"]

# Initialise prob with posterior
action_posterior = np.log(prior_action)
comedy_posterior = np.log(prior_comedy)
action_count = sum(action_unigram.values())
comedy_count = sum(comedy_unigram.values())
# Loop over the document!
for word in document:
    action_posterior += np.log((action_unigram[word] + 1) / (action_count + len(total_unigram)))
    comedy_posterior += np.log((comedy_unigram[word] + 1) / (comedy_count + len(total_unigram)))

# Compare!
print("=============")
print("We determine the sentance to be: ")
if action_posterior > comedy_posterior:
    print("Action!")
else:
    print("Comedy!")

# ================
# Assignment 6.3
# ================

print("================")
print("Assignment 6.3")
print("================")

sentance = "A good good plot and great characters but poor acting".split()
classes = ["pos", "neg"]
documents = [
    ("pos",["good", "good", "good", "great", "great", "great"]),
    ("pos",["poor", "great", "great"]),
    ("neg",["good", "poor", "poor", "poor"]),
    ("neg",["good", "poor", "poor", "poor", "poor", "poor", "great", "great"]),
    ("neg",["poor", "poor"])
]

# Calculate counts in the documents
counters = {}
counters_bin = {}
counters["pos"] = Counter()
counters["neg"] = Counter()
counters_bin["pos"] = Counter()
counters_bin["neg"] = Counter()
for C in classes:
    for p, document in documents:
        if C == p:
            counters[C] += Counter(document)
            counters_bin[C] += Counter(set(document))

counters["tot"] = counters["neg"] + counters["pos"]
counters_bin["tot"] = counters_bin["neg"] + counters_bin["pos"]

# Calculate the posteriors
pos_prior = np.log2(2/5)
pos_bin_prior = np.log2(2/5)
neg_prior = np.log2(3/5)
neg_bin_prior = np.log2(3/5)
pos_count = sum(counters["pos"].values())
neg_count = sum(counters["neg"].values())
pos_bin_count = sum(counters_bin["pos"].values())
neg_bin_count = sum(counters_bin["neg"].values())

for word in sentance:
    # Make sure word is known!
    if word in counters["tot"].keys():
        pos_prior += np.log2( (counters["pos"][word] + 1) / (pos_count + len(counters["tot"])))
        neg_prior += np.log2( (counters["neg"][word] + 1) / (neg_count + len(counters["tot"])))

        pos_bin_prior += np.log2( (counters_bin["pos"][word] + 1) / (pos_bin_count + len(counters_bin["tot"])))
        neg_bin_prior += np.log2( (counters_bin["neg"][word] + 1) / (neg_bin_count + len(counters_bin["tot"])))

print("=============")
print("Normal Byes ")
print("pos: ", pos_prior, " neg: ", neg_prior)

if pos_prior > neg_prior:
    print("Positive!")
else:
    print("Negative!")
print("Binary Byes ")
print("pos: ",pos_bin_prior, " neg: ", neg_bin_prior)

if pos_bin_prior > neg_bin_prior:
    print("Positive!")
else:
    print("Negative!")
print("=============")


import os
import pandas as pd

HISTORY_NAME = 'history.csv'

class TrainingHistory:

    def __init__(self, savedir):
        self._history = None
        self._savedir = savedir

    def end_epoch(self, epoch, metrics):
        values = metrics.values
        values['epoch'] = epoch
        values['time']  = pd.Timestamp.utcnow()

        if self._history is None:
            self._history = pd.DataFrame([ values, ])
        else:
            self._history = self._history.append([ values, ])

        self.save()

    def save(self):
        self._history.to_csv(
            os.path.join(self._savedir, HISTORY_NAME), index = False
        )

    def load(self):
        path = os.path.join(self._savedir, HISTORY_NAME)

        if os.path.exists(path):
            self._history = pd.read_csv(path, parse_dates = [ 'time', ])

    @property
    def history(self):
        return self._history


import numpy as np

from math import floor

DISTANCE_TO_ROOT = "dist_to_root"

DEPTH = "depth"

HEIGHT = "height"

LADDER = "ladder"

VISITED = "visited"

col_chains = [
        'number_sumchain', 'mean_sumchain', 'min_sumchain', '1st_decile_sumchain', '2nd_decile_sumchain',
        '3rd_decile_sumchain', '4th_decile_sumchain', 'median_sumchain', '6th_decile_sumchain', '7th_decile_sumchain',
        '8th_decile_sumchain', '9th_decile_sumchain', 'max_sumchain', 'var_sumchain'
    ]

def add_depth_and_get_max(tre):
    """
    adds depth to each node. Adapted to polytomial (non binary) trees.
    :param tre: ete3.Tree, the tree to which depth should be added
    :return: modifies the original tree + maximum depth
    """
    maxdepth = 0
    for node in tre.traverse('levelorder'):
        if not node.is_root():
            # initiation at children of root
            if node.up.is_root():
                node.add_feature("depth", 1)
            # then, add 1 depth at each level
            else:
                node.add_feature("depth", getattr(node.up, "depth", False) + 1)
                if getattr(node, "depth", False) > maxdepth:
                    maxdepth = getattr(node, "depth", False)
    return maxdepth

def add_ladder(tr):
    """
    adds ladder score to each node.
    :param tr: ete3.Tree, the tree to which ladder score should be added
    :return: modifies the original tree
    """
    for node in tr.traverse('levelorder'):
        if not node.is_root():
            if node.up.is_root():
                if not node.is_leaf():
                    if node.children[0].is_leaf() or node.children[1].is_leaf():
                        node.add_feature("ladder", 0)
                    else:
                        node.add_feature("ladder", -1)
                else:
                    node.add_feature("ladder", -1)
            else:
                if not node.is_leaf():
                    if node.children[0].is_leaf() and node.children[1].is_leaf():
                        node.add_feature("ladder", 0)
                    elif node.children[0].is_leaf() or node.children[1].is_leaf():
                        node.add_feature("ladder", getattr(node.up, "ladder", False) + 1)
                    else:
                        node.add_feature("ladder", 0)
                else:
                    node.add_feature("ladder", -1)
        else:
            node.add_feature("ladder", -1)
    return None

def tree_height(tre):
    """
    Returns the maximum and minimum height of leaves
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: set of floats, max and min leaf height
    """
    root_to_tips = [leaf.dist_to_root for leaf in tre]
    max_h = max(root_to_tips)
    min_h = min(root_to_tips)
    return [max_h, min_h]

def branches(tre):
    """
    Returns branch length metrics (all branches taken into account and external only)
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: set of floats, metrics on all branches
    """
    # branch lengths
    dist_all = [node.dist for node in tre.traverse("levelorder")]
    # external branch lengths
    dist_ext = [leaf.dist for leaf in tre]

    all_bl_mean = np.mean(dist_all)
    all_bl_median = np.median(dist_all)
    all_bl_var = np.nanvar(dist_all)

    ext_bl_mean = np.mean(dist_ext)
    ext_bl_median = np.median(dist_ext)
    ext_bl_var = np.nanvar(dist_ext)

    return [all_bl_mean, all_bl_median, all_bl_var, ext_bl_mean, ext_bl_median, ext_bl_var]

def piecewise_branches(tre, all_max, e_bl_mean, e_bl_median, e_bl_var):
    """
    Returns piecewise branch length metrics
    :param tre: ete3.Tree, tree on which these metrics are computed
    :param all_max: float, tree height
    :param e_bl_mean: float, mean length of external branches
    :param e_bl_median: float, median length of external branches
    :param e_bl_var: float, variance of length of external branches
    :return: list of 18 floats, summary statistics on piecewise branch length
    """
    dist_all_1 = []
    dist_all_2 = []
    dist_all_3 = []

    for node in tre.traverse("levelorder"):
        if node.dist_to_root < all_max / 3 and not node.is_leaf():
            dist_all_1.append(node.dist)
        elif node.dist_to_root < 2 * all_max / 3 and not node.is_leaf():
            dist_all_2.append(node.dist)
        elif node.dist_to_root > 2 * all_max / 3 and not node.is_leaf():
            dist_all_3.append(node.dist)

    if len(dist_all_1) > 0:
        i_bl_mean_1 = np.mean(dist_all_1)
        i_bl_median_1 = np.median(dist_all_1)
        i_bl_var_1 = np.nanvar(dist_all_1)

        ie_bl_mean_1 = np.mean(dist_all_1) / e_bl_mean
        ie_bl_median_1 = np.median(dist_all_1) / e_bl_median
        ie_bl_var_1 = np.nanvar(dist_all_1) / e_bl_var

    else:
        i_bl_mean_1 = 0
        i_bl_median_1 = 0
        i_bl_var_1 = 0

        ie_bl_mean_1 = 0
        ie_bl_median_1 = 0
        ie_bl_var_1 = 0

    if len(dist_all_2) > 0:
        i_bl_mean_2 = np.mean(dist_all_2)
        i_bl_median_2 = np.median(dist_all_2)
        i_bl_var_2 = np.nanvar(dist_all_2)

        ie_bl_mean_2 = np.mean(dist_all_2) / e_bl_mean
        ie_bl_median_2 = np.median(dist_all_2) / e_bl_median
        ie_bl_var_2 = np.nanvar(dist_all_2) / e_bl_var
    else:
        i_bl_mean_2 = 0
        i_bl_median_2 = 0
        i_bl_var_2 = 0

        ie_bl_mean_2 = 0
        ie_bl_median_2 = 0
        ie_bl_var_2 = 0

    if len(dist_all_3) > 0:
        i_bl_mean_3 = np.mean(dist_all_3)
        i_bl_median_3 = np.median(dist_all_3)
        i_bl_var_3 = np.nanvar(dist_all_3)

        ie_bl_mean_3 = np.mean(dist_all_3) / e_bl_mean
        ie_bl_median_3 = np.median(dist_all_3) / e_bl_median
        ie_bl_var_3 = np.nanvar(dist_all_3) / e_bl_var

    else:
        i_bl_mean_3 = 0
        i_bl_median_3 = 0
        i_bl_var_3 = 0

        ie_bl_mean_3 = 0
        ie_bl_median_3 = 0
        ie_bl_var_3 = 0

    return [i_bl_mean_1, i_bl_median_1, i_bl_var_1, ie_bl_mean_1, ie_bl_median_1, ie_bl_var_1, i_bl_mean_2, \
           i_bl_median_2, i_bl_var_2, ie_bl_mean_2, ie_bl_median_2, ie_bl_var_2, i_bl_mean_3, i_bl_median_3, \
           i_bl_var_3, ie_bl_mean_3, ie_bl_median_3, ie_bl_var_3]

def colless(tre):
    """
    Returns colless metric of given tree
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: float, colless metric
    """
    colless_score = 0
    for node in tre.traverse("levelorder"):
        if not node.is_leaf():
            if len(node.children) == 2:
                child1, child2 = node.children
                colless_score += abs(len(child1) - len(child2))
            else:
                scores = []
                for j in range(len(node.children)):
                    for k in range(j + 1, len(node.children)):
                        scores.append(abs(len(node.children[j]) - len(node.children[k])))
                colless_score = np.average(scores)
    return colless_score

def sackin(tre):
    """
    Returns sackin metric
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: float, sackin score computed on the whole tree (sum of this score on all branches)
    """
    sackin_score = 0
    for node in tre.traverse("levelorder"):
        if node.is_leaf():
            sackin_score += int(getattr(node, DEPTH, False))
    return sackin_score

def wd_ratio_delta_w(tre, max_dep):
    """
    Returns two metrics of tree width
    :param tre: ete3.Tree, tree on which these metrics are computed
    :param max_dep: float, maximal depth of tre
    :return: set of two floats, ratio and difference of maximum width and depth
    """
    width_count = np.zeros(max_dep + 1)
    for node in tre.traverse("levelorder"):
        if not node.is_root():
            width_count[int(getattr(node, DEPTH))] += 1
    max_width = max(width_count)
    delta_w = 0
    for j in range(0, len(width_count) - 1):
        if delta_w < abs(width_count[j] - width_count[j - 1]):
            delta_w = abs(width_count[j] - width_count[j - 1])
    return [max_width / max_dep, delta_w]

def max_ladder_il_nodes(tre):
    """
    Returns the maximal ladder score and proportion of internal nodes in ladder
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: set of two floats, metrics
    """
    max_ladder_score = 0
    il_nodes = 0
    for node in tre.traverse("preorder"):
        if not node.is_leaf():
            if node.ladder > max_ladder_score:
                max_ladder_score = node.ladder
            if node.ladder > 0:
                il_nodes += 1
    res = [None, None]
    try:
        res = [max_ladder_score / len(tre), il_nodes / (len(tre) - 1)]
    except ZeroDivisionError as e:
        print(e)
    finally:
        return res

def staircaseness(tre):
    """
    Returns staircaseness metrics
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: set of two floats, metrics
    """
    nb_imbalanced_in = 0
    ratio_imbalance = []
    for node in tre.traverse("preorder"):
        if not node.is_leaf():
            if abs(len(node.children[0]) - len(node.children[1])) > 0:
                nb_imbalanced_in += 1
            if len(node.children[0]) > len(node.children[1]):
                ratio_imbalance.append(len(node.children[1]) / len(node.children[0]))
            else:
                ratio_imbalance.append(len(node.children[0]) / len(node.children[1]))
    res = [None, None]
    try:
        res = [nb_imbalanced_in / (len(tre) - 1), np.mean(ratio_imbalance)]
    except ZeroDivisionError as e:
        print(e)
    finally:
        return res

def ltt_plot(tre):
    """
    Returns an event (branching, removal) matrix
    :param tre: ete3.Tree, tree on which these metrics are computed
    :return: np.matrix, branching and removal events
    """
    events = []

    for node in tre.traverse("levelorder"):
        if node.is_leaf():
            events.append([node.dist_to_root, -1, 0])
        else:
            for j in range(1, len(node.children)):
                events.append([node.dist_to_root, 1, 0])

    events = np.asmatrix(events)
    events = np.sort(events.view('i8, i8, i8'), order=['f0'], axis=0).view(float)

    events[0, 2] = float(events[0, 1]) + 1
    for j in np.arange(1, events.shape[0]):
        events[j, 2] = float(events[j - 1, 2]) + float(events[j, 1])

    return events

def ltt_plot_comput(event_mat):
    """
    Returns LTT plot based metrics
    :param event_mat: np.matrix, branching and removal events
    :return: set of 9 floats, LTT plot based metrics
    """
    from scipy.stats import linregress
    # PART1 find max and max time of LTT plot
    events = event_mat.copy()
    max_lineages = 0
    max_lineages_t = 0
    index_slope_change = 0
    for j in range(events.shape[0]):
        if events[j, 2] > max_lineages:
            max_lineages = events[j, 2]
            max_lineages_t = events[j, 0]
            index_slope_change = j

    # PART2 slopes before and after max
    slope_1 = linregress(np.squeeze(events[0:index_slope_change, 2]), np.squeeze(events[0:index_slope_change, 0]))[0]
    slope_2 = linregress(np.squeeze(events[index_slope_change:, 2]), np.squeeze(events[index_slope_change:, 0]))[0]
    slope_rat = slope_1 / slope_2

    all_max = events[-1, 0]

    # PART3 mean sampling time, mean branching times

    # all sampling and branching times
    sampling_times = []
    branching_times_1 = []
    branching_times_2 = []
    branching_times_3 = []
    for j in range(events.shape[0]):
        if events[j, 1] == -1:
            sampling_times.append(events[j, 0])
        elif events[j, 0] < all_max / 3:
            branching_times_1.append(events[j, 0])
        elif events[j, 0] < 2 * all_max / 3:
            branching_times_2.append(events[j, 0])
        else:
            branching_times_3.append(events[j, 0])

    # differences of consecutive sampling/branching times leading to mean sampling and branching (1st, 2nd and 3rd
    # part) times
    diff_samp_times = []
    diff_b_times_1 = []
    diff_b_times_2 = []
    diff_b_times_3 = []

    for j in range(0, len(sampling_times) - 1):
        diff_samp_times.append(sampling_times[j + 1] - sampling_times[j])
    for j in range(0, len(branching_times_1) - 1):
        diff_b_times_1.append(branching_times_1[j + 1] - branching_times_1[j])
    for j in range(0, len(branching_times_2) - 1):
        diff_b_times_2.append(branching_times_2[j + 1] - branching_times_2[j])
    for j in range(0, len(branching_times_3) - 1):
        diff_b_times_3.append(branching_times_3[j + 1] - branching_times_3[j])

    # mean sampling time
    mean_s_time = np.mean(diff_samp_times)
    if len(diff_b_times_1) > 0:
        mean_b_time_1 = np.mean(diff_b_times_1)
    else:
        mean_b_time_1 = 0

    if len(diff_b_times_2) > 0:
        mean_b_time_2 = np.mean(diff_b_times_2)
    else:
        mean_b_time_2 = 0

    if len(diff_b_times_3) > 0:
        mean_b_time_3 = np.mean(diff_b_times_3)
    else:
        mean_b_time_3 = 0

    return [max_lineages, max_lineages_t, slope_1, slope_2, slope_rat, mean_s_time, mean_b_time_1, mean_b_time_2, mean_b_time_3]

def coordinates_comp(events):
    """
    Returns representation of LTT plot under 20 bins (20 x-axis and 20 y axis coordinates)
    :param events: np.matrix, branching and removal events
    :return: list of 40 floats, y- and x-axis coordinates from LTT plot
    """
    # from math import floor
    binscor = np.linspace(0, events.shape[0], 21)
    y_axis = []
    x_axis = []
    for j in range(int(len(binscor) - 1)):
        y_axis.append(np.average(events[int(floor(binscor[j])):int(floor(binscor[j + 1])), 0]))
        x_axis.append(np.average(events[int(floor(binscor[j])):int(floor(binscor[j + 1])), 2]))

    y_axis.extend(x_axis)
    return y_axis

def add_height(tre):
    """
    adds height to each internal node.
    :param tre: ete3.Tree, the tree to which height should be added
    :return: void, modifies the original tree
    """
    for node in tre.traverse('postorder'):
        if node.is_leaf():
            node.add_feature("height", 0)
        else:
            max_child = 0
            for child in node.children:
                if getattr(child, "height", False) > max_child:
                    max_child = getattr(child, "height", False)
            node.add_feature("height", max_child + 1)
    return None

def compute_chain(node, order=4):
    """
    Return a list of shortest descending path from given node (i.e. 'transmission chain'), of given order at maximum
    :param node: ete3.node, node on which the descending path will be computed
    :param order: int, order of transmission chain
    :return: list of floats, of maximum length (order)
    """
    chain = []
    contin = True
    while len(chain) < order and contin:
        children_dist = [child.dist for child in node.children]

        chain.append(min(children_dist))
        node = node.children[children_dist.index(min(children_dist))]
        if node.is_leaf():
            contin = False
    return chain

def compute_chain_stats(tre, order=4):
    """
    Returns mean, min, deciles and max of all transmission chains of given order
    :param tre: ete3.Tree, tree on which these metrics are computed
    :param order: int, order of transmission chain
    :return: list of floats
    """
    chain_sumlengths = []
    for node in tre.traverse():
        if getattr(node, 'height', False) > (order - 1):
            node_chain = compute_chain(node, order=order)
            if len(node_chain) == order:
                chain_sumlengths.append(sum(node_chain))
    sumstats_chain = [len(chain_sumlengths)]
    if len(chain_sumlengths) > 1:
        # mean
        sumstats_chain.append(np.mean(chain_sumlengths))
        # deciles
        sumstats_chain.extend(np.percentile(chain_sumlengths, np.arange(0, 101, 10)))
        # var
        sumstats_chain.append(np.var(chain_sumlengths))
    else:
        sumstats_chain = [0 for _ in range(len(col_chains))]
    return sumstats_chain