import os
from pygame.sprite import Sprite
import pygame
import math
import random
import time

from utils import load_entity_image
from projectile import Projectile

class Tank(Sprite):
    MOVE_UP = "up"
    MOVE_DOWN = "down"
    MOVE_LEFT = "left"
    MOVE_RIGHT = "right"

    color = "blue"

    def __init__(self, settings, x: int, y: int):
        super().__init__()
        self.settings = settings
        
        image_folder = os.path.join(self.settings.img_folder, "tanks", self.color)
        self.IMAGES = {
            self.MOVE_UP: (load_entity_image(self.settings, os.path.join(image_folder, "tile004.png")), load_entity_image(self.settings, os.path.join(image_folder, "tile006.png"))),
            self.MOVE_DOWN: (load_entity_image(self.settings, os.path.join(image_folder, "tile010.png")), load_entity_image(self.settings, os.path.join(image_folder, "tile008.png"))),
            self.MOVE_LEFT: (load_entity_image(self.settings, os.path.join(image_folder, "tile012.png")), load_entity_image(self.settings, os.path.join(image_folder, "tile014.png"))),
            self.MOVE_RIGHT: (load_entity_image(self.settings, os.path.join(image_folder, "tile000.png")), load_entity_image(self.settings, os.path.join(image_folder, "tile002.png")))
        }

        self.image = self.IMAGES[self.MOVE_UP][0]
        self.rect = self.image.get_rect()

        self.now_move = self.MOVE_UP
        self.now_frame = 0
        self.move_stage = 0
        self.animation_tick_change = 3
        self.frame_quantity = 2
        self.animation_max_tick = self.animation_tick_change * self.frame_quantity

        self.x = x
        self.y = y
        self.rect.x = self.x
        self.rect.y = self.y

        self.last_shot = time.time()
    
    
    
    def update_move_state(self, triggered: str):
        if triggered == self.now_move:
            self.move_stage += 1
            
            self.now_frame = math.floor(self.move_stage / self.animation_tick_change)

            if self.now_frame > self.frame_quantity - 1:
                self.move_stage = 0
                self.now_frame = 0
        else:
            self.now_move = triggered
            self.move_stage = 0

        del self.rect
        self.image = self.IMAGES[self.now_move][self.now_frame]
        self.rect = self.image.get_rect()
        self.rect.y = self.y
        self.rect.x = self.x

    
    def update(self):
        keystate = pygame.key.get_pressed()

        if keystate[pygame.K_w]:
            self.move_up()
        elif keystate[pygame.K_a]:
            self.move_left()
        elif keystate[pygame.K_s]:
            self.move_down()
        elif keystate[pygame.K_d]:
            self.move_right()
    
    def get_rect(self):
        return pygame.Rect(self.x, self.y, self.settings.entity_size, self.settings.entity_size)
    
    def create_new_projectile(self):
        if self.now_move == Tank.MOVE_UP:
            sx = self.x + self.settings.block_size / 2 - self.settings.projectile_size / 2 + 1
            sy = self.y - self.settings.projectile_size / 2 - 1
        elif self.now_move == Tank.MOVE_DOWN:
            sx = self.x + self.settings.block_size / 2 - self.settings.projectile_size / 2 + 1
            sy = self.y + self.settings.block_size + 1
        elif self.now_move == Tank.MOVE_LEFT:
            sy = self.y + self.settings.block_size / 2 - self.settings.projectile_size / 2 + 1
            sx = self.x - self.settings.projectile_size / 2 - 1
        elif self.now_move == Tank.MOVE_RIGHT:
            sy = self.y + self.settings.block_size / 2 - self.settings.projectile_size / 2 + 1
            sx = self.x + self.settings.block_size + 1

        self.last_shot = time.time()
        self.game.projectiles.add(Projectile(self.game, sx, sy, self.now_move))


class Player(Tank):
    color = "blue"

    def __init__(self, game, x, y):
        self.game = game
        super().__init__(self.game.settings, x, y)
    
    def move_up(self):
        self.update_move_state(self.MOVE_UP)

    def move_down(self):
        self.update_move_state(self.MOVE_DOWN)

    def move_left(self):
        self.update_move_state(self.MOVE_LEFT)

    def move_right(self):
        self.update_move_state(self.MOVE_RIGHT)
    
    def update(self):
        keystate = pygame.key.get_pressed()

        if keystate[pygame.K_SPACE]:
            if time.time() - self.last_shot > self.settings.recharge_time:
                self.create_new_projectile()
        
        self.game.screen.blit(self.image, (self.x, self.y))
    
    


