import pygame
import random
import math
import time
import sys

# --- Configuration ---
SCREEN_W = 800
SCREEN_H = 600
FPS = 60

PLAYER_SPEED = 160.0
ENEMY_SPEED = 110.0
BULLET_SPEED = 360.0
BULLET_LIFETIME = 2.0

SPAWN_ENEMIES = 6

# Regeneration parameters
REGEN_RATE = 12.0    # HP per second for player
REGEN_DELAY = 3.0    # seconds after taking damage before regen starts
MAX_HP = 100

# Colors
WHITE = (255,255,255)
BLACK = (0,0,0)
GRAY = (120,120,120)
RED = (200,40,40)
GREEN = (40,200,40)
BLUE = (50,50,200)
YELLOW = (220,200,40)

# -----------------------

pygame.init()
screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 20)


def clamp(v, a, b):
    return max(a, min(b, v))


def vec_len(v):
    return math.hypot(v[0], v[1])


def normalize(v):
    l = vec_len(v)
    if l == 0: 
        return (0,0)
    return (v[0]/l, v[1]/l)


class Bullet:
    def __init__(self, pos, vel, owner):
        self.pos = list(pos)
        self.vel = list(vel)
        self.owner = owner
        self.life = BULLET_LIFETIME
        self.radius = 3

    def update(self, dt, world):
        self.pos[0] += self.vel[0] * dt
        self.pos[1] += self.vel[1] * dt
        self.life -= dt

        # collide with walls
        if self.pos[0] < 0 or self.pos[0] > SCREEN_W or self.pos[1] < 0 or self.pos[1] > SCREEN_H:
            self.life = 0

        # collide with tanks
        for t in world.tanks:
            if t is self.owner or not t.alive:
                continue
            dx = t.pos[0] - self.pos[0]
            dy = t.pos[1] - self.pos[1]
            if dx*dx + dy*dy <= (t.radius + self.radius)**2:
                t.take_damage(20)
                self.life = 0
                break

    def draw(self, surf):
        pygame.draw.circle(surf, YELLOW, (int(self.pos[0]), int(self.pos[1])), self.radius)


class Tank:
    def __init__(self, pos, color, is_player=False):
        self.pos = list(pos)
        self.color = color
        self.angle = 0.0
        self.turret_angle = 0.0
        self.radius = 14
        self.speed = PLAYER_SPEED if is_player else ENEMY_SPEED
        self.is_player = is_player
        self.hp = MAX_HP
        self.max_hp = MAX_HP
        self.fire_cooldown = 0.0
        self.respawn_time = 0.0
        self.alive = True

        # AI state
        self.target = None
        self.idle_timer = 0.0
        self.path_target = None

        # Regeneration state
        self.last_damaged_time = -9999.0
        self.regen_rate = REGEN_RATE if is_player else 0.0
        self.regen_delay = REGEN_DELAY

    def rect(self):
        return pygame.Rect(self.pos[0]-self.radius, self.pos[1]-self.radius,
                           self.radius*2, self.radius*2)

    def take_damage(self, dmg):
        if not self.alive:
            return
        self.hp -= dmg
        self.last_damaged_time = time.time()
        # print("Tank took dmg, hp:", self.hp)
        if self.hp <= 0:
            self.alive = False
            self.respawn_time = 3.0 + random.random()*3.0

    def update(self, dt, world):
        if not self.alive:
            self.respawn_time -= dt
            if self.respawn_time <= 0:
                self.alive = True
                self.hp = self.max_hp
                self.last_damaged_time = -9999.0
            return

        # cooldown
        if self.fire_cooldown > 0:
            self.fire_cooldown -= dt

        # Regeneration (if configured)
        if self.alive and self.regen_rate > 0 and self.hp < self.max_hp:
            nowt = time.time()
            if nowt - self.last_damaged_time >= self.regen_delay:
                self.hp += self.regen_rate * dt
                if self.hp > self.max_hp:
                    self.hp = self.max_hp

        if self.is_player:
            # player input movement handled elsewhere
            return

        # Simple enemy AI: wander and aim at player if nearby
        player = world.player
        dx = player.pos[0] - self.pos[0]
        dy = player.pos[1] - self.pos[1]
        dist = math.hypot(dx, dy)

        if dist < 260:
            # move toward player if not too close
            if dist > 80:
                ndx, ndy = dx/dist, dy/dist
                self.pos[0] += ndx * self.speed * dt
                self.pos[1] += ndy * self.speed * dt
            # aim turret at player
            self.turret_angle = math.atan2(dy, dx)
            # shoot sometimes
            if self.fire_cooldown <= 0 and dist < 300:
                self.fire(world)
                self.fire_cooldown = 1.0 + random.random()*1.5
        else:
            # wander randomly
            self.idle_timer -= dt
            if self.idle_timer <= 0:
                self.idle_timer = 1.0 + random.random()*2.0
                ang = random.random()*math.pi*2
                self.wander_dir = (math.cos(ang), math.sin(ang))
            self.pos[0] += self.wander_dir[0] * self.speed * dt * 0.6
            self.pos[1] += self.wander_dir[1] * self.speed * dt * 0.6
            self.turret_angle += (random.random()-0.5)*0.1

        # clamp to screen
        self.pos[0] = clamp(self.pos[0], self.radius, SCREEN_W - self.radius)
        self.pos[1] = clamp(self.pos[1], self.radius, SCREEN_H - self.radius)

    def fire(self, world):
        if not self.alive:
            return
        ang = self.turret_angle
        vx = math.cos(ang) * BULLET_SPEED
        vy = math.sin(ang) * BULLET_SPEED
        bpos = (self.pos[0] + math.cos(ang)*(self.radius+6),
                self.pos[1] + math.sin(ang)*(self.radius+6))
        bullet = Bullet(bpos, (vx, vy), self)
        world.bullets.append(bullet)

    def draw(self, surf):
        if not self.alive:
            # draw respawn indicator
            return
        # body
        pygame.draw.circle(surf, self.color, (int(self.pos[0]), int(self.pos[1])), self.radius)
        # turret
        tx = self.pos[0] + math.cos(self.turret_angle)*(self.radius+8)
        ty = self.pos[1] + math.sin(self.turret_angle)*(self.radius+8)
        pygame.draw.line(surf, BLACK, (int(self.pos[0]), int(self.pos[1])), (int(tx), int(ty)), 4)
        # hp bar
        hpw = int((self.hp/self.max_hp) * (self.radius*2))
        pygame.draw.rect(surf, RED, (int(self.pos[0]-self.radius), int(self.pos[1]-self.radius-8), self.radius*2, 6))
        pygame.draw.rect(surf, GREEN, (int(self.pos[0]-self.radius), int(self.pos[1]-self.radius-8), hpw, 6))


