import pyxel
import time
import json
import math
WEB = False
try:
    import requests
    from multiprocessing import Process, Manager, Value
except:
    print("IF NOT USING BROWSER, please install (pip install): requests, multiprocessing")
    import js
    WEB = True


# E:\programs\python\python-3.10.11.3/python -m pip install requests;E:\programs\python\python-3.10.11.3/python -m pip install multiprocessing

HOST = "https://nuit-du-code-api.onrender.com"
# HOST = "http://127.0.0.1:3000"

TILESIZE = 8
AVATARS = [{
    "name": "mike",
    "map": 1,
    "position": (32, 40),
    "sprites": 2
}, {
    "name": "bill",
    "map": 1,
    "position": (32, 48),
    "sprites": 2
}]

ENEMIES = [{
    "name": "zombie",
    "map": 1,
    "position": (0, 104),
    "sprites": 1
}, {
    "name": "skeleton",
    "map": 1,
    "position": (16, 104),
    "sprites": 1
},{
    "name": "snake_1",
    "map": 1,
    "position": (0, 96),
    "sprites": 1
},{
    "name": "snake_2",
    "map": 1,
    "position": (8, 96),
    "sprites": 1
}]
FRUITS = [{
    "name": "banana",
    "map": 1,
    "position": (24, 120),
    "sprites": 1,
    "healing": 20
}, {
    "name": "apple",
    "map": 1,
    "position": (24, 128),
    "sprites": 1,
    "healing": 20
}]
LOADING = [{
    "map": 1,
    "position": (112, 136),
    "sprites": 9,
    "size_x": TILESIZE*2,
    "size_y": TILESIZE*2
}]
START_TEXT = (32, 64)
BOULES = [
    (0, 56),
    (8, 56)
]




X = 128
Y = 128

SIZE = (60, 60)
FRUIT_SPAWN_COORDS = [[0, (23*TILESIZE, 19*TILESIZE)], [0, (43*TILESIZE, 10*TILESIZE)], [0, (45*TILESIZE, 18*TILESIZE)], [0, (67*TILESIZE, 19*TILESIZE)], [0, (71*TILESIZE, 14*TILESIZE)], [0, (20*TILESIZE, 36*TILESIZE)], [0, (16*TILESIZE, 43*TILESIZE)], [0, (23*TILESIZE, 64*TILESIZE)], [0, (24*TILESIZE, 64*TILESIZE)], [1, (16*TILESIZE, 8*TILESIZE)], [1, (27*TILESIZE, 14*TILESIZE)], [1, (55*TILESIZE, 13*TILESIZE)], [1, (61*TILESIZE, 25*TILESIZE)], [1, (18*TILESIZE, 34*TILESIZE)], [1, (19*TILESIZE, 34*TILESIZE)], [1, (17*TILESIZE, 58*TILESIZE)], [1, (50*TILESIZE, 51*TILESIZE)], [1, (51*TILESIZE, 56*TILESIZE)], [1, (23*TILESIZE, 69*TILESIZE)]]
EGGS = [{
    "name": "egg_snake_1",
    "map": 1,
    "position": (0, 88),
    "sprites": 1
},{
    "name": "egg_snake_2",
    "map": 1,
    "position": (8, 88),
    "sprites": 1
}]
SNAKE_EGGS_SPAWN_COORDS = [
    [0, (0*TILESIZE, 26*TILESIZE)], [0, (3*TILESIZE, 34*TILESIZE)], [0, (2*TILESIZE, 48*TILESIZE)], [0, (18*TILESIZE, 19*TILESIZE)], [0, (22*TILESIZE, 22*TILESIZE)], [0, (18*TILESIZE, 19*TILESIZE)],
    [1, (14*TILESIZE, 25*TILESIZE)], [1, (12*TILESIZE, 53*TILESIZE)], [1, (10*TILESIZE, 66*TILESIZE)], [1, (35*TILESIZE, 26*TILESIZE)], [1, (43*TILESIZE, 33*TILESIZE)], [1, (40*TILESIZE, 45*TILESIZE)], [1, (32*TILESIZE, 48*TILESIZE)], [1, (65*TILESIZE, 52*TILESIZE)]]

class Fruit():
    def __init__(self, fruit, position):
        self.animation = Animation(fruit)
        self.healing = fruit["healing"]
        self.x = position[0]
        self.y = position[1]
    
class Egg():
    def __init__(self, egg, position):
        self.egg = egg
        self.animation = Animation(egg)
        self.x = position[0]
        self.y = position[1]
        self.radius = int(round(TILESIZE*1.5))

        if self.egg["name"] == "egg_snake_1":
            self.enemy_spawn = 2
        elif self.egg["name"] == "egg_snake_2":
            self.enemy_spawn = 3


class Player():
    def __init__(self, game):
        self.health = 100
        self.animation = Animation(AVATARS[0])
        self.x = 20
        self.y = 20
        self.speed_x = 3
        self.speed_y = 3
        self.moved = False
        self.looking_right = True
        self.tile_size = 8
        self.game = game
        self.dead = False
         
    def attack(self):
        pass

    def move_right(self):
        if not self.game.possible_position(self.x + self.speed_x, self.y):
            return
        self.x += self.speed_x
        self.looking_right = True
        self.tile_size = 8
        self.move()

    def move_left(self):
        if not self.game.possible_position(self.x - self.speed_x, self.y):
            return
        self.x -= self.speed_x
        self.looking_right = False
        self.tile_size = -8
        self.move()
    
    def move_up(self):
        if not self.game.possible_position(self.x, self.y - self.speed_y):
            return
        self.y -= self.speed_y
        self.move()
    
    def move_down(self):
        if not self.game.possible_position(self.x, self.y + self.speed_y):
            return
        self.y += self.speed_y
        self.move()

    def move(self):
        self.moved = True

    def heal(self, healing):
        if self.health + healing > 100:
            self.health = 100
        else:
            self.health += healing
    def select_avatar(self, avatar):
        self.animation = Animation(avatar)

    def update(self):
        if self.moved:
            self.animation.animate(loop=True)
            self.moved = False
        else:
            self.animation.animate(loop=False)

    def hurt(self, damage):
        self.health -= damage
        if self.health <= 0:
            self.die()
            
    def die(self):
        self.dead = True
    
