import random
import os
script_dir = os.path.dirname(__file__) #<-- absolute dir the script is in

import pygame
import myparameters
import pygame_menu
from pygame.locals import (
    RLEACCEL,
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    K_SPACE,
    KEYDOWN,
    QUIT,
)

SCREEN_HEIGHT = myparameters.SCREEN_HEIGHT
SCREEN_WIDTH = myparameters.SCREEN_WIDTH
PLAY_HEIGHT = myparameters.PLAY_HEIGHT
PLAY_WIDTH = myparameters.PLAY_WIDTH

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.tag = 'player'
        self.surf = pygame.Surface((64, 64))
        self.surf.fill((255, 255, 255))
        self.surf = pygame.image.load("asset/player_up.png").convert()
        self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        self.rect = self.surf.get_rect(center=(PLAY_WIDTH/2 , PLAY_HEIGHT/2))
        self.level_up_sound = pygame.mixer.Sound("asset/chime_up.wav")
        self.direction = 1
        self.speed = 5
        self.Strength=5
        self.Agility=5
        self.Vigor=5
        self.Intellect=5
        self.Wisdom=5
        self.HP = 50
        self.max_HP = 10*self.Vigor
        self.Mana = 50
        self.max_Mana = 10 * self.Wisdom
        self.STR_exp = 0
        self.STR_exp_required = 100*self.Strength
        self.AGI_exp = 0
        self.AGI_exp_required = 100 * self.Agility
        self.VIG_exp = 0
        self.VIG_exp_required = 100 * self.Vigor
        self.INT_exp = 0
        self.INT_exp_required = 100 * self.Intellect
        self.WIS_exp = 0
        self.WIS_exp_required = 100 * self.Wisdom
        self.move_x = 0
        self.move_y = 0
        self.equiped_weapon = 'Iron Sword'
        self.equiped_armor = 'Leather Armor'
        self.equiped_fire = 'firebolt'
        self.equiped_lighting = 'lightingbolt'
        self.equiped_ice = 'iceball'
        self.defense = 0
        self.equiped_armor_defense = 5
        self.current_weapon_cooldown = 333
        self.regen_counter =0
    def regen(self):
        if self.regen_counter>=120:
            if self.Mana< self.max_Mana:
                self.Mana += round(self.max_Mana*0.02)
            if self.HP < self.max_HP:
                self.HP += round(self.max_HP * 0.01+0.01)
            self.regen_counter=0
        self.regen_counter += 1
    def update(self, pressed_keys,slow_factor):
        if pressed_keys[K_UP] and pressed_keys[K_LEFT] == False and pressed_keys[K_RIGHT] == False:
            self.direction = 1  # up
            if self.direction == 1:
                self.move_y = -self.speed+slow_factor
                #self.rect.move_ip(0, -self.speed+slow_factor)
            self.surf = pygame.image.load("asset/player_up.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if pressed_keys[K_DOWN] and pressed_keys[K_LEFT] == False and pressed_keys[K_RIGHT] == False:
            self.direction = 2  # down
            if self.direction == 2:
                self.move_y =  +self.speed-slow_factor
                #self.rect.move_ip(0, self.speed-slow_factor)
            self.surf = pygame.image.load("asset/player_down.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if pressed_keys[K_LEFT] and pressed_keys[K_UP] == False and pressed_keys[K_DOWN] == False:
            self.direction = 3  # left
            if self.direction == 3:
                self.move_x =  -self.speed+slow_factor
                #self.rect.move_ip(-self.speed+slow_factor, 0)
            self.surf = pygame.image.load("asset/player_left.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if pressed_keys[K_RIGHT] and pressed_keys[K_UP] == False and pressed_keys[K_DOWN] == False:
            self.direction = 4  # right
            if self.direction == 4:
                self.move_x = + self.speed - slow_factor
                #self.rect.move_ip(self.speed-slow_factor, 0)
            self.surf = pygame.image.load("asset/player_right.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if self.rect.left < 0:
            self.rect.left = 0
        if self.rect.right > PLAY_WIDTH:
            self.rect.right = PLAY_WIDTH
        if self.rect.top <= 0:
            self.rect.top = 0
        if self.rect.bottom >= PLAY_HEIGHT:
            self.rect.bottom = PLAY_HEIGHT

class Tile(pygame.sprite.Sprite):
    def __init__(self, chosen_tile, x, y):
        super(Tile, self).__init__()
        self.surf = pygame.Surface((32, 32))
        self.rect = self.surf.get_rect(topleft=(x, y))
        self.surf.blit(chosen_tile, (0,0))

class Tileset:
    def __init__(self, file_path):
        super(Tileset, self).__init__()
        self.surf = pygame.Surface((32, 32))
        self.tiles = []
        self.image = pygame.image.load(file_path)
        self.rect = self.image.get_rect()
        self.load()

    def load(self):
        for x in range(0, self.rect.right, 32):
            for y in range(0, self.rect.bottom, 32):
                tile = pygame.Surface((32,32))
                tile.blit(self.image, (0,0), (y, x, 32,32))
                self.tiles.append(tile)

class Tilemap:
    def __init__(self, rel_path ,tileset_path, map_id, all_sprites, ob_tiles, slow_tiles , new_map_tiles,prev_map_tiles):
        super(Tilemap, self).__init__()
        abs_file_path = os.path.join(script_dir, rel_path)
        with open(abs_file_path) as f:
            map_input = [[int(num) for num in line.split(',')] for line in f]
        self.size = (len(map_input[0])*32, len(map_input)*32)
        self.surf = pygame.Surface(self.size)
        self.surf.fill((50,60,70))
        self.rect = self.surf.get_rect()
        self.map = map_input
        self.map_id=map_id
        self.tileset=Tileset(tileset_path)
        self.tiles=self.tileset.tiles
        for i in range(len(self.map)):
            for j in range(len(self.map[0])):
                new_tile = Tile(self.tiles[self.map[i][j]], j * 32, i * 32)
                #screen.blit(new_tile.surf, (j * 32 , i * 32))
                self.surf.blit(new_tile.surf, (j * 32, i * 32))
                if map_id == 'Tomb_entrance':
                    if self.map[i][j] <=1 or self.map[i][j] >=3 and self.map[i][j]<=13 :  # brick tile
                        ob_tiles.add(new_tile)
                        all_sprites.add(new_tile)
                    if self.map[i][j] == 14 or self.map[i][j] == 15:
                        new_map_tiles.add(new_tile)
                        all_sprites.add(new_tile)
                if map_id == 'Tomb_L1' or map_id == 'Tomb_L2' or map_id == 'Tomb_L3' or map_id == 'Tomb_L4':
                    if self.map[i][j] >=3:  # brick tile
                        ob_tiles.add(new_tile)
                        all_sprites.add(new_tile)
                    if self.map[i][j] == 1:
                        prev_map_tiles.add(new_tile)
                        all_sprites.add(new_tile)
                    if self.map[i][j] == 2:
                        new_map_tiles.add(new_tile)
                        all_sprites.add(new_tile)

                elif self.map[i][j] == 1:  # grass tile
                    slow_tiles.add(new_tile)
                    all_sprites.add(new_tile)

"""
Enemy
"""

class Skeleton(pygame.sprite.Sprite):
    def __init__(self,enemy_type, map_size):
        super(Skeleton, self).__init__()
        self.enemy_kill_sound= pygame.mixer.Sound("asset/floop2_x.wav")
        if enemy_type=='skeleton_normal':
            self.tag = 'skeleton_normal'
            self.speed = 3
            self.Strength = 5
            self.Agility = 2
            self.Vigor = 2
            self.Intellect = 0
            self.Wisdom = 0
            self.HP = 50
            self.max_HP = 50
            self.defense = 5
            self.direction = 2  # 1up 2down 3left 4right
            self.exp_points = 100
            self.img_x=40
            self.img_y=40
            self.img_surf = pygame.image.load(f"asset/{self.tag}_down.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if enemy_type=='skeleton_archer':
            self.tag = 'skeleton_archer'
            self.speed = 3
            self.Strength = 5
            self.Agility = 2
            self.Vigor = 2
            self.Intellect = 0
            self.Wisdom = 0
            self.HP = 75
            self.max_HP = 75
            self.defense = 5
            self.direction = 2  # 1up 2down 3left 4right
            self.exp_points = 200
            self.img_x=40
            self.img_y=40
            self.img_surf = pygame.image.load(f"asset/{self.tag}_down.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if enemy_type=='skeleton_warrior':
            self.tag = 'skeleton_warrior'
            self.speed = 5
            self.Strength = 5
            self.Agility = 2
            self.Vigor = 5
            self.Intellect = 0
            self.Wisdom = 0
            self.HP = 300
            self.max_HP = 300
            self.defense = 15
            self.direction = 2  # 1up 2down 3left 4right
            self.exp_points = 500
            self.img_x=60
            self.img_y=60
            self.img_surf = pygame.image.load(f"asset/{self.tag}_down.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if enemy_type=='skeleton_mage':
            self.tag = 'skeleton_mage'
            self.speed = 4
            self.Strength = 5
            self.Agility = 2
            self.Vigor = 3
            self.Intellect = 5
            self.Wisdom = 0
            self.HP = 200
            self.max_HP = 200
            self.defense = 10
            self.direction = 2  # 1up 2down 3left 4right
            self.exp_points = 400
            self.img_x=40
            self.img_y=40
            self.img_surf = pygame.image.load(f"asset/{self.tag}_down.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if enemy_type=='skeleton_boss':
            self.tag = 'skeleton_boss'
            self.speed = 3
            self.Strength = 50
            self.Agility = 20
            self.Vigor = 20
            self.Intellect = 0
            self.Wisdom = 0
            self.HP = 3000
            self.max_HP = 3000
            self.defense = 15
            self.direction = 2  # 1up 2down 3left 4right
            self.exp_points = 1500
            self.img_x=120
            self.img_y=120
            self.img_surf = pygame.image.load("asset/skeleton_boss_down.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        self.steps = 0
        self.max_step = random.randint(200,400)
        self.img_surf = pygame.Surface((self.img_x, self.img_y))
        #self.img_surf.fill((255, 255, 255))
        self.surf=pygame.Surface((self.img_x, self.img_y+10))
        self.HP_bar=Floating_bar(self.img_x)
        self.surf.blit(self.img_surf, (0,0))
        self.surf.blit(self.HP_bar.surf, (0,self.img_y+5))
        self.rect = self.img_surf.get_rect(
            center=(
                random.randint(40, map_size[0] - 40),
                random.randint(40, map_size[1] - 40),
            )
        )

    def enemy_fire(entity, enemy_bullets, all_sprites):
        if entity.tag == 'skeleton_archer':
            x = (entity.rect.left + entity.rect.right) / 2
            y = (entity.rect.top + entity.rect.bottom) / 2
            new_enemy_bullet = Bullet(x, y, entity.direction, entity.tag)
            enemy_bullets.add(new_enemy_bullet)
            all_sprites.add(new_enemy_bullet)
        if entity.tag == 'skeleton_mage':
            x = (entity.rect.left + entity.rect.right) / 2
            y = (entity.rect.top + entity.rect.bottom) / 2
            new_enemy_bullet = Bullet(x, y, entity.direction, entity.tag)
            enemy_bullets.add(new_enemy_bullet)
            all_sprites.add(new_enemy_bullet)

    def update(self, dir_int, player, collide = False):
        x_dist = abs(self.rect.centerx - player.rect.centerx)
        y_dist = abs(self.rect.centery - player.rect.centery)
        if collide == True and x_dist>y_dist:
            x_dist=0  # change direction when hit wall
        if collide == True and y_dist>x_dist:
            y_dist=0

        if self.rect.centerx>player.rect.centerx and x_dist>y_dist:
            dir_int=3
        elif self.rect.centerx<player.rect.centerx and x_dist>y_dist:
            dir_int=4
        elif self.rect.centery>player.rect.centery:
            dir_int=1
        elif self.rect.centery<player.rect.centery:
            dir_int=2
        if self.steps < self.max_step:
            if self.direction == 1:
                self.rect.move_ip(0, -self.speed)
            if self.direction == 2:
                self.rect.move_ip(0, self.speed)
            if self.direction == 3:
                self.rect.move_ip(-self.speed, 0)
            if self.direction == 4:
                self.rect.move_ip(self.speed, 0)
            self.steps = self.steps + self.speed

        elif self.steps >= self.max_step:
            self.direction = dir_int
            if self.direction == 0:
                self.rect.move_ip(0, 0)
            if self.direction == 1:
                self.rect.move_ip(0, -self.speed)
            if self.direction == 2:
                self.rect.move_ip(0, self.speed)
            if self.direction == 3:
                self.rect.move_ip(-self.speed, 0)
            if self.direction == 4:
                self.rect.move_ip(self.speed, 0)
            self.steps = 0

        if self.direction == 2:
            self.img_surf = pygame.image.load(f'asset/{self.tag}_down.png').convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if self.direction == 1:
            self.img_surf = pygame.image.load(f"asset/{self.tag}_up.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if self.direction == 4:
            self.img_surf = pygame.image.load(f"asset/{self.tag}_right.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        if self.direction == 3:
            self.img_surf = pygame.image.load(f"asset/{self.tag}_left.png").convert()
            self.img_surf.set_colorkey((0, 0, 0), RLEACCEL)
        self.surf.blit(self.img_surf, (0, 0))
        self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        self.surf.blit(self.HP_bar.surf, (0,self.img_y+5))
        self.HP_bar.update(self)
"""
weapon
"""

class Sword(pygame.sprite.Sprite):
    def __init__(self, x, y, direction,user_size, sword_name):
        super(Sword, self).__init__()
        if direction == 1:
            self.surf = pygame.Surface((64, 10))
            self.surf = pygame.image.load("asset/sword_light_up.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 2:
            self.surf = pygame.Surface((64, 10))
            self.surf = pygame.image.load("asset/sword_light_down.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 3 :
            self.surf = pygame.Surface((10, 64))
            self.surf = pygame.image.load("asset/sword_light_left.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 4 :
            self.surf = pygame.Surface((10, 64))
            self.surf = pygame.image.load("asset/sowrd_light_right.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if direction == 1:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0]/2, y - user_size[1]/2 -25))
        if direction == 2:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0]/2, y + user_size[1]/2 + 15))
        if direction == 3:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0]/2 - 25, y - user_size[1]/2))
        if direction == 4:
            self.rect = self.surf.get_rect(topleft=(x + user_size[0]/2 + 15, y - user_size[1]/2))
        self.lifecycle = 5
        self.life_counter=0
        self.direction = direction
        self.shoot_sound= pygame.mixer.Sound("asset/swing.flac")
        self.collide_sound = pygame.mixer.Sound("asset/swing.flac")
        if sword_name == 'Iron Sword':
            self.min_attack = 3
            self.max_attack = 6
            self.tag = 'Iron Sword'
        if sword_name == 'Steel Sword':
            self.min_attack = 8
            self.max_attack = 12
            self.tag = 'Steel Sword'

    def update(self):
        self.life_counter += 1
        if self.life_counter>=self.lifecycle:
            self.kill()

class Greatsword(pygame.sprite.Sprite):
    def __init__(self, x, y, direction,user_size, sword_name):
        super(Greatsword, self).__init__()
        if direction == 1:
            self.surf = pygame.Surface((96, 32))
            self.surf = pygame.image.load("asset/greatsword_light_up.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 2:
            self.surf = pygame.Surface((96, 32))
            self.surf = pygame.image.load("asset/greatsword_light_down.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 3 :
            self.surf = pygame.Surface((32, 96))
            self.surf = pygame.image.load("asset/greatsword_light_left.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 4 :
            self.surf = pygame.Surface((32, 96))
            self.surf = pygame.image.load("asset/greatsword_light_right.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if direction == 1:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0]/2, y - user_size[1]/2 -35))
        if direction == 2:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0]/2, y + user_size[1]/2 + 25))
        if direction == 3:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0]/2 - 35, y - user_size[1]/2))
        if direction == 4:
            self.rect = self.surf.get_rect(topleft=(x + user_size[0]/2 + 25, y - user_size[1]/2))
        self.lifecycle = 5
        self.life_counter=0
        self.direction = direction
        self.shoot_sound= pygame.mixer.Sound("asset/swing.flac")
        self.collide_sound = pygame.mixer.Sound("asset/swing.flac")
        if sword_name == 'Claymore':
            self.min_attack = 12
            self.max_attack = 20
            self.defense =0
            self.tag = 'Claymore'

class Spear(pygame.sprite.Sprite):
    def __init__(self, x, y, direction, user_size, sword_name):
        super(Spear, self).__init__()
        if direction == 1:
            self.surf = pygame.Surface((16, 96))
            self.surf = pygame.image.load("asset/spear_light_up.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 2:
            self.surf = pygame.Surface((16, 96))
            self.surf = pygame.image.load("asset/spear_light_down.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 3:
            self.surf = pygame.Surface((96, 16))
            self.surf = pygame.image.load("asset/spear_light_left.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if direction == 4:
            self.surf = pygame.Surface((96, 16))
            self.surf = pygame.image.load("asset/spear_light_right.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)

        if direction == 1:
            self.rect = self.surf.get_rect(topleft=(x - 8, y - user_size[1] / 2 - 106))
        if direction == 2:
            self.rect = self.surf.get_rect(topleft=(x - 8, y + user_size[1] / 2 + 10))
        if direction == 3:
            self.rect = self.surf.get_rect(topleft=(x - user_size[0] / 2 - 106, y - 8))
        if direction == 4:
            self.rect = self.surf.get_rect(topleft=(x + user_size[0] / 2 + 10, y - 8))
        self.lifecycle = 5
        self.life_counter = 0
        self.direction = direction
        self.shoot_sound = pygame.mixer.Sound("asset/swing.flac")
        self.collide_sound = pygame.mixer.Sound("asset/swing.flac")
        if sword_name == 'Iron Spear':
            self.min_attack = 16
            self.max_attack = 24
            self.defense = 0
            self.tag = 'Iron Spear'

    def update(self):
        self.life_counter += 1
        if self.life_counter>=self.lifecycle:
            self.kill()

class Bar(pygame.sprite.Sprite):
    def __init__(self, bar_type, screen):
        super(Bar, self).__init__()
        self.surf = pygame.Surface((150, 10))
        if bar_type == 'HP':
            self.bar_len=150
            self.surf = pygame.Surface((self.bar_len, 10))
            self.surf.fill((255,0,0))
        if bar_type == 'Mana':
            self.bar_len = 150
            self.surf = pygame.Surface((self.bar_len, 10))
            self.surf.fill((0,0,255))
        if bar_type == 'STR' or bar_type == 'AGI' or bar_type == 'AGI' or bar_type == 'VIG' or bar_type == 'INT' or bar_type == 'WIS':
            self.bar_len = 50
            self.surf = pygame.Surface((100, 10))
            self.surf.fill((0, 255, 255))

        self.tag=bar_type

        #fill border
        self.surf.fill((255, 255, 255), (0, 0, self.bar_len, 1))
        self.surf.fill((255, 255, 255), (0, 9, self.bar_len, 1))
        self.surf.fill((255, 255, 255), (0, 0, 1, 10))
        self.surf.fill((255, 255, 255), (self.bar_len-1, 0, 1, 10))

        #blit to screen
        if bar_type == 'HP':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-180, 40))
        if bar_type == 'Mana':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-180, 80))
        if bar_type == 'STR':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 105))
        if bar_type == 'AGI':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 135))
        if bar_type == 'VIG':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 165))
        if bar_type == 'INT':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 195))
        if bar_type == 'WIS':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 225))

    def update(self, player, screen):
        self.surf.fill((0, 0, 0))
        if self.tag == 'HP':
            x=round(player.HP/player.max_HP*self.bar_len)
            self.surf.fill((255, 0, 0), (0,0,x,10))
        if self.tag == 'Mana':
            x=round(player.Mana/player.max_Mana*self.bar_len)
            self.surf.fill((0, 0, 255), (0,0,x,10))
        if self.tag == 'STR':
            x=round(player.STR_exp/player.STR_exp_required*self.bar_len)
            self.surf.fill((0, 255, 255), (0,0,x,10))
        if self.tag == 'AGI':
            x=round(player.AGI_exp/player.AGI_exp_required*self.bar_len)
            self.surf.fill((0, 255, 255), (0,0,x,10))
        if self.tag == 'VIG':
            x=round(player.VIG_exp/player.VIG_exp_required*self.bar_len)
            self.surf.fill((0, 255, 255), (0,0,x,10))
        if self.tag == 'INT':
            x=round(player.INT_exp/player.INT_exp_required*self.bar_len)
            self.surf.fill((0, 255, 255), (0,0,x,10))
        if self.tag == 'WIS':
            x=round(player.WIS_exp/player.WIS_exp_required*self.bar_len)
            self.surf.fill((0, 255, 255), (0,0,x,10))
        # fill border
        self.surf.fill((255, 255, 255), (0, 0, self.bar_len, 1))
        self.surf.fill((255, 255, 255), (0, 9, self.bar_len, 1))
        self.surf.fill((255, 255, 255), (0, 0, 1, 10))
        self.surf.fill((255, 255, 255), (self.bar_len-1, 0, 1, 10))
        if self.tag == 'HP':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-180, 40))
        if self.tag == 'Mana':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-180, 80))
        if self.tag == 'STR':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 105))
        if self.tag == 'AGI':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 135))
        if self.tag == 'VIG':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 165))
        if self.tag == 'INT':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 195))
        if self.tag == 'WIS':
            screen.blit(self.surf, (myparameters.SCREEN_WIDTH-100, 225))

