import pygame as pg
vec = pg.math.Vector2
fps = 60


pg.font.init()
fonts = {
    'medieval': 'medieval.ttf',
    'minecraft': 'Minecraft Evenings.otf',
    '3dpixel': '3D-Pixel.ttf',
    '8bit': '8bitlim.ttf',
    '8bito': '8blimro.ttf',
    'arcade': 'ARCADECLASSIC.ttf',
    'modern_game': 'astron boy video.otf',
    'modern': 'astron boy.otf',
    'wonder': 'Beyond Wonderland.ttf',
    'curved': 'Digitag.ttf',
    'simple': 'DisposableDroidBB.ttf',
    'rounded': 'dpcomic.ttf',
    'playfull': 'Endalian Script.ttf',
    'blocky': 'FREAKSOFNATURE.ttf',
    'catchy': 'Future TimeSplitters.otf',
    'simple_wide': 'Halo3.ttf',
    'simple_fat': 'INVASION2000.ttf',
    'very_gamy': 'ka1.ttf',
    'simple_round': 'Karma Suture.otf',
    'mono': 'manaspc.ttf',
    'damaged': 'Merchant Copy.ttf',
    'big_natural': 'MorialCitadel.TTF',
    'spacy': 'nasalization-rg.otf',
    'sci-fi': 'neuropol.otf',
    'hollow_big_edge': 'papercut.ttf',
    'space_shuttle': 'pdark.ttf',
    'thin': 'PixelFJVerdana12pt.ttf',
    'random': 'Seattle Avenue.ttf',
    'pixel': 'yoster.ttf'
}

class Objects():
    def __init__(self, name, ms, sprite, x, y) -> None:
        self.name = name
        self.speed = ms
        with open(f'art/images/{sprite}') as i:
            self.sprite = pg.image.load(i)
        self.x = x
        self.y = y
        self.hidden = False
        self.rect = pg.Rect(self.x, self.y, self.sprite.get_width(), self.sprite.get_height())
    
    def draw(self, screen):
        if self.hidden:
            return
        self.rect.x, self.rect.y = self.x, self.y
        screen.blit(self.sprite, self.rect)

class NPC(Objects):
    def __init__(self, name, ms, sprite, convo_act, x, y) -> None:
        self.talking = False
        self.hidden = True
        super().__init__(name, ms, sprite, x, y)
        self.conversation = Convo('Hello, you can shoot fireballs with f now.', convo_act, 'person')
    
    def talk(self, objects):
        self.talking = True
        objects[0][0].talking = True
        self.conversation.hidden = False
    
    def draw(self, screen):
        super().draw(screen)
        if self.talking == True:
            self.conversation.draw(screen)
    
    def update(self, keys, objects):
        if self.talking:
            self.conversation.update(keys, objects)

class Convo():
    def __init__(self, text, convo_act, person, x = 140, y = 600, width = 1000, height = 100, font='simple', font_size = 20) -> None:
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.hidden = False
        self.font = pg.font.Font(f'fonts/{fonts[font]}', font_size)
        with open('art/images/label.png', 'r') as tb:
            self.box = pg.image.load(tb)
            self.box = pg.transform.scale(self.box, (width, height))
        self.labelRect = pg.Rect(self.x, self.y, self.width, self.height)
        self.labelSurf = self.font.render(text, True, '#1E90FF')


    def draw(self, screen):
        if self.hidden:
            return
        self.box.blit(self.labelSurf, [
            self.labelRect.width/2 - self.labelSurf.get_rect().width/2,
            self.labelRect.height/2 - self.labelSurf.get_rect().height/2
        ])
        screen.blit(self.box, self.labelRect)
    
    def update(self, keys, objects):
        if keys[pg.K_SPACE]:
            objects[0][0].book.addspell('fireball')
            self.talking = False
            objects[0][0].talking = False
            self.hidden = True




class Fighter(Objects):
    def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr) -> None:
        super().__init__(name, ms, sprite, x, y)
        self.health = health
        self.damage = damage
        self.level = level
        self.attack_speed = asp
        self.attack_range = atr
        self.lastHurt = pg.time.get_ticks()
        self.lastAttack = pg.time.get_ticks()
        self.hurtCooldown = 0