class Projectile():
    def __init__(self, speed, position, direction) -> None:
        self.speed = speed
        self.x = position[0]
        self.y = position[1]
        self.real_x = position[0]
        self.real_y = position[1]
        self.spawned_at = position
        self.direction = direction
        self.max_distance = 150
        self.sprite = ()
        self.hitbox_size = (TILESIZE, TILESIZE)
        self.hitbox_position = (0, 0)
        if self.direction == "east":
            self.sprite = (0, 136)
            self.hitbox_size = (6, 2)
            self.hitbox_position = (1, 3)
        elif self.direction == "north":
            self.sprite = (0, 144)
            self.hitbox_size = (2, 6)
            self.hitbox_position = (3, 1)
        elif self.direction == "west":
            self.sprite = (0, 136)
            self.hitbox_size = (6, 2)
            self.hitbox_position = (1, 3)
        elif self.direction == "south":
            self.sprite = (0, 144)
            self.hitbox_size = (2, 6)
            self.hitbox_position = (3, 1)
        elif self.direction == "north-east":
            self.sprite = (8, 136)
            self.hitbox_size = (6, 6)
            self.hitbox_position = (1, 1)
        elif self.direction == "south-east":
            self.sprite = (8, 144)
            self.hitbox_size = (6, 6)
            self.hitbox_position = (1, 1)
        elif self.direction == "north-west":
            self.sprite = (8, 144)
            self.hitbox_size = (6, 6)
            self.hitbox_position = (1, 1)
        elif self.direction == "south-west":
            self.sprite = (8, 136)
            self.hitbox_size = (6, 6)
            self.hitbox_position = (1, 1)
        self.animation = Animation({"sprites": 1, "position": self.sprite, "size_x": TILESIZE, "size_y": TILESIZE})

    def move(self):
        if math.sqrt((self.spawned_at[0] - self.x)**2) > self.max_distance or math.sqrt((self.spawned_at[1] - self.y)**2) > self.max_distance:
            #despawn
            return True
        
        if self.direction == "east":
            self.real_x += self.speed
        elif self.direction == "north":
            self.real_y -= self.speed
        elif self.direction == "west":
            self.real_x -= self.speed
        elif self.direction == "south":
            self.real_y += self.speed
        elif self.direction == "north-east":
            self.real_x += self.speed/2
            self.real_y -= self.speed/2
        elif self.direction == "south-east":
            self.real_x += self.speed/2
            self.real_y += self.speed/2
        elif self.direction == "north-west":
            self.real_x -= self.speed/2
            self.real_y -= self.speed/2
        elif self.direction == "south-west":
            self.real_x -= self.speed/2
            self.real_y += self.speed/2
        
        self.x = int(round(self.real_x))
        self.y = int(round(self.real_y))
        return False


class SnakeVenom(Projectile):
    def __init__(self, position, direction) -> None:
        self.speed = 5
        super().__init__(self.speed, position, direction)
        self.damage = 50



class Snake():
    def __init__(self, game, enemy, coords):
        self.health = 15
        self.animation = Animation(enemy)
        self.x = coords[0]
        self.y = coords[1]
        self.speed_x = 0.6
        self.speed_y = 0.6
        self.real_x = coords[0]
        self.real_y = coords[1]
        self.moved = False
        self.looking_right = True
        self.tile_size = 8
        self.game = game
        self.damage = 0
        self.lastTime = time.time()
        self.shoot_rate = 0
        if enemy['name'] == "snake_1":
            self.damage = 5
        elif enemy['name'] == "snake_2":
            self.shoot_rate = 1

    def move(self, x, y):
        self.real_x += x
        self.real_y += y
        self.x = int(round(self.real_x))
        self.y = int(round(self.real_y))

        if x > 0:
            self.tile_size = -8
        elif x < 0:
            self.tile_size = 8
        self.moved = True

        if self.shoot_rate > 0:
            if time.time() - self.lastTime >= self.shoot_rate:
                dist_y =  self.y - self.game.player.y
                dist_x = self.game.player.x -self.x
                # spawn venom
                # opp = math.sqrt((dist_y**2))
                # adj = math.sqrt((dist_x**2))
                opp = dist_y
                adj = dist_x
                if opp == 0 or adj == 0:
                    return
                direction_deg = math.degrees( math.atan((opp/adj)))
                coeff = math.sqrt((direction_deg/45)**2)
                if dist_y > 0:
                    if dist_x > 0:
                        pass
                    else:
                        coeff = 4 - coeff
                else:
                    if dist_x > 0:
                        coeff = 8 - coeff
                    else:
                        coeff += 4
                coeff = int(round(coeff))
                direction = ''
                if coeff == 0:
                    direction = "east"
                elif coeff == 1:
                    direction = "north-east"
                elif coeff == 2:
                    direction = "north"
                elif coeff == 3:
                    direction = "north-west"
                elif coeff == 4:
                    direction = "west"
                elif coeff == 5:
                    direction = "south-west"
                elif coeff == 6:
                    direction = "south"
                elif coeff == 7:
                    direction = "south-east"
                elif coeff == 8:
                    direction = "east"
                else:
                    return
                
                self.game.projectiles.append(SnakeVenom((self.x, self.y), direction))
                self.lastTime = time.time()
            return

        if not self.game.enemy_collision(self.x + x, self.y + y, self):
            return

class Enemy():
    def __init__(self, game, enemy, coords):
        self.health = 100
        self.animation = Animation(enemy)
        self.x = coords[0]
        self.y = coords[1]
        self.speed_x = 1
        self.speed_y = 1
        self.moved = False
        self.looking_right = True
        self.tile_size = 8
        self.game = game
        self.damage = 1

    def move(self, x, y):

        if not self.game.enemy_collision(self.x + x, self.y + y, self):
            return
        self.x += x
        self.y += y

        if x > 0:
            self.tile_size = 8
        elif x < 0:
            self.tile_size = -8
        self.moved = True
    







class Animation():
    def __init__(self, avatar):
        self.position = avatar["position"]
        self.sprites = avatar["sprites"]
        self.x = self.position[0]
        self.y = self.position[1]
        self.avatar = avatar
        self.at = 0
        self.animation = False
        self.size_x = TILESIZE
        self.size_y = TILESIZE
        if "size_x" in avatar:
            self.size_x = avatar["size_x"]
        if "size_y" in avatar:
            self.size_y = avatar["size_y"]


    def animate(self, loop=False):
        if loop:
            self.animation = True
        if self.animation and self.sprites > 1:
            if self.at < self.sprites - 1:
                self.at += 1
            else:
                self.at = 0
                if loop is False:
                    self.animation = False
            
        
        

        

        self.update()

    def update(self):
        self.x = self.position[0] + self.at * self.size_x