class Floating_bar(pygame.sprite.Sprite):
    def __init__(self, width):
        super(Floating_bar, self).__init__()
        self.surf = pygame.Surface((width, 6))
        self.surf.fill((255,0,0))
        self.width=width
        #fill border
        self.surf.fill((255, 255, 255), (0, 0, self.width, 1))
        self.surf.fill((255, 255, 255), (0, 4 , self.width, 1))
        self.surf.fill((255, 255, 255), (0, 0, 1, 6))
        self.surf.fill((255, 255, 255), (self.width-1, 0, 1, 6))

        #blit to screen
        #screen.blit(self.surf, entity.rect.leftbottom)

    def update(self, entity):
        self.surf.fill((0, 0, 0))
        x=round(entity.HP/entity.max_HP*self.width)
        self.surf.fill((255, 0, 0), (0,0,x,6))
        # fill border
        self.surf.fill((255, 255, 255), (0, 0, self.width, 1))
        self.surf.fill((255, 255, 255), (0, 4, self.width, 1))
        self.surf.fill((255, 255, 255), (0, 0, 1, 6))
        self.surf.fill((255, 255, 255), (self.width - 1, 0, 1, 6))
        #screen.blit(self.surf, screen.blit(self.surf, entity.rect.leftbottom))

class Dmg_txt(pygame.sprite.Sprite):
    def __init__(self, content, x, y, screen):
        super(Dmg_txt, self).__init__()
        font = pygame.font.Font('asset/Roboto-Black.ttf', 18)
        self.text = font.render(content, True, (255, 0, 0), (0, 0, 0))
        self.text.set_colorkey((0, 0, 0), RLEACCEL)
        self.rect=self.text.get_rect(center=(x, y))
        screen.blit(self.text, self.rect)
        self.timer = 0

    def update(self,screen):
        self.rect.move_ip(0,-1)
        if self.timer <= 30:
            screen.blit(self.text, self.rect)
        else:
            self.kill()
        self.timer +=1