class World:
    def __init__(self):
        self.player = Tank((SCREEN_W/2, SCREEN_H/2), BLUE, is_player=True)
        # ensure player regen settings (already set in Tank init for player)
        self.player.regen_rate = REGEN_RATE
        self.player.regen_delay = REGEN_DELAY

        self.tanks = []
        for i in range(SPAWN_ENEMIES):
            p = (random.randint(40, SCREEN_W-40), random.randint(40, SCREEN_H-40))
            t = Tank(p, RED, is_player=False)
            self.tanks.append(t)

        self.bullets = []

    def update(self, dt, keys):
        # player movement and aiming
        if self.player.alive:
            mvx = 0.0
            mvy = 0.0
            if keys[pygame.K_w] or keys[pygame.K_UP]:
                mvy -= 1
            if keys[pygame.K_s] or keys[pygame.K_DOWN]:
                mvy += 1
            if keys[pygame.K_a] or keys[pygame.K_LEFT]:
                mvx -= 1
            if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
                mvx += 1
            if mvx != 0 or mvy != 0:
                nv = normalize((mvx, mvy))
                self.player.pos[0] += nv[0] * self.player.speed * dt
                self.player.pos[1] += nv[1] * self.player.speed * dt
                self.player.angle = math.atan2(nv[1], nv[0])

            mx, my = pygame.mouse.get_pos()
            dx = mx - self.player.pos[0]
            dy = my - self.player.pos[1]
            self.player.turret_angle = math.atan2(dy, dx)

        # update tanks
        for t in self.tanks:
            t.update(dt, self)
        self.player.update(dt, self)

        # update bullets
        for b in list(self.bullets):
            b.update(dt, self)
            if b.life <= 0:
                try:
                    self.bullets.remove(b)
                except ValueError:
                    pass

    def draw(self, surf):
        surf.fill(GRAY)
        # draw bullets
        for b in self.bullets:
            b.draw(surf)
        # draw tanks
        for t in self.tanks:
            t.draw(surf)
        self.player.draw(surf)
        # HUD
        txt = font.render("HP: %d / %d" % (int(self.player.hp), self.player.max_hp), True, WHITE)
        surf.blit(txt, (10, 8))
        txt2 = font.render("Regen: %.1f hp/s after %.1fs" % (self.player.regen_rate, self.player.regen_delay), True, WHITE)
        surf.blit(txt2, (10, 28))


def main():
    world = World()
    running = True
    while running:
        dt = clock.tick(FPS) / 1000.0
        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                running = False
            elif ev.type == pygame.MOUSEBUTTONDOWN:
                if ev.button == 1:
                    # left click fire
                    if world.player.alive and world.player.fire_cooldown <= 0:
                        world.player.fire(world)
                        world.player.fire_cooldown = 0.25

        keys = pygame.key.get_pressed()
        world.update(dt, keys)
        world.draw(screen)
        pygame.display.flip()

    pygame.quit()
    sys.exit()


if __name__ == '__main__':
    main()