import pygame as pg
from classes import *
from main import *

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.transform.scale2x(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)
        pg.draw.rect(screen, '#ef0120', self.rect,  2)

class NPC(Objects):
    def __init__(self, name, ms, sprite, convo_act, x, y) -> None:
        super().__init__(name, ms, sprite, x, y)
        self.talking = False
        self.hidden = False
        self.conversation = Convo('Hello, you can shoot fireballs with f now.', convo_act, 'person')
        self.lastUpdate = pg.time.get_ticks()
    
    def talk(self, objects):
        self.talking = True
        objects[0][0].talking = True
    
    def draw(self, screen):
        super().draw(screen)
        if self.talking:
            self.conversation.draw(screen)
    
    def update(self, keys, objects): 
        if  self.lastUpdate + 200 < pg.time.get_ticks():
            if self.talking:
                self.conversation.update(keys, objects, self)
                self.lastUpdate = pg.time.get_ticks()
            else:
                touches = pg.sprite.spritecollideany(self, objects[0])
                if touches is not None and keys[pg.K_SPACE] and isinstance(touches, MainCharacter):
                    self.talk(objects) 
                    self.lastUpdate = pg.time.get_ticks()


class Convo(Label):
    def __init__(self, text, convo_act, person, x = 140, y = 600, width = 1000, height = 100, font='simple', font_size = 20) -> None:
        super().__init__(x, y, width, height, text, font, font_size)
    
    def update(self, keys, objects, npc):
        if keys[pg.K_SPACE]:
            objects[0][0].book.addspell('fireball')
            npc.talking = False
            objects[0][0].talking = False


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, 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)
        self.thinks = Thinks(self.x+20, self.y-50, 150, 100, 'brr Im freezing')
        self.freezing = True

    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)
        pg.draw.rect(screen, '#e900fa', self.rect, 2)
        if self.thinks.hidden == False:
            self.thinks.draw(screen, self.x+20, self.y-100)

    def hurt(self, damage, objects):
        if not self.talking:
            self.health.hurt(damage)
    
    def obstacle_interaction(self, objects):
        touches = pg.sprite.spritecollideany(self, objects[4])
        if touches is not None:
            if touches.name == 'fireplace':
                self.freezing = False
            elif touches.name == 'portal':
                return 'play'
            elif touches.name == 'house':
                return 'house'
            elif 'wall' in touches.name:
                return 'wall'
        else:
             return True
    
    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[2] + objects[4])
        if touches is not None and not isinstance(touches, Weapons):
            if isinstance(touches, Obstacle):
                if not touches.collision:
                    # print(touches.name)
                    return
                if touches.type == 'wall':
                    if touches.name == 'wall_l':
                        self.x += (2 + (self.x - touches.rect.x))
                    elif touches.name == 'wall_r':
                        self.x -= (2 + self.rect.width - (touches.rect.x - self.x))
                    if touches.name == 'wall_t':
                        self.y += (2 + (self.y - touches.rect.y))
                    elif touches.name == 'wall_b':
                        self.y -= (2 + self.rect.height - (touches.rect.y - self.y))
                    return
            elif isinstance(touches, NPC):
                if keys[pg.K_f]:
                    touches.talk(objects)
                    return
                else:
                    return

            if self.x <= touches.rect.x: self.x -= (self.rect.width - (touches.rect.x - self.x))
            elif self.x > touches.rect.x: self.x += (self.rect.width - (self.x - touches.rect.x - touches.rect.width * 0.66))
            #if self.y <= touches.y: pass
            #elif self.y > touches.y: pass
            #self.x -= moveto[0] * 2 / fps
            #self.y -= moveto[1] * 2 / fps

        """
        if self.x <= 32:
            self.x = 33
        elif self.x >= objects[3][0].width - 32:
            self.x = objects[3][0].width - 32 - self.rect.width + 1
        if self.y <= 32:
            self.y = 33
        elif self.y >= objects[3][0].height - 32:
            self.y = objects[3][0].height - 32 - self.rect.height + 1
        """
    
    def attack(self, obj, mouse):
        if self.lastAttack + self.attack_speed * 1000 < pg.time.get_ticks():
            moveto = mouse - vec(self.x, self.y)
            if self.book.current_sp == 'fireball':
                weapon = Fireball('fb1', 100, self.x, self.y, moveto, 5)
            elif self.book.current_sp == 'windslash':
                weapon = Windslash('ws1', 100, self.x, self.y, moveto, 5)
            else:
                return
            obj[3].append(weapon)
            self.lastAttack = pg.time.get_ticks()

    def update(self, keys, mouse, objects):
        if not self.talking:
            self.walk(keys, objects)
            if pg.mouse.get_pressed()[0]:
                self.attack(objects, vec(mouse))
            self.thinks.update(self)
        if self.health.health <= 0:
            return 'village'
        else:
            return self.obstacle_interaction(objects)

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/main_attributes/{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/main_attributes/{parts}') as i:
                self.sprite.append(pg.image.load(i))


class Level(Label):
    def __init__(self, x, y, width, height, text, font='simple', font_size=20, font_color='#1e90ff', sprite='label.png') -> None:
        super().__init__(x, y, width, height, text, font, font_size, font_color, sprite)

class Thinks(Label):
    def __init__(self, x, y, width, height, text, font='simple', font_size=15, font_color='#000000', sprite='thinks.png') -> None:
        super().__init__(x, y, width, height, text, font, font_size, font_color, sprite)

    def draw(self, screen, x, y):
        if self.hidden:
            return
        self.x = x
        self.y = y
        super().draw(screen)
    
    def update(self, main):
        if not self.hidden:
            if not main.freezing:
                self.hidden = True

class Book():
    def __init__(self, x, y, spells, current_spell, current_shield) -> None:
        with open(f'art/images/main_attributes/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 = False
        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
        self.text_left = ["Dear User, ", "in case you fell on the ground too hard,", "here is a quick reminder:", 
                            "You are a homeless person.","One cold day you went to the library to get warm.", 
                            "There you saw and opened me out of boedom.", "This lead to you being thrown in this world.", 
                            "But you can find a way out of here again."] 
        self.text_right = ["This book will help you to survive.", "Click on a picture to choose your spell.",
                          "Talk to fairies to unlock new spells!"]
        self.buttons=[]
        self.buttons_y = 400
        self.buttons_x = 800

    
    def draw(self, screen):
        if self.hidden:
            return
        self.rect.x, self.rect.y = self.x, self.y
        screen.blit(self.sprite, self.rect)
        text_left_y = 100
        text_right_y = 100
        for text in self.text_left:
            label = Label(100, text_left_y, 500, 50, text, font_color='#000000', sprite='empty.png')
            label.draw(screen)
            text_left_y += 50
        for text in self.text_right:
            label = Label(680, text_right_y, 500, 50, text, font_color='#000000', sprite='empty.png')
            label.draw(screen)
            text_right_y += 50
        for button in self.buttons:
            button.update(screen)
    
    def addspell(self, spell):
        if spell not in self.sp_list:
            self.sp_list.append(spell)
            self.current_sp = spell
            self.buttons.append(Button(self.buttons_x, self.buttons_y, 58, 50, f'{spell}_icon.png', 'medieval', 23, attributes=[spell], onclickFunction=self.update_spell))
            self.buttons_y += 100

    def update_spell(self, 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)
    
    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
            touches = pg.sprite.spritecollideany(self, obj[4])
            if touches is not None and not isinstance(touches, Weapons):
                if isinstance(touches, Obstacle):
                    if not touches.collision:
                        return
                    if touches.type == 'wall':
                        if touches.name == 'wall_l':
                            self.x += (2 + (self.x - touches.rect.x))
                        elif touches.name == 'wall_r':
                            self.x -= (2 + self.rect.width - (touches.rect.x - self.x))
                        if touches.name == 'wall_t':
                            self.y += (2 + (self.y - touches.rect.y))
                        elif touches.name == 'wall_b':
                            self.y -= (2 + self.rect.height - (touches.rect.y - self.y))

                    if self.x <= touches.rect.x: self.x -= (self.rect.width - (touches.rect.x - self.x))
                    elif self.x > touches.rect.x: self.x += (self.rect.width - (self.x - touches.rect.x - touches.rect.width * 0.66))
        else:
            self.attack(moveto, obj)
    
    
    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 Skeleton(Mobs):
    def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite = 'people/skeleton.png', drops=0) -> None:
        super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops)
    
    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()
    