class Enemy(Tank):
    color = "red"
    last_movement = Tank.MOVE_UP
    prev_variants = set()
    prev_prev_variants = set()
    could_move = True

    def __init__(self, game, x, y):
        super().__init__(game.settings, x, y)
        self.game = game
        self.settings = game.settings
        self.map = game.map
    
    def update(self):
        tsh = self.map.settings.block_size // 2 #tile size half
        block = self.map.find_block_on_coords(self.x + tsh, self.y + tsh)
        i, j = block.i, block.j

        if abs(self.x - block.x) < 0.6 and abs(self.y - block.y) < 0.6:
            #print(block)

            variants = self.get_possible_movements(i, j)

            if variants == self.prev_variants or variants == self.prev_variants:
                next_movement = self.last_movement
            else:
                next_movement = random.choice(list(variants))
                self.last_movement = next_movement
                self.prev_prev_variants = self.prev_variants
                self.prev_variants = variants

            #print(variants)
        else:
            if self.could_move:
                next_movement = self.last_movement
            else:
                variants = self.get_possible_movements(i, j)
                next_movement = random.choice(list(variants))
                self.last_movement = next_movement
                self.prev_variants = variants
        
        #Shot
        if random.randint(1, self.settings.enemy_shot_rev) == 1:
            self.create_new_projectile()
        

        if next_movement == Tank.MOVE_UP:
            self.move_up()
        elif next_movement == Tank.MOVE_LEFT:
            self.move_left()
        elif next_movement == Tank.MOVE_DOWN:
            self.move_down()
        elif next_movement == Tank.MOVE_RIGHT:
            self.move_right()
    
    def get_possible_movements(self, i, j):
        variants = set()
        if self.map.blockmap[i - 1][j].passable:
            variants.add(Tank.MOVE_UP)

        if self.map.blockmap[i + 1][j].passable:
            variants.add(Tank.MOVE_DOWN)

        if self.map.blockmap[i][j + 1].passable:
            variants.add(Tank.MOVE_RIGHT)

        if self.map.blockmap[i][j - 1].passable:
            variants.add(Tank.MOVE_LEFT)
        
        return variants
    
    def change_coords_by_delta(self, dx=None, dy=None):
        #print(dx, dy)
        self.x = self.x - dx
        self.y = self.y - dy
        self.rect.x = self.x
        self.rect.y = self.y
    
    def move_up(self):
        cx, cy, tsh = self.get_block_collision_vars()
        cp1 = (cx - tsh + 1, cy - tsh)
        cp2 = (cx + tsh - 1, cy - tsh)

        if self.check_on_moveability(cp1[0], cp1[1], 0, self.settings.move_speed) and self.check_on_moveability(cp2[0], cp2[1], 0, self.settings.move_speed):
            self.y -= self.settings.move_speed
            self.rect.y = self.y
            self.update_move_state(self.MOVE_UP)
            self.could_move = True
        else:
            self.could_move = False

    def move_down(self):
        cx, cy, tsh = self.get_block_collision_vars()
        cp1 = (cx - tsh + 1, cy + tsh)
        cp2 = (cx + tsh - 1, cy + tsh)

        if self.check_on_moveability(cp1[0], cp1[1], 0, -self.settings.move_speed) and self.check_on_moveability(cp2[0], cp2[1], 0, -self.settings.move_speed):
            self.y += self.settings.move_speed
            self.rect.y = self.y
            self.update_move_state(self.MOVE_DOWN)
            self.could_move = True
        else:
            self.could_move = False

    def move_left(self):
        cx, cy, tsh = self.get_block_collision_vars()
        cp1 = (cx - tsh, cy - tsh + 1)
        cp2 = (cx - tsh, cy + tsh - 1)

        if self.check_on_moveability(cp1[0], cp1[1], self.settings.move_speed, 0) and self.check_on_moveability(cp2[0], cp2[1], -self.settings.move_speed, 0):
            self.x -= self.settings.move_speed
            self.rect.x = self.x
            self.update_move_state(self.MOVE_LEFT)
            self.could_move = True
        else:
            self.could_move = False

    def move_right(self):
        cx, cy, tsh = self.get_block_collision_vars()
        cp1 = (cx + tsh, cy - tsh + 1)
        cp2 = (cx + tsh, cy + tsh - 1)

        if self.check_on_moveability(cp1[0], cp1[1], -self.settings.move_speed, 0) and self.check_on_moveability(cp2[0], cp2[1], -self.settings.move_speed, 0):
            self.x += self.settings.move_speed
            self.rect.x = self.x
            self.update_move_state(self.MOVE_RIGHT)
            self.could_move = True
        else:
            self.could_move = False

    def get_block_collision_vars(self):
        tsh = self.map.settings.block_size // 2
        return self.x + tsh, self.y + tsh, tsh

    def check_on_moveability(self, x, y, dx, dy):
        return self.map.check_on_moveability(x, y, dx, dy)