"""
bullet
"""

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y, direction, bullet_type):
        super(Bullet, self).__init__()
        self.surf = pygame.Surface((1, 1))
        if bullet_type=='firebolt':
            self.surf = pygame.Surface((20, 20))
            self.surf = pygame.image.load("asset/firebolt.png").convert()
            self.tag = bullet_type
            self.speed = 20
            self.Mana_cost = 5
            self.mgk_dmg_fctr=5
            self.mgk_base_dmg = 25
        if bullet_type=='fireball':
            self.surf = pygame.Surface((40, 40))
            self.surf = pygame.image.load("asset/weapon_icon.png").convert()
            self.tag = bullet_type
            self.speed = 20
            self.Mana_cost = 20
            self.mgk_dmg_fctr=20
            self.mgk_base_dmg = 20
        if bullet_type=='lightingbolt':
            if direction == 1 or direction ==2:
                self.surf = pygame.Surface((30, 60))
                self.surf = pygame.image.load("asset/lightingbolt_down.png").convert()
            if direction == 3 or direction ==4:
                self.surf = pygame.Surface((60, 30))
                self.surf = pygame.image.load("asset/lightingbolt_left.png").convert()
            self.tag = bullet_type
            self.speed = 30
            self.Mana_cost = 10
            self.mgk_dmg_fctr = 1
            self.mgk_base_dmg = 10
            self.travel_distance = 0
            self.max_distance = 300
        if bullet_type=='iceball':
            self.surf = pygame.Surface((60, 60))
            self.surf = pygame.image.load("asset/iceball.png").convert()
            self.tag = bullet_type
            self.speed = 10
            self.Mana_cost = 8
            self.mgk_dmg_fctr=2
            self.mgk_base_dmg = 5
            self.lifecycle=10
        if bullet_type=='skeleton_archer':
            if direction == 1 :
                self.surf = pygame.Surface((10, 20))
                self.surf = pygame.image.load("asset/arrow_up.png").convert()
            if direction == 2 :
                self.surf = pygame.Surface((10, 20))
                self.surf = pygame.image.load("asset/arrow_down.png").convert()
            if direction == 3:
                self.surf = pygame.Surface((20, 10))
                self.surf = pygame.image.load("asset/arrow_left.png").convert()
            if direction == 4:
                self.surf = pygame.Surface((20, 10))
                self.surf = pygame.image.load("asset/arrow_right.png").convert()
            self.tag = bullet_type
            self.speed = 10
            self.attack = 15
        if bullet_type=='skeleton_mage':
            self.surf = pygame.Surface((20, 20))
            self.surf = pygame.image.load("asset/firebolt.png").convert()
            self.tag = bullet_type
            self.speed = 15
            self.attack = 30
        #self.surf.fill((0, 0, 0))
        self.surf.set_colorkey((0,0,0),RLEACCEL)
        self.rect = self.surf.get_rect( center=(x,y,))
        self.bullet_direction = direction
        self.collide_sound = pygame.mixer.Sound("asset/explosion_x.wav")

    def update(self):
        if self.tag == 'iceball':
            self.rect.move_ip(random.randint(-self.speed, self.speed), random.randint(-self.speed, self.speed))
        elif self.bullet_direction == 1:
            self.rect.move_ip(0, -self.speed)
        elif self.bullet_direction == 2:
            self.rect.move_ip(0, self.speed)
        elif self.bullet_direction == 3:
            self.rect.move_ip(-self.speed, 0)
        elif self.bullet_direction == 4:
            self.rect.move_ip(self.speed, 0)
        elif self.rect.right > PLAY_WIDTH or self.rect.right < 0 or self.rect.top < 0 or self.rect.top > PLAY_HEIGHT:
            self.kill()

        if self.tag == 'lightingbolt':
            self.travel_distance += self.speed
            if self.travel_distance >= self.max_distance:
                self.kill()