class Game():

    def __init__(self):

        pyxel.init(X, Y, title="NDC 2023")

        self.player = Player(self)
        self.start = False
        self.current_avatar = 0
        self.avatar = 0
        self.enemies = []
        self.time = time.time()
        self.spawn_interval = 2
        self.spawn_rate = 1
        self.gameover = False
        self.end = False
        self.t = time.time()
        self.g = 0
        self.fruits = []
        self.username = ""
        self.input_active = False
        self.username_selected = False
        self.scores = Scores()
        self.account_screen = False
        self.show_password = False
        self.loading_animation = Animation(LOADING[0])
        self.loading_animation_looped = False
        self.snake_eggs = []
        self.projectiles = []
        self.back_space_pressed_time = time.time()
        self.max_back_space_pressed_time = 0.5

        for i in FRUIT_SPAWN_COORDS:
            self.fruits.append(Fruit(FRUITS[i[0]], i[1]))
        for i in SNAKE_EGGS_SPAWN_COORDS:
            self.snake_eggs.append(Egg(EGGS[i[0]], i[1]))


        # init form
        # create forms
        self.main_form = Form()
        self.select_form = Form()
        self.login_form = Form(self.login)
        self.create_form = Form(self.create)

        # main form
        self.main_form.add(Button("Back", self.back))
        # select form
        btn = Button("Create account", self.create_mode)
        btn.add_styles({"display": "inline-spaced"})
        btn2 = Button("Login", self.login_mode)
        btn2.x = 128-btn2.width
        self.select_form.add(btn)
        self.select_form.add(btn2)
        self.select_form.y_offset = 10
        # login form
        self.login_form.add(Input("username", "", "username: "))
        self.login_form.add(Input("password", "", "password: "))
        self.login_form.add(Button("Login", self.login))
        self.login_form.y_offset = 3
        # create form
        self.create_form.add(Input("username", "", "username: "))
        self.create_form.add(Button("Create account", self.create))
        self.create_form.y_offset = 3


        # link
        self.select_form.add(self.login_form)
        self.select_form.add(self.create_form)
        self.main_form.add(self.select_form)

        # format
        self.login_form.add_styles({"position": "absolute"})
        self.create_form.add_styles({"position": "absolute"})
        self.select_form.add_styles({"display": "inline-spaced"})
        self.main_form.start_format()
        self.login_form.hide()
        # end of form

        pyxel.load("new.pyxres")
        pyxel.playm(0, loop=True)
        pyxel.run(self.update, self.render)


    def login(self, data):
        print("login", data)
        if len(data["username"]) < self.scores.username_min_length or len(data["password"]) < self.scores.password_min_length:
            return
        self.scores.start_login(data["username"], data["password"])
        self.login_form.clear()
    def create(self, data):
        print("creating account", data)
        if len(data["username"]) < self.scores.username_min_length:
            return
        self.scores.start_create_account(data["username"])
        self.create_form.clear()
    def back(self, data):
        print("going back", data)
        self.account_screen = False
    def login_mode(self, data):
        print("switching to login", data)
        self.login_form.show()
        self.create_form.hide()
    def create_mode(self, data):
        print("switching to create", data)
        self.create_form.show()
        self.login_form.hide()

    


    def player_collision(self):

        for fruit in self.fruits:
            if not self.check_collision(self.player.x, self.player.y, TILESIZE, TILESIZE, fruit.x, fruit.y, TILESIZE, TILESIZE):
                # use fruit
                self.player.heal(fruit.healing)
                self.fruits.remove(fruit)

    def wall_collision(self,):
        x = self.player.x
        y = self.player.y

            

    def key_input(self):
        if self.gameover:
            if pyxel.btnp(pyxel.KEY_SPACE):
                self.flush()
                self.end = False
                self.gameover = False
        elif not self.start:
            key_return =  pyxel.btnp(pyxel.KEY_RETURN)
            if self.account_screen:
                # render ui
                self.main_form.update_native_data()
                # show user data
                if pyxel.btnp(pyxel.KEY_CAPSLOCK):
                    if not self.scores.loading and self.scores.verified_account and self.account_screen:
                        self.show_password = not self.show_password
                
                if pyxel.btnp(pyxel.KEY_RSHIFT):
                    if not self.scores.loading and self.scores.verified_account and self.account_screen:
                        self.scores.log_out()
            else:
                if key_return:
                    # go account screen
                    self.account_screen = True

                if pyxel.btnp(pyxel.KEY_RIGHT):
                    if self.current_avatar < len(AVATARS)-1:
                        self.current_avatar += 1
                if pyxel.btnp(pyxel.KEY_LEFT):
                    if self.current_avatar > 0:
                        self.current_avatar -= 1
                
                
                if pyxel.btnp(pyxel.KEY_SPACE):
                    self.avatar = self.current_avatar
                    self.player.select_avatar(AVATARS[self.avatar])
                    self.t = time.time()
                    self.time = time.time()
                    self.start = True
        else:
            if pyxel.btn(pyxel.KEY_RIGHT):
                self.player.move_right()
            if pyxel.btn(pyxel.KEY_LEFT):
                self.player.move_left()
            if pyxel.btn(pyxel.KEY_DOWN):
                self.player.move_down()
            if pyxel.btn(pyxel.KEY_UP):
                self.player.move_up()

    def check_collision(self, x1, y1, w1, h1, x2, y2, w2, h2):
        # can move -> true
        if x1 < x2 + w2 and x1 + w1 > x2 and y1 + h1 > y2 and y1 < y2 + h2:
            return False
        return True
    
    


    def enemy_collision(self, x, y, enemy):
        # collision
        col = self.check_collision(x, y, TILESIZE, TILESIZE, self.player.x, self.player.y, TILESIZE, TILESIZE)
        if not col:
            self.player.hurt(enemy.damage)
            # self.enemies.remove(enemy)
        return col

    def random_corrds_around_player(self):
        half_x = round(X/4)
        half_y = round(Y/4)
        x = 0
        y = 0

        x1 = pyxel.rndi(self.player.x -half_x*2, self.player.x - half_x)
        x2 = pyxel.rndi(self.player.x +half_x*2, self.player.x + half_x)
        y1= pyxel.rndi(self.player.y -half_y*2, self.player.y - half_y)
        y2= pyxel.rndi(self.player.y +half_y*2, self.player.y + half_y)

        if pyxel.rndi(0, 1):
            x = x1
        else:
            x = x2
        if pyxel.rndi(0, 1):
            y = y1
        else:
            y = y2

        return x, y
        

    def update(self):
        self.key_input()
        self.player.update()
        self.scores.loop()
        if self.start:

            if self.player.dead:
                self.gameover = True
                # upload score, download scores
            if self.gameover:
                return
            if time.time() > self.time+self.spawn_interval:
                for i in range(0, round(self.spawn_rate)):
                    # pass
                    self.enemies.append(Enemy(self, ENEMIES[0], self.random_corrds_around_player()))
                self.time = time.time()
                # self.spawn_interval -= 0.5
                # self.spawn_rate += 0.4
            for projectile in self.projectiles:
                # check for collision
                if not self.check_collision(self.player.x, self.player.y, TILESIZE, TILESIZE, projectile.x+projectile.hitbox_position[0], projectile.y+projectile.hitbox_position[1], projectile.hitbox_size[0], projectile.hitbox_size[1]):
                    # collision
                    #despawn
                    self.player.hurt(projectile.damage)
                    self.projectiles.remove(projectile)
                else:
                    if projectile.move():
                        #despawn
                        self.projectiles.remove(projectile)
                    

            for enemy in self.enemies:
                x = 0
                y = 0
                if enemy.x < self.player.x:
                    x = enemy.speed_x
                else:
                    x = -enemy.speed_x
                if enemy.y < self.player.y:
                    y = enemy.speed_y
                else:
                    y = -enemy.speed_y
                enemy.move(x, y)
                enemy.animation.animate()
    def flush(self):
        self.player = Player(self)
        self.start = False
        self.current_avatar = 0
        self.avatar = 0
        self.enemies = []
        self.time = time.time()
        self.spawn_interval = 2
        self.spawn_rate = 1
        self.t = time.time()
        self.snake_eggs = []
        self.fruits = []
        self.projectiles = []

        for i in FRUIT_SPAWN_COORDS:
            self.fruits.append(Fruit(FRUITS[i[0]], i[1]))

        for i in SNAKE_EGGS_SPAWN_COORDS:
            self.snake_eggs.append(Egg(EGGS[i[0]], i[1]))

    def render(self):
        pyxel.cls(0)
        
        if self.gameover:
            pyxel.camera(0, 0)
            pyxel.text(45, 40,"Game Over", 1)
            pyxel.text(45, 50, str(self.g) + " seconds", 1)
            pyxel.text(20, 110, "main menu, press [SPACE]", 1)
            at = 0
            for user in self.scores.best_scores:
                pyxel.text(30, 70+at*7, str(user["username"]) + " : "+str(user["score"])+" seconds", 10)
                at += 1
            # loading
            if self.scores.loading:
                self.loading_animation.animate(loop=True)
                if self.loading_animation_looped:
                    if self.loading_animation.at == 0:
                        self.loading_animation_looped = False
                    pyxel.blt(50, 85, 1, self.loading_animation.x, self.loading_animation.y, TILESIZE*2, -TILESIZE*2, 0)
                else:
                    if self.loading_animation.at == 0:
                        self.loading_animation_looped = True
                    pyxel.blt(50, 85, 1, self.loading_animation.x, self.loading_animation.y, TILESIZE*2, TILESIZE*2, 0)
            elif self.scores.show_for > 0:
                    if time.time() - self.scores.show_start < self.scores.show_for:
                        if len(self.scores.message) > 25:
                            pyxel.text(0, 80, self.scores.message[:25], 8)
                            pyxel.text(0, 90, self.scores.message[25:], 8)
                        else:
                            pyxel.text(0, 80, self.scores.message, 8)
                    else:
                        self.scores.show_for = 0
            #end loading
            if not self.end:
                self.end = True
                self.g = round(time.time()-self.t)
                self.scores.start_update_get_scores(self.g)
                # self.scores.start_update_score(self.g)
                # self.scores.start_get_scores()
        elif self.start:
            pyxel.bltm(0, 0, 0, 0, 0, 128*TILESIZE, 128*TILESIZE)
            
            # camera
            x, y = self.camera()
            # render fruits in vision
            for fruit in self.fruits:
                if fruit.x > x-TILESIZE and fruit.x < x+X and fruit.y > y-TILESIZE and fruit.y < y+Y:
                    pyxel.blt(fruit.x, fruit.y, 1, fruit.animation.x, fruit.animation.y, TILESIZE, TILESIZE, 11)
            for egg in self.snake_eggs:
                if egg.x > x-TILESIZE and egg.x < x+X and egg.y > y-TILESIZE and egg.y < y+Y:
                    pyxel.blt(egg.x, egg.y, 1, egg.animation.x, egg.animation.y, TILESIZE, TILESIZE, 11)
                    # check if in player range
                    if not self.check_collision(self.player.x, self.player.y, TILESIZE, TILESIZE, egg.x-egg.radius, egg.y-egg.radius, TILESIZE+egg.radius*2, TILESIZE+egg.radius*2):
                        
                        # spaw snake
                        self.enemies.append(Snake(self, ENEMIES[egg.enemy_spawn], (egg.x, egg.y)))
                        # despawn egg 
                        self.snake_eggs.remove(egg)
            pyxel.blt(self.player.x, self.player.y, 1, self.player.animation.x, self.player.animation.y, self.player.tile_size, TILESIZE, 0)
            
            for projectile in self.projectiles:
                pyxel.blt(projectile.x, projectile.y, 1, projectile.animation.x, projectile.animation.y, TILESIZE, TILESIZE, 0)

            pyxel.text(x+5, y+5, str(self.player.health) + " health", 2)
            pyxel.camera(x, y)
            for enemy in self.enemies:
                if enemy.x > x-10 and enemy.x < x+10+X and enemy.y > y-10 and enemy.y < y+10+Y:
                    pyxel.blt(enemy.x, enemy.y, 1, enemy.animation.x, enemy.animation.y, enemy.tile_size, TILESIZE, 0)
        else:
            # login
            if self.account_screen:
                self.main_form.render()
                # pyxel.text(0, 5, "to return to menu, press [TAB]", 10)
                # pyxel.text(0, 15, "to toggle login/signup", 10)
                # pyxel.text(0, 25, "press [SPACE]", 10)
                # pyxel.text(0, 35, "use up, down keys for form", 10)
                # state = ''
                # if self.scores.login_mode:
                #     state = "login"
                # else:
                #     state = "create account"
                # pyxel.text(0, 45, "state: " + state, 10)
                # if self.scores.username_selected:
                #     pyxel.text(0, 55, "username: " + str(self.scores.username), 11)
                # else:
                #     pyxel.text(0, 55, "username: " + str(self.scores.username), 10)
                # if self.scores.login_mode:
                #     if not self.scores.username_selected:
                #         pyxel.text(0, 65, "password: " + str(self.scores.password), 11)
                #     else:
                #         pyxel.text(0, 65, "password: " + str(self.scores.password), 10)
                if self.scores.loading:
                    pyxel.text(5, 75, "loading...", 10)
                    self.loading_animation.animate(loop=True)
                    if self.loading_animation_looped:
                        if self.loading_animation.at == 0:
                            self.loading_animation_looped = False
                        pyxel.blt(10, 85, 1, self.loading_animation.x, self.loading_animation.y, TILESIZE*2, -TILESIZE*2, 0)
                    else:
                        if self.loading_animation.at == 0:
                            self.loading_animation_looped = True
                        pyxel.blt(10, 85, 1, self.loading_animation.x, self.loading_animation.y, TILESIZE*2, TILESIZE*2, 0)
                # pyxel.text(0, 95, "loading...", 8)

                if self.scores.verified_account:
                    pyxel.text(0, 95, "current account: " + str(self.scores.account["username"]), 8)
                    pyxel.text(0, 112, "to log out, press [RIGHT SHIFT]", 10)
                    if self.show_password:
                        pyxel.text(0, 105, "password: " + self.scores.account["password"] , 8)
                        pyxel.text(0, 120, "to hide pswd, press [CAPSLOCK]" , 10)
                    else:
                        pyxel.text(0, 120, "to show pswd, press [CAPSLOCK]" , 10)

                if self.scores.show_for > 0:
                    if time.time() - self.scores.show_start < self.scores.show_for:
                        if len(self.scores.message) > 25:
                            pyxel.text(0, 110, self.scores.message[:25], 8)
                            pyxel.text(0, 120, self.scores.message[25:], 8)
                        else:
                            pyxel.text(0, 110, self.scores.message, 8)
                    else:
                        self.scores.show_for = 0
                

            else:
                pyxel.mouse(False)
                pyxel.text(0, 5, "to access account, press [enter]", 10)
                pyxel.text(5, 12, "high score: " + str(self.scores.best_score), 9)
                pyxel.text(40, 35, "THE CHALLENGE", 11)
                # start screen
                pyxel.blt(50, 50, 1, START_TEXT[0], START_TEXT[1], TILESIZE*4, TILESIZE*2, 0)
                if (self.current_avatar == 0):
                    pyxel.blt(50, 85, 1, BOULES[0][0], BOULES[0][1], TILESIZE, TILESIZE, 0)
                else:
                    pyxel.blt(70, 85, 1, BOULES[1][0], BOULES[1][1], TILESIZE, TILESIZE, 0)
                pyxel.blt(50, 75, 1, AVATARS[0]["position"][0], AVATARS[0]["position"][1], TILESIZE, TILESIZE, 0)
                pyxel.blt(70, 75, 1, AVATARS[1]["position"][0], AVATARS[1]["position"][1], TILESIZE, TILESIZE, 0)
                pyxel.text(10, 105, "right key, left key to pick", 1)
                pyxel.text(35, 115, "space to select", 1)

    def possible_position(self, x, y):
        self.player_collision()
        if x < TILESIZE or x > SIZE[0]*TILESIZE:
            return False
        if y < TILESIZE or y > SIZE[1]*TILESIZE:
            return False
        return True

    def camera(self):
        half_x = X/2
        half_y = Y/2
        x = 0
        y = 0
        if self.player.x < half_x:
            x = 0
        else:
            x = self.player.x - half_x
        if self.player.y < half_y:
            y = 0
        else:
            y = self.player.y - half_y
        
        return x, y

    
