import random
import pygame
import math

from scripts.particle import Particle
from scripts.sparks import Spark

class PhysicsEntity:
    def __init__(self, game, e_type, pos, size):
        self.game = game
        self.type = e_type
        self.pos = list(pos)
        self.size = size
        self.velocity = [0, 0]
        self.collisions = {'up': False, 'down': False, 'right': False, 'left': False}
        self.hp = 3

        self.last_movement = [0,0]

        self.assets = game.assets
        self.can_control = True
        self.action = ''
        self.anim_offset = (-3,-3)
        self.flip = False
        self.set_action("idle")
    
    def rect(self):
        return pygame.Rect(self.pos[0] , self.pos[1], self.size[0], self.size[1])

    def set_action(self,action):
        if action != self.action: 
            self.action = action
            self.animation = self.assets[self.type+'/'+self.action].copy()

    def update(self, tilemap, movement=(0, 0)):
        self.collisions = {'up': False, 'down': False, 'right': False, 'left': False}
        
        frame_movement = ((movement[0] ) + self.velocity[0], (movement[1] ) + self.velocity[1])
        
        self.pos[0] += frame_movement[0]
        entity_rect = self.rect()
        for rect in tilemap.physics_rects_around(self.pos):
            if entity_rect.colliderect(rect):
                if frame_movement[0] > 0:
                    entity_rect.right = rect.left
                    self.collisions['right'] = True
                if frame_movement[0] < 0:
                    entity_rect.left = rect.right
                    self.collisions['left'] = True
                self.pos[0] = entity_rect.x
        
        self.pos[1] += frame_movement[1]
        entity_rect = self.rect()
        for rect in tilemap.physics_rects_around(self.pos):
            if entity_rect.colliderect(rect):
                if frame_movement[1] > 0:
                    entity_rect.bottom = rect.top
                    self.collisions['down'] = True
                if frame_movement[1] < 0:
                    entity_rect.top = rect.bottom
                    self.collisions['up'] = True
                self.pos[1] = entity_rect.y
        
        if movement[0] > 0:
            self.flip = False
        elif movement[0] < 0:
            self.flip = True

        self.velocity[1] = min(5, self.velocity[1] + 0.1)
        
        self.last_movement = movement

        if self.collisions['down'] or self.collisions['up']:
            self.velocity[1] = 0
        
        self.animation.update()
        
    def render(self, surf,offset = [0,0]):
        surf.blit(pygame.transform.flip(self.animation.img(),self.flip,False), (self.pos[0] - offset[0] - 2,self.pos[1]-offset[1]))
        #surf.blit(self.game.assets['player'], (self.pos[0] - offset[0],self.pos[1]-offset[1]))

class Enemy(PhysicsEntity):
    def __init__(self, game,  pos, size):
        super().__init__(game, 'enemy', pos, size)
        
        self.hp = 2
        self.walking = 0
        self.can_hurt = True
        self.hurt_timer = 0
        
    def update(self,tilemap,movement=(0,0)):
        if self.walking:
            if tilemap.solid_check ((self.rect().centerx + (-7 if self.flip else 7),self.pos[1]+23)):
                movement = (movement[0] - 0.5  if self.flip else 0.5,movement[1])
            else:
                self.flip = not self.flip
            
            if (self.collisions['right'] or self.collisions['left']):
                self.flip = not self.flip

            self.walking = max(0,self.walking - 1)

            if not self.walking:
                dis = (self.game.player.pos[0] - self.pos[0],self.game.player.pos[1] - self.pos[1])
                if (abs(dis[1]) < 16):
                    if ( self.flip and dis[0]<0 ):
                        self.game.E_projectiles.append([[self.rect().centerx-7,self.rect().centery],-1.5,0])
                        for i in range(4):
                            self.game.sparks.append(Spark(self.game.E_projectiles[-1][0],random.random()-0.5+math.pi,2+random.random()))
                    if not self.flip and dis[0] > 0:
                        self.game.E_projectiles.append([[self.rect().centerx+7,self.rect().centery],1.5,0])
                        self.game.sparks.append(Spark(self.game.E_projectiles[-1][0],random.random()-0.5,2+random.random()))
        elif random.random() < 0.01:
            self.walking = random.randint(30,120)

        super().update(tilemap=tilemap,movement=movement)

        if movement[0] != 0:
            self.set_action('run')
        else:
            self.set_action('idle')
        
        if not self.can_hurt:
            self.hurt_timer += 1
            if self.hurt_timer == 40:
                self.hurt_timer = 0
                self.can_hurt  =True

        if abs(self.game.player.dashing) >= 50:

            if self.rect().colliderect(self.game.player.rect()) and self.can_hurt:
                self.game.screen_shake = 20
                self.can_hurt = False
                self.hp -= 2
    
    def render(self,surf,offset=(0,0)):
        super().render(surf, offset)
        if self.flip:
            surf.blit(pygame.transform.flip(self.game.assets['gun'],True,False),(self.rect().centerx + 8 - self.game.assets['gun'].get_width() - offset[0],self.rect().centery - offset[1] - 6))
        else:
            surf.blit(pygame.transform.flip(self.game.assets['gun'],False,False),(self.rect().centerx + 8 - self.game.assets['gun'].get_width() - offset[0],self.rect().centery - offset[1] - 6))
        