"""
drop
"""
class Treasurebox(pygame.sprite.Sprite):
    def __init__(self, bonusbox_id, x, y):
        super(Treasurebox, self).__init__()
        self.tag=bonusbox_id
        if bonusbox_id == 0:
            self.surf = pygame.image.load("asset/treasurebox.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if bonusbox_id == 1:
            self.surf = pygame.image.load("asset/treasurebox.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        if bonusbox_id == 2:
            self.surf = pygame.image.load("asset/treasurebox.png").convert()
            self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        self.rect = self.surf.get_rect(center=(x, y))
        self.bonus_sound = pygame.mixer.Sound("asset/chime.wav")

def enemy_drop(entity, treasureboxes, all_sprites):
    x = (entity.rect.left + entity.rect.right) / 2
    y = (entity.rect.top + entity.rect.bottom) / 2
    if entity.tag == 'skeleton_normal' or entity.tag == 'skeleton_archer':
        new_treasurebox=Treasurebox(0,x,y)
        treasureboxes.add(new_treasurebox)
        all_sprites.add(new_treasurebox)
    if entity.tag == 'skeleton_archer' :
        new_treasurebox=Treasurebox(1,x,y)
        treasureboxes.add(new_treasurebox)
        all_sprites.add(new_treasurebox)
    if entity.tag == 'skeleton_warrior' or entity.tag == 'skeleton_mage':
        new_treasurebox=Treasurebox(2,x,y)
        treasureboxes.add(new_treasurebox)
        all_sprites.add(new_treasurebox)

def loot_treasurebox(treasurebox):
    seed = random.randint(1, 100)
    if treasurebox.tag == 0:
        gold=random.randint(2, 10)
        myparameters.item_inventory['Gold'] += gold
        #notification(f'Gold +{gold}')
        if seed<=2:
            myparameters.weapon_inventory['Steel Sword'] = True
            notification(f'Got Steel Sword')
        if seed>90:
            myparameters.item_inventory['HP Potion'] += 1
            notification(f'Got HP Potion')
        if seed>80 and seed<=90:
            myparameters.item_inventory['Mana Potion'] += 1
            notification(f'Got Mana Potion')
    if treasurebox.tag == 1:
        gold=random.randint(5, 15)
        myparameters.item_inventory['Gold'] += gold
        #notification(f'Gold +{gold}')
        if seed<=5:
            myparameters.weapon_inventory['Steel Sword'] = True
            notification(f'Got Steel Sword')
        if seed>5 and seed<=10:
            myparameters.weapon_inventory['Iron Spear'] = True
            notification(f'Got Iron Spear')
        if seed>85:
            myparameters.item_inventory['HP Potion'] += 1
            notification(f'Got HP Potion')
        if seed>70 and seed<=85:
            myparameters.item_inventory['Mana Potion'] += 1
            notification(f'Got Mana Potion')
    if treasurebox.tag == 2:
        gold=random.randint(10, 30)
        myparameters.item_inventory['Gold'] += gold
        #notification(f'Gold +{gold}')
        if seed<=5:
            myparameters.weapon_inventory['Iron Armor'] = True
            notification(f'Got Iron Armor')
        if seed>5 and seed <=10:
            myparameters.weapon_inventory['Claymore'] = True
            notification(f'Got Claymore')
        if seed>85:
            myparameters.item_inventory['HP Potion'] += 1
            notification(f'Got HP Potion')
        if seed>70 and seed<=85:
            myparameters.item_inventory['Mana Potion'] += 1
            notification(f'Got Mana Potion')
    myparameters.item_inventory['HP Potion'] = min(myparameters.item_inventory['HP Potion'], 9)
    myparameters.item_inventory['Mana Potion'] = min (myparameters.item_inventory['Mana Potion'], 9)
"""
functions
"""

def calc_dmg(attacker, attacker_weapon, defenser, atk_type):
    if atk_type == 'Iron Sword' or atk_type == 'Steel Sword' or atk_type == 'Elven Sword':
        dmg=attacker.Strength/2+attacker.Agility/2 \
            +random.randint(attacker_weapon.min_attack,attacker_weapon.max_attack)\
            -defenser.defense
    elif atk_type == 'Claymore' or atk_type == 'Iron Spear':
        dmg=attacker.Strength+random.randint(attacker_weapon.min_attack,attacker_weapon.max_attack)\
            -defenser.defense
    elif atk_type == 'skeleton_archer' :
        dmg=attacker.attack-defenser.defense-defenser.equiped_armor_defense
    elif atk_type == 'skeleton_mage' :
        dmg=attacker.attack

    elif atk_type == 'firebolt' or atk_type == 'lightingbolt' or atk_type == 'iceball' or atk_type== 'skeleton_mage':
        dmg = attacker.Intellect * attacker_weapon.mgk_dmg_fctr + attacker_weapon.mgk_base_dmg

    return round(max(dmg, 1))

def calc_dmg_col(attacker,  defenser):
    dmg= (attacker.Vigor*5 - defenser.defense - defenser.equiped_armor_defense)
    print(defenser.equiped_armor_defense)
    return round(max(dmg, 1))

def calc_exp_gain(attacker, defenser, exp_type, kill_y_n,level_up_sound, dmg=0, mana_used=0):
    if exp_type == 'Iron Sword' or exp_type == 'Steel Sword':
        if kill_y_n == False:
            attacker.STR_exp = attacker.STR_exp + defenser.exp_points/20
            attacker.AGI_exp = attacker.AGI_exp + defenser.exp_points / 20
        if kill_y_n == True:
            attacker.STR_exp = attacker.STR_exp + defenser.exp_points/2
            attacker.AGI_exp = attacker.AGI_exp + defenser.exp_points / 2
        if attacker.STR_exp>=attacker.STR_exp_required:
            level_up_sound.play()
            attacker.Strength += 1
            attacker.STR_exp = 0
            attacker.STR_exp_required = attacker.Strength*100
        if attacker.AGI_exp>=attacker.AGI_exp_required:
            level_up_sound.play()
            attacker.Agility += 1
            attacker.AGI_exp = 0
            attacker.AGI_exp_required = attacker.Agility*100
    if exp_type == 'firebolt' or exp_type == 'lightingbolt' or exp_type == 'iceball':
        if kill_y_n == False:
            attacker.INT_exp = attacker.INT_exp + defenser.exp_points/10
        if kill_y_n == True:
            attacker.INT_exp = attacker.INT_exp + defenser.exp_points
        if attacker.INT_exp>=attacker.INT_exp_required:
            level_up_sound.play()
            attacker.Intellect += 1
            attacker.INT_exp = 0
            attacker.INT_exp_required = attacker.Intellect*100
    if exp_type == 'VIG':
        attacker.VIG_exp = attacker.VIG_exp + dmg*20
        if attacker.VIG_exp>=attacker.VIG_exp_required:
            level_up_sound.play()
            attacker.Vigor += 1
            attacker.VIG_exp = 0
            attacker.VIG_exp_required = attacker.Vigor*100
            attacker.max_HP=attacker.Vigor*10
            attacker.defense += 1
    if exp_type == 'WIS':
        attacker.WIS_exp = attacker.WIS_exp + mana_used*20
        if attacker.WIS_exp>=attacker.WIS_exp_required:
            level_up_sound.play()
            attacker.Wisdom += 1
            attacker.WIS_exp = 0
            attacker.WIS_exp_required = attacker.Wisdom*100
            attacker.max_Mana = attacker.Wisdom*10

def display_text(font_file, font_size, content,x, y, screen):
    font = pygame.font.Font(font_file, font_size)
    text = font.render(content, True, (255, 255, 255), (0, 0, 0))
    textRect = text.get_rect()
    textRect.topleft = (x, y)
    screen.blit(text, textRect)

class Icon(pygame.sprite.Sprite):
    def __init__(self, icon_file,x, y, screen):
        super(Icon, self).__init__()
        self.surf = pygame.Surface((32, 32))
        self.surf = pygame.image.load(icon_file).convert()
        self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        self.rect=self.surf.get_rect(topleft = (x, y))
        screen.blit(self.surf, self.rect)

def move_ob(sprite, direction, speed, ob_tiles):
    if direction == 1:
        sprite.rect.move_ip(0, speed)
    if direction == 2:
        sprite.rect.move_ip(0, -speed)
    if direction == 3:
        sprite.rect.move_ip(speed, 0)
    if direction == 4:
        sprite.rect.move_ip(-speed, 0)
    if pygame.sprite.spritecollideany(sprite, ob_tiles):
        if direction == 1:
            sprite.rect.move_ip(0, -speed)
        if direction == 2:
            sprite.rect.move_ip(0, speed)
        if direction == 3:
            sprite.rect.move_ip(-speed, 0)
        if direction == 4:
            sprite.rect.move_ip(speed, 0)

def hit_bounce(sprite, direction, speed, ob_tiles):
    if direction == 1:
        sprite.rect.move_ip(0, -speed)
    if direction == 2:
        sprite.rect.move_ip(0, speed)
    if direction == 3:
        sprite.rect.move_ip(-speed, 0)
    if direction == 4:
        sprite.rect.move_ip(speed, 0)
    if pygame.sprite.spritecollideany(sprite, ob_tiles):
        if direction == 1:
            sprite.rect.move_ip(0, speed)
        if direction == 2:
            sprite.rect.move_ip(0, -speed)
        if direction == 3:
            sprite.rect.move_ip(speed, 0)
        if direction == 4:
            sprite.rect.move_ip(-speed, 0)

def paused(pause, screen):
    screen.fill((0,0,0))
    display_text('asset/Roboto-Black.ttf', 18, "PAUSED", 400, 400, screen)
    while pause:
        for event in pygame.event.get():
            if event.type == KEYDOWN:
                print("keydown",event.type, KEYDOWN)
                if event.key == pygame.K_p:
                    print("K_p", event.key, pygame.K_p)
                    pause = False
                    print(pause)

            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        # gameDisplay.fill(white)

        #button("Continue", 150, 450, 100, 50, green, bright_green, unpause)
        #button("Quit", 550, 450, 100, 50, red, bright_red, quitgame)

        pygame.display.update()
        #pygame.time.clock.tick(15)

def notification(message):
    myparameters.notifications.append(message)

def use_potion(player,potion_type):
    if potion_type == 'HP Potion':
        if myparameters.item_inventory['HP Potion'] >0:
            player.HP = min(player.HP +round(player.max_HP*0.6), player.max_HP)
            myparameters.item_inventory['HP Potion'] -= 1
    if potion_type == 'Mana Potion':
        if myparameters.item_inventory['Mana Potion'] > 0:
            player.Mana = min(player.Mana + round(player.max_Mana*0.6), player.max_Mana)
            myparameters.item_inventory['Mana Potion'] -= 1

def load_new_map(map_path, tileset_path , map_id,all_sprites, ob_tiles, slow_tiles,new_map_tiles,prev_map_tiles):
    all_sprites.empty()
    ob_tiles.empty()

    tilemap = Tilemap( map_path,tileset_path,map_id, all_sprites, ob_tiles, slow_tiles, new_map_tiles, prev_map_tiles)
    return tilemap

def respawn_enemy(tilemap, enemy_id,cnt, enemies, all_sprites, ob_tiles):
    while cnt>0:
        new_enemy = Skeleton(enemy_id, tilemap.size)
        if pygame.sprite.spritecollideany(new_enemy, ob_tiles):
            new_enemy.kill()
        else:
            enemies.add(new_enemy)
            all_sprites.add(new_enemy)
            cnt -= 1


#8448MiB with medium.en whisper and TTS
#13134MiB with large
#5276MiB with small.en
#4152MiB with tiny.en
#3988MiB with no whisper

import numpy as np
import io
import wave
import whisper
import torch
import json

from transformers import SpeechT5Processor, SpeechT5ForTextToSpeech, SpeechT5HifiGan
from datasets import load_dataset
import torch
import soundfile as sf

import queue
import threading
from pydub import AudioSegment
from pydub.playback import play

from Stopwatch import Stopwatch
sw = Stopwatch()

class TextToSpeech:
	def __init__(self):
		self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
		self.sw = Stopwatch()

	def text_to_speech(self, text:str) -> AudioSegment:
		raise NotImplementedError

class MicrosoftSpeechT5TTS(TextToSpeech):
	def __init__(self):
		super().__init__()
		self.sw.start("load text to speech")
		self.processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts")
		self.model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts").to(self.device)
		self.vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan").to(self.device)
		embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")
		self.speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0).to(self.device)
		self.sw.stop("load text to speech")

	def text_to_speech(self, text:str) -> AudioSegment:
		self.sw.start("text to speech")
		inputs = self.processor(text=text, return_tensors="pt")
		inputs = {name: tensor.to(self.device) for name, tensor in inputs.items()}
		speech = self.model.generate_speech(inputs["input_ids"], self.speaker_embeddings, vocoder=self.vocoder)
		speech = speech.cpu()
		self.sw.stop("text to speech")

		# Convert to 16-bit integer format
		speech = (speech * 32767).numpy().astype('int16')

		return AudioSegment(
			data=speech.tobytes(),
			sample_width=2,  # Update the sample width to 2 for 16-bit integer data
			frame_rate=16000,
			channels=1,
		)

tts = MicrosoftSpeechT5TTS()

# Variables for model size
model_size = "medium.en"  # Other options: "tiny", "small", "medium", "large"

# Load the Whisper model
sw.start("Loading Whisper model")
model = whisper.load_model(model_size)
sw.stop("Loading Whisper model")

for i in range(0,3):
	s1 = tts.text_to_speech(
		"Hello! I am a computer. Do you understand english like I do? If so, can you see how well I am handling punctuation? What about my pauses? Or my grammar?"
	)
	s1.export("file_1.wav", format="wav")

	s2 = tts.text_to_speech(
		"What if I am running on the Gee Pee yoU versus the (C) (P) (U)? Do you still understand me then? That would be good if you did because this thing is fairly slow to run on the [C] [P] [U]. "
	)
	s2.export("file_2.wav", format="wav")

	s3 = tts.text_to_speech(
		"I still as a computer however, do not do well, as you can see, reading acrynyms. Things like See, pea, you? arent read well when you type CPU."
	)
	s3.export("file_3.wav", format="wav")

def transcribe(file:str):
	sw.start("\n\nTranscribing...")
	# Transcribe the audio data with Whisper
	result = model.transcribe(file, language="English", fp16=torch.cuda.is_available())
	transcription_time = sw.stop("\n\nTranscribing...")["last"]

	from pydub import AudioSegment
	audio = AudioSegment.from_file(file)

	print(f"Seconds per seconds {audio.duration_seconds/transcription_time}")
	print(json.dumps(result, indent=4))

for i in range(0,3):
	transcribe("file_1.wav")
	transcribe("file_2.wav")
	transcribe("file_3.wav")


#!/bin/python3

"""
DFS approach on all indices with base case when target <= 0.
Sort array first so no need to do further DFS if array element exceeds target.
O(n(log(n))) running time complexity where n = len(nums)
"""
def combinationSum(nums, target):
	nums.sort()
	res = []

	dfs(nums, target, 0, len(nums), [], res)
	return res

def dfs(array, key, index, end, path, res):
	if key < 0:
		return

	if key == 0:
		res.append(path)
		return

	for i in range(index, end):
		num = array[i]
		dfs(array, key - num, i, end, path + [num], res)

if __name__ == "__main__":
	print("Enter space-separated numbers: ", end="")
	nums = list(map(int, input().strip().split(' ')))

	print("Enter target: ", end="")
	target = int(input().strip())

	solutionSet = combinationSum(nums, target)
	print("Solution set:")
	print(solutionSet)


#UIAHandler.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2008-2018 NV Access Limited, Joseph Lee
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

from comtypes import COMError
import config
from logHandler import log

handler=None
isUIAAvailable=False

if config.conf and config.conf["UIA"]["enabled"]:
	# Because Windows 7 SP1 (NT 6.1) or later is supported, just assume UIA can be used unless told otherwise.
	try:
		from _UIAHandler import *
		isUIAAvailable=True
	except ImportError:
		log.debugWarning("Unable to import _UIAHandler",exc_info=True)
		pass

def initialize():
	global handler
	if not isUIAAvailable:
		raise NotImplementedError
	try:
		handler=UIAHandler()
	except COMError:
		handler=None
		raise RuntimeError("UIA not available")

def terminate():
	global handler
	if handler:
		handler.terminate()
		handler=None


from art import logo, vs
from game_data import data
import random

print(logo)

score = 0
isFinished = False

alreadyUsed = []

itemOne = random.choice(range(0, len(data)))

while not isFinished:
  itemTwo = random.choice(range(0, len(data)))
  if itemTwo in alreadyUsed:
    itemTwo = random.choice(range(0, len(data)))
  else:
    alreadyUsed.append(itemTwo)

  print(f"\n{data[itemOne]['name']}, {data[itemOne]['description']}, from {data[itemOne]['country']} has {data[itemOne]['follower_count']}M followers")
  print(vs)
  print(f"{data[itemTwo]['name']}, {data[itemTwo]['description']}, from {data[itemTwo]['country']}\n")

  guess = input(f"Do you think that {data[itemTwo]['name']} has more followers? (y/n)").lower()

  if guess == "y":
    if data[itemTwo]["follower_count"] > data[itemOne]["follower_count"]:
      score += 1
      itemOne = itemTwo
    elif data[itemTwo]["follower_count"] < data[itemOne]["follower_count"]:
      print(f"\nYou lose, your score is {score}")
      isFinished = True
  elif guess == "n":
    if data[itemTwo]["follower_count"] < data[itemOne]["follower_count"]:
      score += 1
      itemOne = itemTwo
    elif data[itemTwo]["follower_count"] > data[itemOne]["follower_count"]:
      print(f"\nYou lose, your score is {score}")
      isFinished = True


import utils.charts as charts
import utils.chartUtils as chartUtils
import utils.dcrdata_api as dcrdata_api
import pandas as pd
import datetime as dt
from matplotlib import pyplot as plt
import utils.cm as cm
import matplotlib.ticker as ticker
# set colours
import numpy as np
import matplotlib.dates as mdates
import matplotlib.colors as mcolors

# this script pulls out the daily flows of the addresses that received
# 100k or more DCR from block rewards

# build address list - this was taken with a query on the dcrdata postgres db, addresses with over 100k dcr from cb

addrList =  ['DshuxHmbE8qvRu91fMQGaQV6j1oDijkpoJk',
            'DsfD7KYsJtRraYXPZM61ui7779oYJCakYvH',
            'DsXFWjdKKaE4ftDa1AVam35EX9wdaDeZz3q',
            'DskFbReCFNUjVHDf2WQP7AUKdB27EfSPYYE',
            'DshN1YKazYrnVcf9ZmudCiuvYCif72rQqUw',
            'Dso9uYBoEfvka4L7zLw9YoS5N7d6wmHu3sj',
            'Dsa3yVGJK9XFx6L5cC8YzcW3M5Q85wdEXcz',
            'DsaczRtjC31N6XVV69qcBoyR2BEEmjRDay3',
            'DshZYJySTD4epCyoKRjPMyVmSvBpFuNYuZ4',
            'DscMACUgLBhyDhVcg8YgaX68JJVrbdNndrb',
            'DsVc7oQTKhGUQ9DyCXbPEkHCoqT2cmkjgS8',
            'DseXBL6g6GxvfYAnKqdao2f7WkXDmYTYW87',
            'DsjukEpaBShycR6GW3gMX5t8j8kSCLKLTUw',
            'DsnxqhJX2tjyjbfb9y4yPdpJ744G9fLhbbF',
            'DsYAN3vT15rjzgoGgEEscoUpPCRtwQKL7dQ',
            'DsZWrNNyKDUFPNMcjNYD7A8k9a4HCM5xgsW',
            'DsfUs6UvDuvqPka1LK9JFZPiRetJ4WNycmn',
            'DsiDegkW7HxidcUCNpRqpgskc9JFCkqwWeu',
            'Dsh5gKAtf63WuzeqxFV7vJTFkPRkE35Zaf9',
            'Dsh3RaFqAgnfnGqY9cQiJUde5cR7qwdQP8r',
            'DsSWTHFrsXV77SwAcMe451kJTwWjwPYjWTM',
            'DshMNsvETDWpVoCe1re9NTAChiJagzsFV7J']

mLaunch =  [
            dt.date(int(2018), int(1), int(1)),     # baikal bk-b
            dt.date(int(2018), int(4), int(1)),     # innosilicon D9
            dt.date(int(2018), int(6), int(1)),     # baikal bk-d, ffminer d18, obelisk dcr-1, Huobi Listing, OKEX Listing, dex announcement
            dt.date(int(2018), int(8), int(1)),     # innosilicon D9+ ffminer ds19, ibelink dsm7T
            dt.date(int(2018), int(9), int(1)),     # bitmain DR-3
            dt.date(int(2018), int(10), int(1)),    # strongU STU-U1 and STU-U1+ - Binance Listing
            dt.date(int(2018), int(11), int(1)),    # MicroBT Whatsminer D1
            dt.date(int(2018), int(12), int(1)),    # Bitmain DR-5
            dt.date(int(2019), int(7), int(1)),     # StrongU STU-U1++
            dt.date(int(2020), int(10), int(1)),    # DEX launch
            dt.date(int(2021), int(9), int(1)),     # Bitmain DR-5 new batch
            dt.date(int(2021), int(11), int(1)),    # 10/80 proposal announced
            dt.date(int(2022), int(5), int(8)),     # 10/80 activates
            dt.date(int(2022), int(9), int(1))      # Huobi delisting
            ]

tf = 'all'

# create a dataframe with the date range:
fmtt = '%Y-%m-%dT%H:%M:%S'
if tf == 'all':
    dStart = pd.to_datetime(dt.date(int(2016),int(2),int(1)), utc=True, format=fmtt, errors='ignore')
    dEnd = pd.to_datetime(dt.date.today(), utc=True, format=fmtt, errors='ignore')
if tf == 'bear1':
    dStart = pd.to_datetime(dt.date(int(2018),int(2),int(1)), utc=True, format=fmtt, errors='ignore')
    dEnd = pd.to_datetime(dt.date(int(2021),int(1),int(1)), utc=True, format=fmtt, errors='ignore')
if tf == 'bear2':
    dStart = pd.to_datetime(dt.date(int(2021),int(1),int(1)), utc=True, format=fmtt, errors='ignore')
    dEnd = pd.to_datetime(dt.date.today(), utc=True, format=fmtt, errors='ignore')

# create date array to start
date_rng = pd.date_range(start=dStart, end=dEnd, freq='d')
date = pd.DataFrame(date_rng, columns=['date'])
# initialise data arrays
data = date
# create empty dataframe for total by address
total = pd.DataFrame(columns=['address','received'])

# this function creates a vertical line for every event
def eventsVL(ax):
    i = 0
    for dateX in mLaunch:
        pddate = pd.to_datetime(dateX, utc=True, format=fmtt, errors='ignore')
        i = i + 1
        if (pddate < dEnd and pddate > dStart):
            ax.axvline(dateX, color=charts.colour_hex('dcr_orange'),alpha=0.25)
            dist = 0
            if tf == 'all':
                dist = -7
            if tf == 'bear2':
                dist = 10
            if tf == 'bear1':
                dist = 5
            ax.text(dateX, dist, str(i), ha='center',fontsize=6)

# iterate over the addrList, merge a new column with the running balance of each address into the
# data df created above.
i = 0
for addr in addrList:
    i = i + 1
    print('Processing ', i, ' of', len(addrList), 'addresses: '+addr)
    # pull daily flows for this address
    raw = dcrdata_api.addressFlow(addr)
    # create row with the total received for this address and append to the df
    row = {'address': addr, 'received': raw['received'].sum()}
    total.loc[len(total), :] = row
    # merge to date array
    dataAddr = date.merge(raw, left_on='date', right_on='date', how='left')
    # set NAs to 0
    dataAddr = dataAddr.fillna(0)
    # calculate the cumsum shfited by 1 to get a running balance
    #dataAddr[addr] = dataAddr['net'].shift(1).cumsum().clip(lower=0)
    dataAddr[addr] = dataAddr['sent']
    # set NAs to 0 - should only be the first value
    dataAddr = dataAddr.fillna(0)
    # remove other columns
    dataAddr = dataAddr.drop(columns=['net', 'sent', 'received'])
    # merge back into data df
    data = data.merge(dataAddr, left_on='date', right_on='date', how='left')
    # delete temp arrays
    del raw, dataAddr

# set date as index
data = data.set_index('date')
# create a sum
data['total'] = data[list(data.columns)].sum(axis=1)
# grab list of columns
cols = addrList

# use coinmetrics to grab price data
pMetric = 'PriceUSD'
price = cm.getMetric('DCR',pMetric,dStart,dEnd)
# merge price
data = data.merge(price, left_on='date', right_on='date', how='left')

# CHARTING
for i in range(0,len(cols)):
    # prepare chart and fig titles
    fTitle = cols[i] + ' Balance'
    sTitle = tf + '_' + cols[i]
    # create figure
    ax, xfig = charts.fig(fTitle, 'Address Balance (DCR)', None, dStart, dEnd,DJ=False)
    #price
    axs = chartUtils.plot_secondary(data[pMetric], 'Price', 'dcr_grey50', ax,None, 'linear', 0.5,legloc='upper right')
    # add events
    eventsVL(axs)
    # plot
    chartUtils.plot_primary(data[cols[i]], 'Address Balance (DCR)', 'dcr_black', ax, 'linear', 1,legloc='upper left')
    # layout
    plt.tight_layout()
    # save figure
    chartUtils.saveFigure(xfig, fTitle)

# Chart the Total
fTitle = 'Combined 100k+ PoW Block Reward Address Balance'

# create figure
ax, xfig = charts.fig(fTitle, 'Address Balance (DCR)', None, dStart, dEnd, DJ=False)
# plot price
axs = chartUtils.plot_secondary(data[pMetric], 'Price', 'dcr_grey50', ax, None, 'linear', 0.5,legloc='upper right')
# add events
eventsVL(axs)
# plot main
chartUtils.plot_primary(data['total'], 'Address Balance (DCR)', 'dcr_black', ax, 'linear', 1,legloc='upper left')
# layout
plt.tight_layout()
# save figure
chartUtils.saveFigure(xfig, fTitle)

pd.options.display.float_format = '{:,.2f}'.format


import numpy as np
import cv2
from matplotlib.backends.backend_agg import FigureCanvas
import random
from gym import Env, spaces, GoalEnv
from InsectGym.Voronoi.voronoi_maze import VoronoiMaze
from InsectGym.Voronoi.voronoi_maze_plots import VoronoiMazePlot
from InsectGym.Voronoi.VoronoiWorld import VoronoiWorld, Robot
font = cv2.FONT_HERSHEY_COMPLEX_SMALL

class VoronoiWorldGoal(VoronoiWorld, GoalEnv):
    def __init__(self, colors_dict=None, multi_route_prob=0.1, plot_path=None, task_path=None,
                 random_start=False, num_goals=1):
        # super(VoronoiWorldTarget, self).__init__(colors_dict=colors_dict, multi_route_prob=multi_route_prob)
        self.random_start = random_start
        super(VoronoiWorldGoal, self).__init__(colors_dict=colors_dict, multi_route_prob=multi_route_prob,
                                               plot_path=plot_path, task_path=task_path, num_exits=num_goals)
        # self.width = 100
        # self.height = 100
        # self.maze = VoronoiMaze(width=self.width, height=self.height, multi_route_prob=multi_route_prob)
        # self.locations = np.array(self.maze.voronoi.points)
        # print("max_viable_neighbours = %d" % self.maze.max_viable_neighbours)
        # if not colors_dict:
        #     self.colors_dict = {
        #         "background_color": "#e0e0e0",
        #         "maze_line_color": "navy",
        #         "neighbor_line_color": "orange",
        #         "point_color": "orange",
        #         "start_color": "blue",
        #         "exit_color": "green",
        #         "polygon_face_color": "#1fc600",
        #         "polygon_edge_color": "none",
        #         "polygon_backtracking_color": "purple",
        #         "polygon_backtracking_edge_color": "none"
        #     }
        # else:
        #     self.colors_dict = colors_dict

        # # Define a 2-D observation space
        # self.observation_shape = (1,)
        # self.observation_space = spaces.Discrete(len(self.maze.voronoi.points))
        self.observation_space = spaces.Dict(dict(
            desired_goal=spaces.Discrete(len(self.maze.voronoi.points)),
            achieved_goal=spaces.Discrete(len(self.maze.voronoi.points)),
            observation=spaces.Discrete(len(self.maze.voronoi.points)),
        ))
        # # Define an action space according to self.maze.max_viable_neighbours
        # self.action_space = spaces.Discrete(self.maze.max_viable_neighbours, )
        #
        # self.number_of_locations = len(self.locations)
        # self.init_plot_on_canvas()
        # self.reset()

    def step(self, action):
        # Flag that marks the termination of an episode
        done = False

        # Assert that it is a valid action
        assert self.action_space.contains(action), "Invalid Action"

        # apply the action to the robot
        if len(self.maze.path_graph[self.robot.location]) > action:
            new_location = self.maze.path_graph[self.robot.location][action]
            location_index = self.coordinate_to_index(new_location)
            self.robot.move_to(new_location, location_index)
        else:
            self.reward -= 0  # no punishment to hit the wall for now.
        self.robot.cost()
        # If out of fuel, end the episode.
        if self.robot.fuel_left == 0:
            done = True
        if np.any(self.robot.location_index == self.goal_location_index):
            done = True
        obs = {
            'observation': self.robot.location_index,
            'achieved_goal': self.robot.location_index,
            'desired_goal': self.goal_location_index
        }
        state = {'robot_location': self.robot.location, 'goal_location': self.goal_location, 'actual_action':action}
        self.reward = self.compute_reward(self.robot.location_index, self.goal_location_index, state)
        return obs, self.reward, done, state

    def compute_reward(self, achieved_goal, desired_goal, info):
        # Reward for executing a step.
        reward = -1
        if len(self.maze.path_graph[info['robot_location']]) <= info['actual_action']:
            self.reward -= 0  # no punishment to hit the wall for now.
        if np.any(achieved_goal == desired_goal):
            reward += 20
        return reward

    def reset(self):
        if self.random_start:
            self.start_location_index = random.choice(range(self.number_of_locations))
        else:
            if hasattr(self, 'goal_location_index'):
                self.start_location_index = self.robot.location_index
            else:
                self.start_location_index = random.choice(range(self.number_of_locations))
        self.robot = Robot(location=self.index_to_coordinate(self.start_location_index),
                           location_index=self.start_location_index)

        self.goal_location_index = np.random.choice(len(self.locations), self.num_exits).tolist()
        if self.num_exits == 1:
            self.goal_location_index = self.goal_location_index[0]
        self.goal_location = self.index_to_coordinate(self.goal_location_index)
        self.reward = 0
        self.init_enter_exit_on_canvas()
        self.draw_location_on_canvas()
        # return the observation
        obs = {
            'observation': self.robot.location_index,
            'achieved_goal': self.robot.location_index,
            'desired_goal': self.goal_location_index,
        }
        print(obs)
        return obs

if __name__ == "__main__":
    env = VoronoiWorldGoal(num_goals=2)
    obs = env.reset()
    while True:
        # Take a random action
        action = env.action_space.sample()
        obs, reward, done, info = env.step(action)

        # Render the game
        env.render()

        if done:
            break

    env.close()


from json import load
from pathlib import Path
from sys import path
from os import getcwd

import pytest
from web3 import (
    EthereumTesterProvider,
    Web3,
)

esp = EthereumTesterProvider()

@pytest.fixture
def eth_tester():
    return esp.ethereum_tester

@pytest.fixture
def w3():
    return get_w3()

def contract(smart_contract_name, parameters=[], contract_directory=Path('.')):
    build_contracts_dir = contract_directory / Path('build') / Path('contracts')
    contract_json_file = (build_contracts_dir / smart_contract_name).with_suffix('.json')

    with open(contract_json_file, 'r') as smart_contract_build_file:
        json_object = load(smart_contract_build_file)
        if json_object["compiler"]["name"] == "solidity":
            bytecode = json_object["bin"]
        else:
            bytecode = json_object["bytecode"]
        abi = json_object["abi"]
        w3 = get_w3()
        contract = w3.eth.contract(abi=abi, bytecode=bytecode)
        tx_hash = contract.constructor(*parameters).transact({"from": w3.eth.accounts[0]})
        tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
        contract = w3.eth.contract(abi=abi, address=tx_receipt.contractAddress)
        return contract

def get_w3():
    settings_directory = getcwd()
    path.append(settings_directory)
    import settings
    try:
        ganache_cli = settings.ganache_cli
        address = ganache_cli["development"]["address"]
        w3 = Web3(Web3.HTTPProvider(address))
        if w3.isConnected():
            return w3
        else:
            esp = EthereumTesterProvider()
            return Web3(esp)
    except AttributeError:
        return Web3(esp)

class TestContract:

    def setup_method(self, method):
        w3 = get_w3()
        try:
            w3.provider.ethereum_tester
        except AttributeError:
            w3.testing.snapshot()

    def teardown_method(self, method):
        w3 = get_w3()
        try:
            w3.provider.ethereum_tester
        except AttributeError:
            w3.testing.revert(1)


#!/usr/bin/env python

import sys
import os
import json
import regex
import vulnerabilities
from esbulkstream import Documents
from pathlib import Path
from tqdm import tqdm
from version_parser import Version

def get_deps(file_dir):

    path = Path(file_dir)

    dep_graph = {}

    file_count = 0
    for i in path.rglob('*'):
        file_count += 1

    for filename in tqdm(path.rglob('*'), total=file_count):

        if os.path.isdir(filename):
            continue

        with open(filename, mode="r") as fh:
            data = json.loads(fh.read())

            if "name" not in data:
                #print("No name %s" % filename)
                continue

            package_name = data["name"]

            if package_name not in dep_graph:
                dep_graph[package_name] = 0

            if not "versions" in data:
                # These are unpublished packages. Maybe do something with this
                # later
                continue

            for ver in data["versions"].keys():
                dep_set = set()

                if "dependencies" in data["versions"][ver]:
                    if type(data["versions"][ver]["dependencies"]) is list:
                        #print("List: %s" % filename)
                        continue

                    if type(data["versions"][ver]["dependencies"]) is str:
                        #print("String: %s" % filename)
                        continue

                    if type(data["versions"][ver]["dependencies"]) is type(None):
                        #print("None: %s" % filename)
                        continue

                    for dep in data["versions"][ver]["dependencies"].keys():

                        dep_set.add(dep)

                    # We only want to count a dep reference once per package.
                    # Otherwise a package with A LOT of releases could skew the
                    # numbers
                    for i in dep_set:
                        if i not in dep_graph:
                            dep_graph[i] = 0
                        dep_graph[i] += 1

    return dep_graph

def get_vers(file_dir):

    path = Path(file_dir)

    the_vers = {}

    file_count = 0
    for i in path.rglob('*'):
        file_count += 1

    for filename in tqdm(path.rglob('*'), total=file_count):

        if os.path.isdir(filename):
            continue

        with open(filename, mode="r") as fh:
            data = json.loads(fh.read())

            if "name" not in data:
                #print("No name %s" % filename)
                continue

            package_name = data["name"]

            if package_name not in the_vers:
                the_vers[package_name] = {}

            if not "time" in data:
                # These are unpublished packages. Maybe do something with this
                # later
                continue

            for ver in data["time"].keys():

                if ver == "modified":
                    continue
                elif ver == "created":
                    continue

                one_ver = Version(ver)

                # Skip weird versions
                #if type(one_ver) is LegacyVersion:
                #    continue

                the_time = data["time"][ver]

                major = one_ver.get_major_version()
                minor = one_ver.get_minor_version()
                micro = one_ver.get_micro_version()

                if not major in the_vers[package_name]:
                    the_vers[package_name][major] = {}

                if not minor in the_vers[package_name][major]:
                    the_vers[package_name][major][minor] = {}

                if not micro in the_vers[package_name][major][minor]:
                    the_vers[package_name][major][minor][micro] = the_time

    return dep_graph


import sys
import math

'''
Капитан Флинт зарыл клад на Острове сокровищ.
Он оставил описание, как найти клад.
Описание состоит из строк вида: "North 5",
где  слово – одно из "North", "South", "East", "West", – задает направление движения,
а  число – количество шагов, которое необходимо пройти в этом направлении.
Напишите программу, которая по описанию пути к кладу определяет точные координаты клада,
считая, что начало координат находится в начале пути,
ось OX направлена на восток, ось OY – на север.

Необходимо вывести  координаты клада – два целых числа через пробел.
Гарантируется, что эти числа не превосходят 10**8.

'''

def count_coord(d, v):
    if d == 'South':
        v = v * (-1)
        return 0, v
    elif d == 'North':
        return 0, v
    if d == 'East':
        return v, 0
    elif d == 'West':
        v = v * (-1)
        return v, 0

def run(iter):
	result_1 = 0
	result_2 = 0
	for i in iter:
		i = i.split()
		d = i[0]
		v = i[1]
		r1, r2 = count_coord(d, int(v))
		result_1 += r1
		result_2 += r2
	print(result_1, result_2)

inp = sys.stdin.read().strip()
inp = inp.split('\n')
run(inp)

# t = 'North 1 \n South 10'
# inp = t.split('\n')
# run(inp)

# t1 = 'South 19'
# inp = t1.split('\n')
# run(inp)


'''2D Raytracing Example using Pygame'''
import sys
from math import pi, cos, sin
import pygame

# Constants
SIZE = (600, 600)
BORDERS = [[0, 0, SIZE[0], 0], [0, 0, 0, SIZE[1]],
           [0, SIZE[1], SIZE[0], SIZE[1]], [SIZE[0], 0, SIZE[0], SIZE[1]]]
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)