class Scores():
    def __init__(self):
        self.username = ''
        self.password = ""
        self.password_done = False
        self.username_done = False
        self.input_active = False
        self.login_mode = True
        self.account = {"username": "", "password": "", "score": 0}
        self.verified_account = False
        self.password_min_length = 6
        self.username_min_length = 2
        self.username_selected = True
        self.possible_actions = ["update", "create", "login", "get", "update-get"]
        self.loading = False
        self.message = ""
        self.show_for = 0
        self.show_start = time.time()
        self.best_scores = []
        self.best_score = 0
        self.processes = []
        self.active_slot = 0

        
        if not WEB:
            # IMPORTANT, access through .value
            self.manager = Manager()
        else:
            self.slots = []
            self.slot = -1
            self.valid = True
            self.web_queue = []

    def log_out(self):
        self.verified_account = False
        self.account = {"username": "", "password": "", "score": 0}
        self.best_score = 0

    def loop(self):
        loading = False
        if WEB:
            if self.valid and len(self.web_queue) > 0:
                self.valid = False
                self.slot = self.web_queue[0]
                self.web_get_request(self.slots[self.slot]["url"])
            for slot in self.slots:
                if slot["finished"] and not slot["valid"]:
                    # finished
                    if slot["action_type"] == "get":
                        self.web_get_scores(slot)
                    elif slot["action_type"] == "update":
                        self.web_update_score(slot)
                    elif slot["action_type"] == "create":
                        self.web_create_account(slot)
                    elif slot["action_type"] == "login":
                        self.web_login(slot)
                    elif slot["action_type"] == "update-get":
                        self.web_update_get_score(slot)
                if not slot["finished"] and not slot["valid"]:
                    loading = True
        else:
            for pr in self.processes:
                if pr["finished"].value and not pr["valid"]:
                    # finished
                    if pr["action_type"] == "get":
                        self.get_scores(pr)
                    elif pr["action_type"] == "update":
                        self.update_score(pr)
                    elif pr["action_type"] == "create":
                        self.create_account(pr)
                    elif pr["action_type"] == "login":
                        self.login(pr)
                    elif pr["action_type"] == "update-get":
                        self.update_get_score(pr)
                if not pr["finished"].value and not pr["valid"]:
                    loading = True
        self.loading = loading

    def enter(self):
        if self.login_mode:
            if len(self.username) >= self.username_min_length and len(self.password) >= self.password_min_length:
                self.start_login(self.username, self.password)
        elif len(self.username) >= self.username_min_length:
            self.start_create_account(self.username)

    def set_mode_login(self):
        self.login_mode = True

    def set_mode_create(self):
        self.login_mode = False
        self.username_selected = True
           

    def remove_last_letter(self):
        if self.username_selected:
            self.username = self.username[:len(self.username)-1]
        else:
            self.password = self.password[:len(self.password)-1]
        # if self.input_active:
        #     if not self.username_done:
        #         self.username = self.username[:len(self.username)-1]
        #     elif not self.password_done:
        #         self.password = self.password[:len(self.password)-1]
    
    def select_username(self):
        self.username_selected = True
    def select_password(self):
        self.username_selected = False
    
                    
    def toggle_input(self):
        self.input_active = not self.input_active
        if not self.input_active:
            if not self.username_done and len(self.username) > 1:
                self.username_done = True
                if not self.login_mode:
                    # create account
                    self.create_account(self.username)
            elif not self.password_done and len(self.password) > 5:
                self.password_done = True
                if self.login_mode:
                    #verify account (login)
                    self.login(self.username, self.password)

    def clean(self, pr):
        self.password = ""
        self.username = ""
        # loading = False
        # # FUCKING BU used down here pr instead of _pr, so fucked up lower lines, where it would reset the wrong pr !!!
        # for _pr in self.processes:
        #     if not _pr["finished"].value:
        #         loading = True
        # self.loading = loading
        pr["action_type"] = ''
        pr["finished"].value = False
        pr["username_pending"] = ""
        pr["password_pending"] = ""
        pr["response"]['error'] = False
        pr["valid"] = True

    def web_clean(self, slot):
        self.password = ""
        self.username = ""
        slot["action_type"] = ''
        slot["finished"] = False
        slot["username_pending"] = ""
        slot["password_pending"] = ""
        slot["error"] = False
        slot["valid"] = True

    def web_update_score(self, slot):
        if not slot["finished"] or not slot["action_type"] in self.possible_actions:
             return
        if slot['error']:
            # error
            self.message = slot["data"]
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.web_clean(slot)
            return
        if self.verified_account:
            content = slot["data"]
            if (content["valid"]):
                self.account["score"] = content["score"]
                self.verified_account = True
                self.best_score = content["score"]
                self.update_best_scores()
            else:
                self.message = "error"
                self.show_for = 3
                self.show_start = time.time()
                self.log_out()
        self.web_clean(slot)

    def web_update_get_score(self, slot):
        if not slot["finished"] or not slot["action_type"] in self.possible_actions:
             return
        if slot['error']:
            # error
            self.message = slot["data"]
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.web_clean(slot)
            return
        if self.verified_account:
            content = slot["data"]
            if (content["valid"]):
                self.account["score"] = content["score"]
                self.verified_account = True
                self.best_score = content["score"]
                self.best_scores = content["scores"]
            else:
                self.message = "error"
                self.show_for = 3
                self.show_start = time.time()
                self.log_out()
                self.best_scores = content["scores"]
        self.web_clean(slot)

    def update_get_score(self, process):
        if not process["finished"].value or not process["action_type"] in self.possible_actions:
             return
        process["process"].join()
        if process["response"]['error']:
            # error
            self.message = process["response"]['error_message']
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.clean(process)
            return
        if self.verified_account:
            content = json.loads(process["response"]['state'])
            if (content["valid"]):
                self.account["score"] = content["score"]
                self.verified_account = True
                self.best_score = content["score"]
                self.best_scores = content["scores"]
            else:
                self.message = "error"
                self.show_for = 3
                self.show_start = time.time()
                self.log_out()
                self.best_scores = content["scores"]
        self.clean(process)
        
    def update_score(self, process):
        if not process["finished"].value or not process["action_type"] in self.possible_actions:
             return
        process["process"].join()
        if process["response"]['error']:
            # error
            self.message = process["response"]['error_message']
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.clean(process)
            return
        if self.verified_account:
            content = json.loads(process["response"]['state'])
            if (content["valid"]):
                self.account["score"] = content["score"]
                self.verified_account = True
                self.best_score = content["score"]
                self.update_best_scores()
            else:
                self.message = "error"
                self.show_for = 3
                self.show_start = time.time()
                self.log_out()
        self.clean(process)

    def start_update_score(self, score):
        if WEB:
            if not score > self.best_score:
                return
            if not self.verified_account:
                if score > self.best_score:
                    self.best_score = score
                return
            slot = self.web_select_valid_slot()
            slot["url"] = HOST+f'/users/update?username={self.account["username"]}&password={self.account["password"]}&score={score}'
            slot["action_type"] = "update"
        else:
            if not score > self.best_score:
                return
            if not self.verified_account:
                if score > self.best_score:
                    self.best_score = score
                return
            process = self.select_valid_process()
            payload = dict(username=self.account["username"], password=self.account["password"], score=score)
            process["process"] = Process(target=_post, args=(HOST+'/users/update', payload, process["response"] , process["finished"]))
            process["process"].start()
            process["action_type"] = "update"
        self.loading = True

    def select_valid_process(self):
        for pr in self.processes:
            if pr["valid"] == True:
                pr["valid"] = False
                return pr
        
        self.processes.append({"valid": False, "finished": Value("i", False), "username_pending": "", "password_pending": "", "process": 0, "action_type": "", "response": self.manager.dict()})
        self.processes[-1]["response"]["error"] = False
        return self.processes[-1]
    
    def update_best_scores(self):
        pos = -1
        at = 0
        if len(self.best_scores) > 0 and self.verified_account:
            for user in self.best_scores:
                # user["username"]
                if self.best_score > user["score"]:
                    self.pos = at
                    break
                at += 1
        if pos >= 0 and pos < len(self.best_scores):
            # insert
            self.best_scores.insert(at, {"username": self.account["username"], "score": self.best_score})
            self.best_scores.remove(self.best_scores[-1])

    def web_create_account(self, slot):   
        if not slot["finished"] or not slot["action_type"] in self.possible_actions:
             return
        if slot['error']:
            # error
            self.message = slot["data"]
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.web_clean(slot)
            return
        content = slot["data"]
        if (content["valid"]):
            self.verified_account = True
            self.account["username"] = slot["username_pending"]
            self.account["password"] = content["password"]
            self.account["score"] = 0
            self.best_score = 0
        else:
            self.message = "unavailable username"
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
        self.web_clean(slot)

    def create_account(self, process):   
        if not process["finished"].value or not process["action_type"] in self.possible_actions:
             return
        process["process"].join()
        if process["response"]['error']:
            # error
            self.message = process["response"]['error_message']
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.clean(process)
            return
        content = json.loads(process["response"]['state'])
        if (content["valid"]):
            self.verified_account = True
            self.account["username"] = process["username_pending"]
            self.account["password"] = content["password"]
            self.account["score"] = 0
            self.best_score = 0
        else:
            self.message = "unavailable username"
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
        self.clean(process)


    def start_create_account(self, username):
        if WEB:
            slot = self.web_select_valid_slot()
            slot["url"] = HOST+f'/users/username?username={username}'
            slot["action_type"] = 'create'
            slot["username_pending"] = username
        else:
            process = self.select_valid_process()
            payload = dict(username=username)
            process["process"] = Process(target=_post, args=(HOST+'/users/username', payload, process["response"] , process["finished"]))
            process["process"].start()
            process["action_type"] = 'create'
            process["username_pending"] = username
        self.loading = True

    def web_login(self, slot):
        if not slot["finished"] or not slot["action_type"] in self.possible_actions:
             return
        if slot['error']:
            # error
            self.message = slot["data"]
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.web_clean(slot)
            return
        
        content = slot["data"]
        if (content["valid"]):
            self.verified_account = True
            self.account["username"] = slot["username_pending"]
            self.account["password"] = slot["password_pending"]
            self.account["score"] = content["score"]
            self.best_score = content["score"]
        else:
            self.message = "invalid credentials"
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
        self.web_clean(slot)

    def login(self, process):
        if not process["finished"].value or not process["action_type"] in self.possible_actions:
             return
        process["process"].join()
        if process["response"]['error']:
            # error
            self.message = process["response"]['error_message']
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
            self.clean(process)
            return
        content = json.loads(process["response"]['state'])
        if (content["valid"]):
            self.verified_account = True
            self.account["username"] = process["username_pending"]
            self.account["password"] = process["password_pending"]
            self.account["score"] = content["score"]
            self.best_score = content["score"]
        else:
            self.message = "invalid credentials"
            self.show_for = 3
            self.show_start = time.time()
            self.log_out()
        self.clean(process)

    def start_login(self, username, password):
        if WEB:
            slot = self.web_select_valid_slot()
            slot["url"] = HOST+f'/users/account?username={username}&password={password}'
            slot["action_type"] = "login"
            slot["username_pending"] = username
            slot["password_pending"] = password
        else:
            process = self.select_valid_process()
            payload = dict(username=username, password=password)
            process["process"] = Process(target=_post, args=(HOST+'/users/account', payload, process["response"] , process["finished"]))
            process["process"].start()
            process["action_type"] = "login"
            process["username_pending"] = username
            process["password_pending"] = password
        self.loading = True

    def web_get_scores(self, slot):
        if not slot["finished"] or not slot["action_type"] in self.possible_actions:
                return
        if slot["error"]:
            # error
            self.message = slot["data"]
            self.show_for = 3
            self.show_start = time.time()
            self.web_clean(slot)
            return
        self.best_scores = slot["data"]["scores"]
        self.web_clean(slot)

    def get_scores(self, process):
        if not process["finished"].value or not process["action_type"] in self.possible_actions:
            return
        process["process"].join()
        if process["response"]['error']:
            # error
            self.message = process["response"]['error_message']
            self.show_for = 3
            self.show_start = time.time()
            self.clean(process)
            return
        content = json.loads(process["response"]['state'])
        self.best_scores = content["scores"]
        self.clean(process)

    def web_select_valid_slot(self):
        at = 0
        for slot in self.slots:
            if slot["valid"] == True:
                slot["valid"] = False
                self.web_queue.append(at)
                return slot
            at += 1
        
        self.slots.append({"valid": False, "finished": False, "username_pending": "", "password_pending": "", "action_type": "", "data": {}, "error": False})
        self.web_queue.append(len(self.slots)-1)
        return self.slots[-1]

    def start_get_scores(self):
        if WEB:
            slot = self.web_select_valid_slot()
            slot["action_type"] = "get"
            slot["url"] = HOST+'/users'
        else:
            process = self.select_valid_process()
            process["process"] = Process(target=_get, args=(HOST+'/users', process["response"] , process["finished"]))
            process["process"].start()
            process["action_type"] = "get"
        # global
        self.best_scores = []
        self.loading = True
    
    def start_update_get_scores(self, score):
        self.best_scores = []
        if not score > self.best_score:
            self.start_get_scores()
            return
        if not self.verified_account:
            if score > self.best_score:
                self.best_score = score
                self.start_get_scores()
            return
        if WEB:
            slot = self.web_select_valid_slot()
            slot["action_type"] = "update-get"
            slot["url"] = HOST+f'/users/update-get?username={self.account["username"]}&password={self.account["password"]}&score={score}'
        else:
            process = self.select_valid_process()
            payload = dict(username=self.account["username"], password=self.account["password"], score=score)
            process["process"] = Process(target=_post, args=(HOST+'/users/update-get', payload, process["response"] , process["finished"]))
            process["process"].start()
            process["action_type"] = "update-get"
        # global
        self.loading = True

    def web_get_request(self, url):
        # js.fetch('http://localhost:3000/users/update-get?username=aa&password=aa&score=1').then(res)
        js.fetch(url).then(self.web_gotten).catch(self.web_error)
    def web_gotten(self, response):

        response.text().then(self.web_result)
    def web_result(self, result):
        data = json.loads(result)
        slot = self.slot
        self.slots[slot]["data"] = data
        self.slots[slot]["finished"] = True
        self.valid = True
        del self.web_queue[0]
    def web_error(self, error):
        self.slots[self.slot]["error"] = True
        self.slots[self.slot]["data"] = "maybe check your internet connection"
        self.slots[self.slot]["finished"] = True
        self.valid = True
        del self.web_queue[0]

    def input(self, text):
        if text == " ":
            return
        if self.username_selected:
            self.username += text
        else:
            self.password += text
        # if self.input_active:
        #     if not self.username_done:
        #         self.username += text
        #     elif not self.password_done:
        #         self.password += text