class Player(PhysicsEntity):
    def __init__(self, game, pos, size):
        super().__init__(game, 'player', pos, size)
        self.air_time = 0
        self.jumps = 1
        self.wall_slide = False
        self.can_control_timer = 0
        self.dashing = 0

    def update(self,tilemap,movement=(0,0)):
        if self.hp > 0:
            super().update(tilemap,movement=movement)

        if self.can_control == False:
            self.can_control_timer += 2
            if self.can_control_timer == 60:
                self.can_control = True

        self.air_time += 1
        if self.collisions['down']:
            self.air_time = 0
            self.jumps = 1
        
        self.wall_slide = False

        if (self.collisions['right'] or self.collisions['left']) and self.air_time > 4:
            self.wall_slide = True
            self.velocity[1] = min(self.velocity[1],0.5)
            if self.collisions['right']:
                self.flip = False
            else:
                self.flip = True
        
        if not self.wall_slide:
            if self.air_time > 7:
                self.jumps = 0
                self.set_action('jump')
            elif movement[0] != 0:
                self.set_action('run')
            else:
                self.set_action('idle')

        if abs(self.dashing) in {60, 50}:
                for i in range(20):
                    angle = random.random()*math.pi * 2
                    speed = random.random()*0.5+0.5
                    pvelocity = [math.cos(angle)*speed,math.sin(angle)*speed]
                    self.game.particles.append(Particle(self.game,'particle',self.rect().center,velocity=pvelocity,frame=random.randint(0,7)))

        if self.dashing > 0:
            self.dashing = max(0,self.dashing-1)
        elif self.dashing < 0:
            self.dashing = min(0,self.dashing+1)
        if abs(self.dashing) > 50:
            self.velocity[0] = abs(self.dashing)/self.dashing * 8
            if abs(self.dashing) == 51:
                self.velocity[0] *= 0.2

            pvelocity = [abs(self.dashing)/self.dashing * random.random() * 3,0]
            self.game.particles.append(Particle(self.game,'particle',self.rect().center,velocity=pvelocity,frame=random.randint(0,7)))

        if self.velocity[0] > 0:
            self.velocity[0] = max(self.velocity[0]-0.1,0)
        elif self.velocity[0] < 0:
            self.velocity[0] = min(self.velocity[0]+0.1,0)

    def render(self, surf, offset=[0, 0]):
       # super().render(surf, offset)
       if self.hp > 0:
        if abs(self.dashing) <= 50:
            super().render(surf, offset)
            if self.flip:
                surf.blit(pygame.transform.flip(self.game.assets['gun'],True,False),(self.rect().centerx + 8 - self.game.assets['gun'].get_width() - offset[0],self.rect().centery - offset[1] - 6))
            else:
                    surf.blit(pygame.transform.flip(self.game.assets['gun'],False,False),(self.rect().centerx + 8 - self.game.assets['gun'].get_width() - offset[0],self.rect().centery - offset[1] - 6))
            
    
    def jump(self):
        if self.wall_slide:
            if self.flip and self.last_movement[0] < 0:
                self.flip = False
                self.can_control = False
                self.velocity[0] = 3.5
                self.velocity[1] = -2
                self.air_time = 5
                self.jumps = max(0,self.jumps-1)
                return True
            elif not self.flip and self.last_movement[0] > 0:
                self.can_control = False
                self.flip = True
                self.velocity[0] = -3.5
                self.velocity[1] = -2
                self.air_time = 5
                self.jumps = max(0,self.jumps-1)
                return True
        elif self.jumps:
            self.jumps -= 1
            self.velocity[1] = -3
            self.air_time = 5
            return True
    
    def shoot(self):
        if ( self.flip ):
            self.game.P_projectiles.append([[self.rect().centerx-7,self.rect().centery],-1.5,0])
            for i in range(4):
                self.game.sparks.append(Spark(self.game.P_projectiles[-1][0],random.random()-0.5+math.pi,2+random.random()))
        if not self.flip:
            self.game.P_projectiles.append([[self.rect().centerx+7,self.rect().centery],1.5,0])
            for i in range(4):
                self.game.sparks.append(Spark(self.game.P_projectiles[-1][0],random.random()-0.5,2+random.random()))

    def dash(self):
        if not self.dashing:
            if self.flip:
                self.dashing = -60
            else:
                self.dashing = 60

class Brute(Enemy):
    def __init__(self, game, pos, size):
        super().__init__(game, pos, size)
        self.hp = 3
    
    def render(self,surf,offset = (0,0)):
        super().render(surf=surf,offset=offset)
        surf.blit(self.game.assets['Armour'],(self.pos[0]-offset[0] -2,self.pos[1]-offset[1]))