class Ray:
    '''create rays and see their intersections'''

    def __init__(self, x_1, y_1, x_2, y_2):
        self.x_1 = x_1
        self.y_1 = y_1
        self.x_2 = x_2
        self.y_2 = y_2

    def cast(self):
        '''see https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection'''
        # Checks Window Borders
        for border in BORDERS:
            x_3 = border[0]
            y_3 = border[1]
            x_4 = border[2]
            y_4 = border[3]
            P = (self.x_1 - self.x_2) * (y_3 - y_4) - \
                (self.y_1 - self.y_2) * (x_3 - x_4)
            if P != 0:
                t = ((self.x_1 - x_3) * (y_3 - y_4) -
                     (self.y_1 - y_3) * (x_3 - x_4))/P
                u = -((self.x_1 - self.x_2) * (self.y_1 - y_3) -
                      (self.y_1 - self.y_2) * (self.x_1 - x_3))/P
                if 0 <= t and u >= 0 and u <= 1:
                    point = ((self.x_2 - self.x_1) * t +
                             self.x_1, (y_4 - y_3) * u + y_3)
                    break

        # Checks Barriers
        for barrier in Barrier.collection:
            x_3 = barrier[0]
            y_3 = barrier[1]
            x_4 = barrier[2]
            y_4 = barrier[3]
            P = (self.x_1 - self.x_2) * (y_3 - y_4) - \
                (self.y_1 - self.y_2) * (x_3 - x_4)
            if P != 0:
                t = ((self.x_1 - x_3) * (y_3 - y_4) -
                     (self.y_1 - y_3) * (x_3 - x_4))/P
                u = -((self.x_1 - self.x_2) * (self.y_1 - y_3) -
                      (self.y_1 - self.y_2) * (self.x_1 - x_3))/P
                if 0 <= t and u >= 0 and u <= 1:
                    npoint = ((self.x_2 - self.x_1) * t +
                              self.x_1, (y_4 - y_3) * u + y_3)
                    if abs(npoint[0] - self.x_1) < abs(point[0] - self.x_1):
                        point = npoint
        # Draws Ray
        pygame.draw.aaline(screen, GREEN, (self.x_1, self.y_1), point)