def _get(url, response, finished):
    try:
        r = requests.get(url)
        response['state'] = r.content
        # response['state'] = b'{"scores":[{"username":"isaac","score":1000},{"username":"aa","score":112},{"username":"matt","score":10},{"username":"uwu","score":10},{"username":"az","score":10}]}'
        finished.value = True
    except:
        response["error"] = True
        response["error_message"] = "maybe check your internet connection"
        finished.value = True
    

def _post(url, _payload,  response, finished):
    try:
        payload = dict(_payload)
        r = requests.post(url, data=payload)
        response['state'] = r.content
        finished.value = True
    except:
        response["error"] = True
        response["error_message"] = "maybe check your internet connection"
        finished.value = True



class Rect:
    def __init__(self, width, height) -> None:
        self.x = 0
        self.y = 0
        self.width = width
        self.height = height
        self.x_offset = 0
        self.y_offset = 0
        self.color = 10

class Label(Rect):
    def __init__(self, text) -> None:
        super().__init__(80, 10)
        self.text = text

class Input(Rect):
    def __init__(self, name, text="", label=False) -> None:
        super().__init__(80, 10)
        self.TYPE = "INPUT"
        self.spacing = 2
        self.label = False
        self.text = Label(text)
        self.max_chars = 19
        self.name = name
        if label:
            self.label = Label(label)
    
    def set_label(self, label):
        self.label = label

    def remove_last(self, number=1):
        self.text.text = self.text.text[:-number]
    
    def add(self, text):
        if len(self.text.text) >= self.max_chars:
            return
        if text == " ":
            return
        else:
            self.text.text += text

    def clear(self):
        self.text.text = ""
    