class Zombie(Mobs):
    def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite='people/zombie.png', drops=0) -> None:
        super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops)
    
    
    def attack(self, moveto, obj):
        if self.lastAttack + self.attack_speed * 1000 < pg.time.get_ticks():
            obj[3].append(Punch('punch', 100, self.x, self.y, moveto, self.damage))
            self.lastAttack = pg.time.get_ticks()

class Weapons(Objects):
    def __init__(self, name, ms, sprite, x, y, moveto, damage, life_ticks) -> None:
        super().__init__(name, ms, sprite, x, y)
        self.moveto = moveto
        self.damage = damage
        self.life_ticks= life_ticks
        self.spawn_tick = pg.time.get_ticks()
        pos = vec(1,0)
        angle = pos.angle_to(moveto)
        
        self.sprite = pg.transform.rotate(self.sprite, -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
            if self in objects[3]:
                objects[3].remove(self)
            
    def move(self, objects):
        self.moveto.scale_to_length(self.speed)
        self.x += self.moveto[0] / fps
        self.y += self.moveto[1] / fps
        if pg.time.get_ticks() - self.spawn_tick > self.life_ticks:
            self.hidden = True
            objects[3].remove(self)

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

class Fireball(Spells):
    def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/fireball.png', life_ticks=5000) -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks)
    
    def update(self, objects):
        self.move(objects)
        self.die(objects, Mobs)

class Windslash(Spells):
    def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/windslash.png', life_ticks=700) -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks)
    
    def update(self, objects):
        self.move(objects)
        self.die(objects, Mobs)
    
    def move(self, objects):
        super().move(objects)
        self.moveto = self.moveto.rotate(5)


class Arrow(Weapons):
    def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/arrow.png', life_ticks=5000) -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks)
    
    def update(self, objects):
        self.move(objects)
        self.die(objects, MainCharacter)


class Punch(Weapons):
    def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/empty.png', life_ticks=100) -> None:
        super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks)
    
    def update(self, objects):
        self.move(objects)
        self.die(objects, MainCharacter)