class Radar:
    '''creates rays around a point'''

    def __init__(self, x, y, N):
        self.rays = []
        # N represents number of Rays
        for i in range(0, N):
            # Formula to create rays around a point
            ray = Ray(x, y, x + cos(i/N * 2 * pi), y + sin(i/N * 2 * pi))
            self.rays.append(ray)

    def radiate(self):
        '''emits rays'''
        for ray in self.rays:
            ray.cast()

class Barrier:
    '''create barriers for rays to intersect with'''
    collection = []

    def __init__(self, x_1, y_1, x_2, y_2):
        Barrier.collection.append([x_1, y_1, x_2, y_2])

def draw_barrier():
    '''draws Barriers'''
    for barrier in Barrier.collection:
        p_1 = (barrier[0], barrier[1])
        p_2 = (barrier[2], barrier[3])
        pygame.draw.aaline(screen, BLACK, p_1, p_2)

def create_map():
    '''initializes custom map'''
    width = SIZE[0]
    height = SIZE[1]
    Barrier(width/6, height, width/6, height/2)
    Barrier(width/3, height, width/3, height/1.5)
    Barrier(width/2, height/2, width/6, height/2)
    Barrier(width/2, height/1.5, width/3, height/1.5)
    Barrier(width/1.5, height/1.5, width/1.5, height/2)
    Barrier(width/1.2, height/2, width/1.5, height/2)
    Barrier(width/1.2, height/2, width/1.2, height/1.5)
    Barrier(width/1.5, height/1.5, width/1.2, height/1.5)
    Barrier(width/3, height/6, width/3, height/3)
    Barrier(width/3, height/6, width/2, height/3)
    Barrier(width/2, height/6, width/2, height/3)
    Barrier(width/2, height/6, width/1.5, height/3)
    Barrier(width/1.5, height/6, width/1.5, height/3)

# Initialize Screen
pygame.init()
pygame.display.set_caption("Raytracing Example")
screen = pygame.display.set_mode(SIZE)
create_map()