class Button(Rect):
    def __init__(self, text, onClick=lambda data: data, color=9) -> None:
        super().__init__(80, 10)
        self.x = 0
        self.y = 0
        self.label = Label(text)
        self.width = 30
        self.height = 10
        self.TYPE = "BUTTON"
        self.word_width = 3
        self.onClick = onClick
        self.styles = {}
        self.color = color

        

        if len(text) > 0:
            self.width = self.get_length(text) + 10
    
    def get_length(self, string):
        return len(string) *(self.word_width+1)

    def add_styles(self, styles):
        self.styles = styles


class Form:
    def __init__(self, onEnter=lambda data: data) -> None:
        self.x = 0
        self.y = 0
        self.position = (0, 0)
        self.items = []
        self.spacing = 2
        self.word_height = 6
        self.word_width = 3
        self.active = False
        self.TYPE = 'FORM'
        self.format_x = 0
        self.format_y = 0
        self.disabled = False
        self.hidden = False
        self.back_space_pressed_time = time.time()
        self.max_back_space_pressed_time = 0.5
        self.active_index = 0
        self.elements = []
        self.onEnter = onEnter
        self.styles = {}
        self.y_offset = 0
        
    def add(self, ITEM):
        self.items.append(ITEM)

    def format(self, form):
        for item in form.items:
            if item.TYPE == "INPUT":
                item.x = self.format_x
                item.y = self.format_y
                if item.label:
                    item.label.x = 0
                    item.label.y = self.get_middle_y(item.height)
                    item.x_offset = self.get_length(item.label.text)
                if item.text:
                    item.text.x = item.x_offset + self.spacing
                    item.text.y = self.get_middle_y(item.height)
                    item.text.color = 11
                item.y = self.format_y
                self.format_y += item.height + self.spacing
                self.elements.append({"form": form, "item": item})
            elif item.TYPE == "BUTTON":
                x = self.format_x
                y = self.format_y
                item.label.x = self.get_middle_x(item.label.text, item.width)
                item.label.y = self.get_middle_y(item.height)
                item.x_offset = 0
                item.y = self.format_y
                self.format_y += item.height + self.spacing
                self.elements.append({"form": form, "item": item})
                for style in item.styles.items():
                    if style[0] == "display" and style[1] == "inline-spaced":
                        self.format_y = y
                        self.format_x = x
                        break
            elif item.TYPE == "FORM":
                x = self.format_x
                y = self.format_y
                self.format_y += item.y_offset
                self.format(item)
                for style in item.styles.items():
                    if style[0] == "position" and style[1] == "absolute":
                        self.format_y = y
                        self.format_x = x
                        break
                self.format_x = 0
    
    def start_format(self):
        self.format_x = 0
        self.format_y = self.spacing
        self.format(self)
    def update_native_data(self):
        self.update(pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT), pyxel.mouse_x, pyxel.mouse_y, pyxel.btnp(pyxel.KEY_RETURN), pyxel.btn(pyxel.KEY_BACKSPACE), pyxel.btnp(pyxel.KEY_BACKSPACE), pyxel.btnp(pyxel.KEY_TAB), pyxel.btn(pyxel.KEY_SHIFT), pyxel.input_text)
    def update(self, mouse_left, mouse_x, mouse_y, enter, backspace_pressing, backspace, tab, shift_pressing, text):
        if self.active and self.elements[self.active_index]["item"].TYPE == "INPUT":
            if len(text) > 0:
                self.elements[self.active_index]["item"].add(text)
            if backspace:
                self.back_space_pressed = False
                if not self.back_space_pressed:
                    self.elements[self.active_index]["item"].remove_last()
            if backspace_pressing:
                if not self.back_space_pressed:
                    self.back_space_pressed_time = time.time()
                    self.back_space_pressed = True
                elif (time.time() - self.back_space_pressed_time) > self.max_back_space_pressed_time:
                    self.back_space_pressed_time = time.time() - 0.50
                    self.elements[self.active_index]["item"].remove_last()

        # if self.active and self.active_element.TYPE == "BUTTON":
        if shift_pressing and tab:
            self.active = True
            if self.elements[self.active_index]["item"].TYPE == "BUTTON":
                self.elements[self.active_index]["item"].color = 9
            else:
                self.elements[self.active_index]["item"].color = 10
            found = False
            index = self.active_index
            while not found:
                if index == 0:
                    index = len(self.elements)-1
                else:
                    index -= 1
                if not self.elements[index]["form"].disabled:
                    found = True
            self.active_index = index
            self.elements[self.active_index]["item"].color = 11
        elif tab:
            self.active = True
            if self.elements[self.active_index]["item"].TYPE == "BUTTON":
                self.elements[self.active_index]["item"].color = 9
            else:
                self.elements[self.active_index]["item"].color = 10
            found = False
            index = self.active_index
            while not found:
                if index == len(self.elements)-1:
                    index = 0
                else:
                    index += 1
                if not self.elements[index]["form"].disabled:
                    found = True
            self.active_index = index
            self.elements[self.active_index]["item"].color = 11

        if enter and self.active:
            # get data
            data = {}
            form = self.elements[self.active_index]["form"]
            for item in form.items:
                if item.TYPE == "INPUT":
                    # collect data
                    data[item.name] = item.text.text
            # call action
            if self.elements[self.active_index]["item"].TYPE == "BUTTON":
                self.elements[self.active_index]["item"].onClick(data)
            else:
                form.onEnter(data)

        if not mouse_left: 
            return
        if self.elements[self.active_index]["item"].TYPE == "BUTTON":
            self.elements[self.active_index]["item"].color = 9
        else:
            self.elements[self.active_index]["item"].color = 10
        self.active_index = 0
        self.active = False
        self.update_loop(self, mouse_x, mouse_y)

    def clear(self):
        for item in self.items:
            if item.TYPE == "INPUT":
                item.clear()

    def update_loop(self, form, mouse_x, mouse_y):
        at = 0
        for item in self.elements:
            i = item["item"]
            if item["form"].disabled:
                pass
            elif i.TYPE == "INPUT":
                if self.check_collision(mouse_x,mouse_y, 1, 1, self.x+i.x+self.get_length(i.label.text), self.y+i.y, i.width, i.height):
                    self.active_index = at
                    self.active = True
                    i.color = 11
            elif i.TYPE == "BUTTON":
                if self.check_collision(mouse_x, mouse_y, 1, 1, self.x+i.x, self.y+i.y, i.width, i.height):
                    self.active_index = at
                    self.active = True
                    i.color = 11
                    # get data
                    data = {}
                    form = self.elements[self.active_index]["form"]
                    for item in form.items:
                        if item.TYPE == "INPUT":
                            # collect data
                            data[item.name] = item.text.text
                    # call action
                    i.onClick(data)
            at += 1


    def check_collision(self, x1, y1, w1, h1, x2, y2, w2, h2):
        # can move -> true
        if x1 < x2 + w2 and x1 + w1 > x2 and y1 + h1 > y2 and y1 < y2 + h2:
            return True
        return False
    
    def render(self):
        pyxel.mouse(True)
        self.render_loop(self)
    
    def render_loop(self, form):
        for item in form.items:
            if item.TYPE == "FORM":
                if not item.hidden:
                    self.render_loop(item)
            elif item.TYPE == "INPUT":
                if item.label:
                    pyxel.text(self.x+item.x+item.label.x, self.y+item.y+item.label.y, item.label.text, item.label.color)
                if item.text:
                    pyxel.text(self.x+item.x+item.text.x, self.y+item.y+item.text.y, item.text.text, item.text.color)
                pyxel.rectb(self.x + item.x + item.x_offset, self.y + item.y, item.width, item.height, item.color)
            elif item.TYPE == "BUTTON":
                pyxel.text(self.x+item.x+item.label.x, self.y+item.y+item.label.y, item.label.text, item.label.color)
                pyxel.rectb(self.x + item.x + item.x_offset, self.y + item.y, item.width, item.height, item.color)
                


    def get_length(self, string):
        return len(string) *(self.word_width+1)
    def get_middle_y(self, height):
        return round((height - self.word_height)/2)
    def get_middle_x(self, string, width):
        return round((width - self.get_length(string))/2)


    def hide(self):
        self.disabled = True
        self.hidden = True
    def show(self):
        self.disabled = False
        self.hidden = False

    def add_styles(self, styles):
        self.styles = styles



if __name__ == "__main__":
    if WEB:
        print("starting web version...")
    else:
        print("starting version...")
    Game()
    print("ending...")




# FUCK THE SYSTEM NEED TO COPY 5.pyxres to res.pyxres (different versions), copy with code