class MainCharacter(Fighter):
    def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr) -> None:
        super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr)
        self.book = Book(0, 0, [], None, None)
        self.talking = False
        self.level = Level(1000, 38, level, 150, 40, f'will to live: {level}%', 'simple', 20)
        self.health = Hearts(health, sprite=['fullheart.png', 'fullheart.png', 'fullheart.png', 'fullheart.png', 'fullheart.png'], x=900, y= 50, hurtCooldown=self.hurtCooldown)
    
    def draw(self, screen):
        if self.hidden:
            return
        self.rect.x, self.rect.y = self.x, self.y
        screen.blit(self.sprite, self.rect)
        self.health.draw(screen)
        self.level.draw(screen)
        self.book.draw(screen)

    def hurt(self, damage, objects):
        if not self.talking:
            self.health.hurt(damage)
    
    def walk(self, keys, objects):
        moveto = vec(0, 0)
        if keys[pg.K_w] or keys[pg.K_UP]:
            moveto += vec(0, -1)
        if keys[pg.K_a] or keys[pg.K_LEFT]:
            moveto += vec(-1, 0)
        if keys[pg.K_s] or keys[pg.K_DOWN]:
            moveto += vec(0, 1)
        if keys[pg.K_d] or keys[pg.K_RIGHT]:
            moveto += vec(1, 0)
        if not moveto == vec(0, 0):
            moveto.scale_to_length(self.speed)
        self.x += moveto[0] / fps
        self.y += moveto[1] / fps 
        touches = pg.sprite.spritecollideany(self, objects[1] + objects[2])
        if touches is not None:
            self.x -= moveto[0]*1.5 / fps #change later
            self.y -= moveto[1]*1.5 / fps #change later
            if isinstance(touches, NPC):
                    touches.talk(objects)
    
    def attack(self, obj, moveto = vec(0,1)):
        if self.lastAttack + self.attack_speed * 1000 < pg.time.get_ticks():
            if self.book.current_sp == 'fireball':
                weapon = Fireball('fb1', 100, self.x, self.y, moveto, 5)
            else:
                return
            obj[3].append(weapon)
            self.lastAttack = pg.time.get_ticks()

    def update(self, keys, objects):
        if not self.talking:
            self.walk(keys, objects)
        if keys[pg.K_f]:
            self.attack(objects)
        if self.health.health <= 0:
            return False
        else:
            return True

class Hearts():
    def __init__(self, health, sprite, x, y, hurtCooldown) -> None:
        self.x = x
        self.y = y
        self.health = health
        self.lastHurt = pg.time.get_ticks()
        self.hurtCooldown = hurtCooldown
        self.hidden = False
        self.sprite=[]
        for parts in sprite:
            with open(f'art/images/{parts}') as i:
                self.sprite.append(pg.image.load(i))
        self.rect = []
        for each in self.sprite:
            self.rect.append(pg.Rect(self.x, self.y, each.get_width(), each.get_height()))
        
    def hurt(self,damage):
        if self.lastHurt + self.hurtCooldown < pg.time.get_ticks():
            self.health -= damage
            self.lastHurt = pg.time.get_ticks()
            self.update()

    def draw(self, screen):
        if self.hidden:
            return
        for i in range(0, 5):
            self.rect[i].x, self.rect[i].y = self.x + i * 20, self.y
            screen.blit(self.sprite[i], self.rect[i])
    
    def update(self):
        sprite = []
        for i in range(0, 5):
            if self.health >= 4 + 4 * i:
                sprite.append('fullheart.png')
            elif self.health == 3 + 4 * i:
                sprite.append('dreiviertelheart.png')
            elif self.health >= 2 + 4 * i:
                sprite.append('halfheart.png')
            elif self.health >= 1 + 4 * i:
                sprite.append('viertelheart.png')
            elif self.health <= 4 * i:
                sprite.append('noheart.png')  
        self.sprite = []
        for parts in sprite:
            with open(f'art/images/{parts}') as i:
                self.sprite.append(pg.image.load(i))