# Game Loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
    pygame.display.flip()
    mouse = pygame.mouse.get_pos()
    radar = Radar(mouse[0], mouse[1], 25)
    screen.fill(WHITE)
    draw_barrier()
    radar.radiate()


from typing import *

class Solution:
    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        if n < 2:
            return n
        alloc = [1] * n

        for i in range(1, n):
            if ratings[i] > ratings[i-1]:
                alloc[i] = alloc[i-1] + 1

        for i in range(n-2, -1, -1):
            if ratings[i] > ratings[i+1]:
                alloc[i] = max(alloc[i], alloc[i+1]+1)

        return sum(alloc)

def test(test_name, ratings, expected):
    res = Solution().candy(ratings)
    if res == expected:
        print(test_name + ' succeed')
    else:
        print(test_name + ' fail')

if __name__ == '__main__':
    ratings1 = [1,0,2]
    expected1 = 5
    test('test1', ratings1, expected1)

    ratings2 = [1,2,2]
    expected2 = 4
    test('test2', ratings2, expected2)

    ratings3 = [1,3,4,5,2]
    expected3 = 11
    test('test3', ratings3, expected3)

    ratings4 = [1,2]
    expected4 = 3
    test('test4', ratings4, expected4)


# Uses python3
import sys

def optimal_summands(n):
    summands = []
    total = 0
    if n < 3:
        return [n]

    for i in range(1, n):
        if total + i < n:
            total += i
            summands.append(i)
        elif total + i == n:
            summands.append(i)
            return summands
        else:
            rem = total+i-n
            summands.remove(rem)
            total -= rem
            total += i
            summands.append(i)
            return summands

    return summands

if __name__ == '__main__':
    input = sys.stdin.read()
    n = int(input)
    summands = optimal_summands(n)
    print(len(summands))
    for x in summands:
        print(x, end=' ')


from openerp import models, fields, api, modules, _, SUPERUSER_ID
from datetime import datetime

FIELDS_BLACKLIST = [
    'id', 'create_uid', 'create_date', 'write_uid', 'write_date',
    'display_name', '__last_update',
]

EMPTY_DICT = {}

class AuditTrackedFieldsRule(models.Model):
    _name = 'audit.tracked_fields.rule'
    _description = "Audit Tracked Fields - Settings"
    _rec_name = "model_id"

    @api.one
    @api.depends('field_ids')
    def _compute_field_names(self):
        field_names = [ f.name for f in self.field_ids ]
        self.field_names = str(field_names or [])

    model_id = fields.Many2one('ir.model', "Model", required=True, ondelete='cascade', help="Select model for which you want to track a field.")
    model = fields.Char("Model Technical Name", related='model_id.model', store=True, readonly=True)
    field_ids = fields.Many2many('ir.model.fields', 'audit_tracked_fields_rule_ir_fields_rel', 'field_id', 'rule_id',
                                  string="Tracking Fields", required=True, domain="[('model_id','=',model_id)]")
    field_names = fields.Char('Fields Names List', compute='_compute_field_names', store=True)
    log_create = fields.Boolean("Log Creates", default=True)
    log_write = fields.Boolean("Log Writes", default=True)
    log_unlink = fields.Boolean("Log Deletes", default=True)
    state = fields.Selection([('draft', "Draft"), ('subscribed', "Subscribed")],
        string="State", required=True, default='subscribed')

    _sql_constraints = [
        ('model_uniq', 'unique(model_id)',
         ("There is already a configuration defined on this model\n"
          "You cannot define another: please edit the existing one."))
    ]

    def _register_hook(self, cr, ids=None):
        super(AuditTrackedFieldsRule, self)._register_hook(cr)
        if not hasattr(self.pool, '_audit_tracked_field_cache'):
            self.pool._audit_tracked_field_cache = {}
        if not hasattr(self.pool, '_audit_tracked_field_model_cache'):
            self.pool._audit_tracked_field_model_cache = {}
        if ids is None:
            ids = self.search(cr, SUPERUSER_ID, [('state', '=', 'subscribed')])
        return self._patch_methods(cr, SUPERUSER_ID, ids)

    @api.multi
    def _patch_methods(self):
        """ Patch ORM methods of models defined in rules to log their calls. """
        updated = False
        model_cache = self.pool._audit_tracked_field_model_cache
        for rule in self:
            if rule.state != 'subscribed':
                continue
            if not self.pool.get(rule.model_id.model):
                # ignore rules for models not loadable currently
                continue
            model_cache[rule.model_id.model] = rule.model_id.id
            model_model = self.env[rule.model_id.model]
            #   -> create
            check_attr = 'audit_tracked_fields_ruled_create'
            if getattr(rule, 'log_create') \
                    and not hasattr(model_model, check_attr):
                model_model._patch_method('create', rule._make_create())
                setattr(model_model, check_attr, True)
                updated = True
            #   -> write
            check_attr = 'audit_tracked_fields_ruled_write'
            if getattr(rule, 'log_write') \
                    and not hasattr(model_model, check_attr):
                model_model._patch_method('write', rule._make_write())
                setattr(model_model, check_attr, True)
                updated = True
            #   -> unlink
            check_attr = 'audit_tracked_fields_ruled_unlink'
            if getattr(rule, 'log_unlink') \
                    and not hasattr(model_model, check_attr):
                model_model._patch_method('unlink', rule._make_unlink())
                setattr(model_model, check_attr, True)
                updated = True
        return updated

    @api.multi
    def _revert_methods(self):
        """Restore original ORM methods of models defined in rules."""
        updated = False
        for rule in self:
            model_model = self.env[rule.model_id.model]
            for method in ['create','write','unlink']:
                if getattr(rule, 'log_%s' % method) and hasattr(
                        getattr(model_model, method), 'origin'):
                    model_model._revert_method(method)
                    updated = True
        if updated:
            modules.registry.RegistryManager.signal_registry_change(
                self.env.cr.dbname)

    @api.model
    def create(self, vals):
        """ Update the registry when a new rule is created. """
        new_record = super(AuditTrackedFieldsRule, self).create(vals)
        if self._model._register_hook(self.env.cr, new_record.ids):
            modules.registry.RegistryManager.signal_registry_change(
                self.env.cr.dbname)
        return new_record

    @api.multi
    def write(self, vals):
        """ Update the registry when existing rules are updated. """
        super(AuditTrackedFieldsRule, self).write(vals)
        if self._model._register_hook(self.env.cr, self.ids):
            modules.registry.RegistryManager.signal_registry_change(
                self.env.cr.dbname)
        return True

    @api.multi
    def unlink(self):
        """ Unsubscribe rules before removing them. """
        self.unsubscribe()
        return super(AuditTrackedFieldsRule, self).unlink()

    @api.multi
    def _make_create(self):
        self.ensure_one()

        @api.model
        @api.returns('self', lambda value: value.id)
        def do_create(self, vals, **kwargs):
            rule_model = self.env['audit.tracked_fields.rule']
            do_logging = rule_model.search([('model','=',self._name)], limit=1)
            if do_logging:
                self = self.with_context(audit_tracked_fields_disabled=True)
            new_record = do_create.origin(self, vals, **kwargs)
            if do_logging:
                new_values = dict(
                    (d['id'], d) for d in new_record.sudo()
                    .with_context(prefetch_fields=False).read(eval(do_logging.field_names)))
                rule_model.sudo().create_logs(
                    self.env.uid, self._name, new_record.ids,
                    'create', None, new_values)
            return new_record

        return do_create

    @api.multi
    def _make_write(self):
        self.ensure_one()

        @api.multi
        def do_write(self, vals, **kwargs):
            rule_model = self.env['audit.tracked_fields.rule']
            record = rule_model.search([('model','=',self._name)], limit=1)
            read_fields = record and [ f for f in vals.keys() if f in eval(record.field_names)] or []
            do_logging = bool(read_fields)
            if do_logging:
                self = self.with_context(audit_tracked_fields_disabled=True)
                old_values = dict(
                    (d['id'], d) for d in self.sudo()
                    .with_context(prefetch_fields=False).read(read_fields))
            result = do_write.origin(self, vals, **kwargs)
            if do_logging:
                new_values = dict(
                    (d['id'], d) for d in self.sudo()
                    .with_context(prefetch_fields=False).read(read_fields))
                rule_model.sudo().create_logs(
                    self.env.uid, self._name, self.ids,
                    'write', old_values, new_values)
            return result

        return do_write

    @api.multi
    def _make_unlink(self):
        self.ensure_one()

        @api.multi
        def do_unlink(self, **kwargs):
            do_logging = self.env['audit.tracked_fields.log'].sudo().search_count([('model','=',self._name),('res_id','in',self.ids)])
            if do_logging:
                rule_model = self.env['audit.tracked_fields.rule']
                self = self.with_context(audit_tracked_fields_disabled=True)
                rule_model.sudo().create_logs(
                    self.env.uid, self._name, self.ids, 'unlink', {}, {})
            return do_unlink.origin(self, **kwargs)

        return do_unlink

    def create_logs(self, uid, res_model, res_ids, method,
                    old_values=None, new_values=None):
        """Create logs. `old_values` and `new_values` are dictionaries, e.g:
            {RES_ID: {'FIELD': VALUE, ...}}
        """
        if 'lang' not in (self._context or {}):
            self = self.with_context(lang=self.env.user.lang)

        if old_values is None:
            old_values = EMPTY_DICT
        if new_values is None:
            new_values = EMPTY_DICT

        def _get_field_value_text(value, field_def):
            field_type = field_def.get('ttype', False)
            if field_type == 'many2one':
                return value[1] if (isinstance(value, tuple) and len(value)==2) else value
            if field_type == 'selection':
                try:
                    return _(dict(self.env[field_def.get('model')]._fields[field_def['name']].selection).get(value, '')) or value
                except:
                    return value
            return value

        log_model = self.env['audit.tracked_fields.log'].sudo()
        IrModel = self.env['ir.model']
        model_model = self.env[res_model]
        for res_id in res_ids:
            name = model_model.browse(res_id).name_get()
            res_name = name and name[0] and name[0][1]
            vals = {
                'name': res_name,
                'model_id': self.pool._audit_tracked_field_model_cache[res_model],
                'res_id': res_id,
                'user_id': uid,
            }
            if method == 'create':
                fields_list = new_values.get(res_id, EMPTY_DICT).keys()
                for field_name in fields_list:
                    if field_name in FIELDS_BLACKLIST:
                        continue
                    field = self._get_field(IrModel.browse(vals['model_id']), field_name)
                    if field:
                        if field['relation'] and '2many' in field['ttype']:
                            continue
                        log_vals = vals.copy()
                        log_vals.update({
                            'field_id': field['id'],
                            'new_value': new_values[res_id][field['name']],
                            'new_value_text': _get_field_value_text(new_values[res_id][field['name']], field),
                            'description': _('Record Created')
                        })
                        log_model.create(log_vals)
            if method == 'write':
                fields_list = old_values.get(res_id, EMPTY_DICT).keys()
                for field_name in fields_list:
                    if field_name in FIELDS_BLACKLIST:
                        continue
                    field = self._get_field(IrModel.browse(vals['model_id']), field_name)
                    if field:
                        if field['relation'] and '2many' in field['ttype']:
                            continue
                        log_vals = vals.copy()
                        log_vals.update({
                            'field_id': field['id'],
                            'old_value': old_values[res_id][field['name']],
                            'new_value': new_values[res_id][field['name']],
                            'old_value_text': _get_field_value_text(old_values[res_id][field['name']], field),
                            'new_value_text': _get_field_value_text(new_values[res_id][field['name']], field),
                            'description': _('Record Updated')
                        })
                        previous_log = log_model.search([('model_id','=',vals['model_id']),
                                                         ('res_id','=',res_id),
                                                         ('field_id','=',field['id'])], limit=1)
                        new_log = log_model.create(log_vals)
                        if previous_log:
                            elapsed_time = (datetime.strptime(new_log.create_date, '%Y-%m-%d %H:%M:%S') - datetime.strptime(previous_log.create_date, '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600.0
                            previous_log.write({'elapsed_time': elapsed_time, 'elapsed_time_set': True})
            if method == 'unlink':
                log_vals = vals.copy()
                log_vals.update({
                    'old_value': False,
                    'old_value_text': False,
                    'new_value': False,
                    'new_value_text': False,
                    'field_id': False,
                    'description': _('Record Deleted')
                })
                previous_logs = log_model.search([('model_id','=',vals['model_id']),('res_id','=',res_id),('elapsed_time_set','=',False)])
                if previous_logs:
                    new_log = previous_logs[0].copy(log_vals)
                    updated_field_ids = []
                    for log in previous_logs:
                        if log.field_id.id not in updated_field_ids:
                            updated_field_ids.append(log.field_id.id)
                            elapsed_time = (datetime.strptime(new_log.create_date, '%Y-%m-%d %H:%M:%S') - datetime.strptime(log.create_date, '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600.0
                            log.write({'elapsed_time': elapsed_time, 'elapsed_time_set': True})

    def _get_field(self, model, field_name):
        cache = self.pool._audit_tracked_field_cache
        if field_name not in cache.get(model.model, {}):
            cache.setdefault(model.model, {})
            rule_model = self.env['audit.tracked_fields.rule'].sudo()
            record = rule_model.search([('model_id','=',model.id)], limit=1)
            do_logging = record and field_name in eval(record.field_names)
            if not do_logging:
                cache[model.model][field_name] = False
            else:
                # - search the field in the current model and those it inherits
                field_model = self.env['ir.model.fields'].sudo()
                all_model_ids = [model.id]
                all_model_ids.extend(model.inherited_model_ids.ids)
                field = field_model.search(
                    [('model_id', 'in', all_model_ids), ('name', '=', field_name)])
                # The field can be a dummy one
                if not field:
                    cache[model.model][field_name] = False
                else:
                    field_data = field.read(load='_classic_write')[0]
                    cache[model.model][field_name] = field_data
        return cache[model.model][field_name]

    @api.multi
    def unsubscribe(self):
        """Unsubscribe Auditing Rule on model."""
        # Revert patched methods
        self._revert_methods()
        self.write({'state': 'draft'})
        return True

class AuditTrackedFieldsLog(models.Model):
    _name = 'audit.tracked_fields.log'
    _description = "Audit Tracked Fields - Log"
    _order = "create_date desc"

    @api.one
    @api.depends('elapsed_time_set')
    def _compute_elapsed_time(self):
        elapsed_time_computed = 0.0
        if self.field_id:
            if not self.elapsed_time_set:
                elapsed_time_computed = (datetime.now() - datetime.strptime(self.create_date, '%Y-%m-%d %H:%M:%S')).total_seconds() / 3600.0
            else:
                elapsed_time_computed = self.elapsed_time
        self.elapsed_time_computed = elapsed_time_computed

    name = fields.Char("Resource Name")
    model_id = fields.Many2one('ir.model', string="Model", required=True, index=True, ondelete='cascade')
    model = fields.Char("Model Technical Name", related='model_id.model', store=True, readonly=True)
    field_id = fields.Many2one('ir.model.fields', string="Field", required=False, index=True, ondelete='cascade')
    field_name = fields.Char("Field Technical Name", related='field_id.name', readonly=True)
    old_value = fields.Text("Old Value")
    new_value = fields.Text("New Value")
    old_value_text = fields.Text("Old Value")
    new_value_text = fields.Text("New Value")
    elapsed_time = fields.Float('Elapsed Time', default=0.0, help="Elapsed time in hours with the field value of record.")
    elapsed_time_computed = fields.Float('Elapsed Time', compute="_compute_elapsed_time", help="Elapsed time in hours with the field value of record.")
    elapsed_time_set = fields.Boolean("Elapsed Time Set", readonly=True, default=False)
    res_id = fields.Integer("Related Resource ID")
    user_id = fields.Many2one('res.users', string="User", help="User that makes field modification")
    description = fields.Text("Additional Information")

    def fields_get(self, cr, uid, allfields=None, context=None, write_access=True, attributes=None):
        res = super(AuditTrackedFieldsLog, self).fields_get(cr, uid, allfields=allfields, context=context, write_access=write_access, attributes=attributes)
        if 'res_id' in res:
            res['res_id'].update({'type': 'char', 'searchable': False})
        return res

    @api.model
    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        result = super(AuditTrackedFieldsLog, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
        if 'elapsed_time_computed' in fields or 'elapsed_time' in fields:
            cr = self.env.cr
            for line in result:
                line_domain = line.get('__domain', domain)
                line_domain_ets = line_domain + [('elapsed_time_set','=',True)]
                line_domain_no_ets = line_domain + [('elapsed_time_set','=',False)]
                tables = 'audit_tracked_fields_log'
                where_clause = ""
                where_clause_params = []
                if line_domain:
                    query = self._where_calc(line_domain_ets)
                    tables, where_clause, where_clause_params = query.get_sql()

                query = """ SELECT sum(elapsed_time) as et
                            FROM %s """ % (tables,)
                if where_clause:
                    query += " WHERE %s " % (where_clause,)
                    cr.execute(query, tuple(where_clause_params))
                else:
                    cr.execute(query)
                __res = cr.fetchone()
                elapsed_time = __res and __res[0] or 0
                for row in self.search(line_domain_no_ets):
                    elapsed_time += row.elapsed_time_computed

                if 'elapsed_time' in fields:
                    line['elapsed_time'] = elapsed_time
                if 'elapsed_time_computed' in fields:
                    line['elapsed_time_computed'] = elapsed_time

        return result

    @api.multi
    def open_related_record(self):
        self.ensure_one()
        return self.pool[self.model].get_formview_action(self._cr, self._uid, self.res_id, context=self._context)


import logging
import os
import tempfile
import time
from typing import List, Optional

from music_album_creation.ffmpeg import FFMPEG

from .data import SegmentationInformation

logger = logging.getLogger(__name__)

ffmpeg = FFMPEG(os.environ.get('MUSIC_FFMPEG', 'ffmpeg'))

EXT = 'mp3'

class AudioSegmenter(object):
    def __init__(self, target_directory=tempfile.gettempdir()):
        self._dir = target_directory

    @property
    def target_directory(self):
        """The directory path that will serve as the destination for storing created tracks"""
        return self._dir

    @target_directory.setter
    def target_directory(self, directory_path):
        self._dir = directory_path

    def _trans(self, track_info):
        """Create (file) name of output track."""
        return [os.path.join(self._dir, f'{track_info[0]}.{EXT}')] + track_info[1:]

    def segment(self, album_file, data, sleep_seconds=0):
        """
        :param album_file:
        :param data:
        :param supress_stdout:
        :param supress_stderr:
        :param float sleep_seconds:
        :return:
        """
        exit_code = 0
        i = 0
        track_files: List[str] = []
        while exit_code == 0 and i < len(data) - 1:
            time.sleep(sleep_seconds)
            result = self._segment(
                album_file,
                *self._trans(list(data[i])),
            )
            if result.exit_code != 0:
                logger.error("Fmmpeg exit code: %s", result.exit_code)
                logger.error("Ffmpeg st out: %s", result.stdout)
                logger.error("Fmmpeg stderr: %s", result.stderr)
                raise FfmpegCommandError("Command '{}' failed".format(' '.join(self._args)))
            track_files.append(list(data[i])[1])
            i += 1
        result = self._segment(
            album_file,
            *self._trans(list(data[-1])),
        )
        if result.exit_code != 0:
            logger.error("Fmmpeg exit code: %s", result.exit_code)
            logger.error("Ffmpeg st out: %s", result.stdout)
            logger.error("Fmmpeg stderr: %s", result.stderr)
            raise FfmpegCommandError("Command '{}' failed".format(' '.join(self._args)))
        # TODO: remove the need to hard-code extension. should know from download module
        # if we download youtube video as mp4 audio, then each track should probably be mp4 too
        # if we download youtube video as mp3 audio, then each track should probably be mp3 too
        return [os.path.join(self._dir, f'{list(x)[0]}.{EXT}') for x in data]

    def segment_from_file(
        self,
        album_file,
        tracks_file,
        hhmmss_type,
        supress_stdout=True,
        supress_stderr=True,
        sleep_seconds=0,
    ):
        """
        Assumes the hhmmss are starting timestamps. Given an album audio file and a file with track information, segments the audio file into audio tracks which get stored in the 'self.target_directory' folder.\n
        :param str album_file:
        :param str tracks_file:
        :param str hhmmss_type:
        :param bool supress_stdout:
        :param bool supress_stderr:
        :param float sleep_seconds:
        :return:
        """
        with open(tracks_file, 'r') as f:
            segmentation_info = SegmentationInformation.from_multiline(
                f.read().strip(), hhmmss_type
            )
        return self.segment(
            album_file,
            segmentation_info,
            supress_stdout=supress_stdout,
            supress_stderr=supress_stderr,
            sleep_seconds=sleep_seconds,
        )

    def _segment(self, *args, **kwargs):
        album_file = args[0]
        track_file = args[1]
        start = args[2]  # starting timestamp
        end: Optional[str] = None  # end timestamp

        # if it is the last segment then end timestamp is the end of the album
        # and in that case the client code need to supply 3 arguments (not 4)
        if 3 < len(args):
            end = args[3]

        # args = ['ffmpeg', '-y', '-i', '-acodec', 'copy', '-ss']
        # self._args = args[:3] + ['{}'.format(album_file)] + args[3:] + [start] + (lambda: ['-to', str(end)] if end else [])() + ['{}'.format(track_file)]
        # COPY web stream as it is (no custom encododing)
        # so cannot change the file extension to store
        # output extension is better kept to be guessed by ffmpeg from the input file
        self._args = (
            '-y',
            '-i',
            str(album_file),  # youtube downloaded audio steam (ie local mp4 file)
            # '-acodec',
            # 'copy',
            # 'AAC',
            # '-c:a',
            # 'libmp3lame',
            # '-qscale:a',
            # '9',  # max quality
            # '-ab',
            # '133k',
            '-ss',
            start,
            *list((lambda: ['-to', str(end)] if end else [])()),
            '-f',
            'mp3',
            str(track_file),
        )
        # self._args = (
        #     '-y',
        #     '-i',
        #     str(album_file),  # youtube downloaded audio steam (ie local mp4 file)
        #     '-acodec',
        #     'copy',
        #     '-ss',
        #     start,
        #     *list((lambda: ['-to', str(end)] if end else [])()),
        #     str(track_file).replace('mp4', 'mp3'),
        # )
        logger.info("Segmenting: ffmpeg '{}'".format(' '.join(self._args)))
        return ffmpeg(
            *self._args,
        )

class FfmpegCommandError(Exception):
    pass

if __name__ == '__main__':
    import sys

    if len(sys.argv) < 3:
        print('Usage: python3 Album_segmentation.py AUDIO_FILE TRACKS_FILE')
        sys.exit(1)
    try:
        audio_segmenter = AudioSegmenter()
        audio_segmenter.segment_from_file(
            sys.argv[1],
            sys.argv[2],
            supress_stdout=True,
            supress_stderr=True,
            verbose=True,
            sleep_seconds=0.45,
        )
    except Exception as e:
        print(e)
        sys.exit(1)