class Level():
    def __init__(self, x, y, level, width, height, text, font, font_size) -> None:
        self.x = x
        self.y = y
        self.level = level
        self.width = width
        self.height = height
        self.font = pg.font.Font(f'fonts/{fonts[font]}', font_size)
        self.hidden = False
        with open('art/images/label.png', 'r') as tb:
            self.box = pg.image.load(tb)
            self.box = pg.transform.scale(self.box, (width, height))
        self.labelRect = pg.Rect(self.x, self.y, self.width, self.height)
        self.labelSurf = self.font.render(text, True, '#1E90FF')

    def draw(self, screen):
        self.box.blit(self.labelSurf, [
            self.labelRect.width / 2 - self.labelSurf.get_rect().width / 2,
            self.labelRect.height / 2 - self.labelSurf.get_rect().height / 2
        ])
        screen.blit(self.box, self.labelRect)

class Book():
    def __init__(self, x, y, spells, current_spell, current_shield) -> None:
        with open(f'art/images/book.png') as i:
            self.sprite = pg.image.load(i)
            self.sprite = pg.transform.scale(self.sprite, (1280, 720))
        self.x = x
        self.y = y
        self.hidden = True
        self.rect = pg.Rect(self.x, self.y, self.sprite.get_width(), self.sprite.get_height())
        self.sp_list = spells
        self.current_sp = current_spell
    
    def draw(self, screen):
        if self.hidden:
            return
        self.rect.x, self.rect.y = self.x, self.y
        screen.blit(self.sprite, self.rect)
    
    def addspell(self, spell):
        if spell not in self.sp_list:
            self.sp_list.append(spell)
            self.current_sp = spell
    def update(self):
        pass


class Mobs(Fighter):
    def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr, drops) -> None:
        super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr)
        self.drops = drops * (self.level / 2)

class Skeleton(Mobs):
    def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr, drops=0) -> None:
        super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops)
    
    def chase(self, obj):
        x = obj[0][0].x
        y = obj[0][0].y
        moveto = vec(x, y) - vec(self.x, self.y)
        if not (moveto).length() <= self.attack_range:
            moveto.scale_to_length(self.speed)
            self.x += moveto[0] / fps
            self.y += moveto[1] / fps
        else:
            self.attack(moveto, obj)

    def attack(self, moveto, obj):
        if self.lastAttack + self.attack_speed * 1000 < pg.time.get_ticks():
            obj[3].append(Arrow("arrow", 200, self.x, self.y, moveto, self.damage))
            self.lastAttack = pg.time.get_ticks()
    
    def hurt(self, damage, objects):
        self.health -= damage
        if self.health <= 0:
            self.hidden = True
            objects[1].remove(self)

    def update(self, obj):
        self.chase(obj)


class Weapons(Objects):
    def __init__(self, name, ms, sprite, x, y, moveto, damage) -> None:
        super().__init__(name, ms, sprite, x, y)
        self.moveto = moveto
        self.damage = damage
        pos = vec(1,0)
        angle = pos.angle_to(moveto)
        with open(f'art/images/{sprite}') as i:
            self.sprite = pg.transform.rotate(pg.image.load(i), -angle)
    
    def die(self, objects, kills):
        touches = pg.sprite.spritecollideany(self, objects[0] + objects[1])
        if touches is not None and isinstance(touches, kills):
            touches.hurt(self.damage, objects)
            self.hidden = True
            objects[3].remove(self)

class Spells(Weapons):
    def __init__(self, name, ms, sprite, x, y, moveto, damage) -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage)

class Fireball(Spells):
    def __init__(self, name, ms, x, y, moveto, damage, sprite = 'fireball.png') -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage)
    
    def move(self):
        self.moveto.scale_to_length(self.speed)
        self.x += self.moveto[0] / fps
        self.y += self.moveto[1] / fps
    
    def update(self, objects):
        self.move()
        self.die(objects, Mobs)
        
class Arrow(Weapons):
    def __init__(self, name, ms, x, y, moveto, damage, sprite = 'arrow.png') -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage)

    def move(self):
        self.moveto.scale_to_length(self.speed)
        self.x += self.moveto[0] / fps
        self.y += self.moveto[1] / fps
    
    def update(self, objects):
        self.move()
        self.die(objects, MainCharacter)
