import {Entity} from './Entity.ts';
import type {CollisionEntity} from '../collision/CollisionSystem.ts';
import {TILE_SIZE} from '../TileMap.ts';
import type {ZombieAttackGame} from '../ZombieAttackGame.ts';
import {RGBA} from '@opentui/core';
import type {PixelCanvas} from '../drawing/pixelCanvas.ts';

export abstract class AnimatedCharacter extends Entity implements CollisionEntity {
  // Collision properties
  collisionOffsetX: number = 2; // Smaller collision box than sprite
  collisionOffsetY: number = 4; // Top offset for character sprites
  collisionWidth: number = TILE_SIZE - 4; // Slightly smaller than tile
  collisionHeight: number = TILE_SIZE - 4; // Slightly smaller than tile
  collisionType?: 'player' | 'enemy' | 'projectile' | 'solid';
  solid: boolean = true; // Characters block movement by default

  private lastMoveTime: number = 0;
  private isMoving: boolean = false;
  private lastX: number = 0;
  private lastY: number = 0;
  private animationTime: number = 0;
  private readonly BOB_FREQUENCY: number = 5; // 5 times per second

  constructor(game: ZombieAttackGame, tileName: string) {
    super(game);
    this.x = 50 * TILE_SIZE;
    this.y = 50 * TILE_SIZE;
    this.width = TILE_SIZE;
    this.height = TILE_SIZE;
    this.tileName = tileName;
    this.layer = 1;
    this.facingLeft = false;
    this.verticalAnimationOffset = 0;
    this.lastX = this.x;
    this.lastY = this.y;
  }

  updateAnimation(deltaTime: number): void {
    const hasMovedX = this.x !== this.lastX;
    const hasMovedY = this.y !== this.lastY;

    if (hasMovedX || hasMovedY) {
      this.isMoving = true;
      this.lastMoveTime = 0; // Reset move timer
      this.lastX = this.x;
      this.lastY = this.y;
    } else {
      this.lastMoveTime += deltaTime;
      if (this.lastMoveTime > 100) {
        this.isMoving = false;
        this.verticalAnimationOffset = 0;
        return;
      }
    }

    // Update animation based on delta time
    if (this.isMoving) {
      this.animationTime += deltaTime;

      // Calculate bobbing offset using sine wave at specified frequency
      const cycleTime = 1000 / this.BOB_FREQUENCY; // Time for one complete cycle in ms
      const phase = (this.animationTime % cycleTime) / cycleTime; // 0 to 1

      // Use sine wave for smooth bobbing motion
      this.verticalAnimationOffset = Math.round(Math.sin(phase * Math.PI * 2) * 0.5 + 0.5);
    }
  }

  move(dx: number, dy: number): void {
    // Move in pixels
    this.x += dx;
    this.y += dy;
    if (dx < 0) {
      this.facingLeft = true;
    } else if (dx > 0) {
      this.facingLeft = false;
    }
  }

  /**
   * Render an animated sprite with bobbing effect
   */
  protected renderAnimatedSprite(
    pixelBuffer: PixelCanvas,
    tileName: string,
    flipHorizontal: boolean = false,
    animationOffset: number = 0,
  ): void {
    const pixels = this.game.tileMap.getTilePixels(tileName);
    if (!pixels) {
      console.warn(`Tile "${tileName}" not found in tile map.`);
      return;
    }

    // Render each pixel from the tile to the pixel buffer
    for (let py = 0; py < TILE_SIZE; py++) {
      for (let px = 0; px < TILE_SIZE; px++) {
        // Apply animation offset for bobbing effect (only for top half of sprite)
        let sourceY = py;
        if (py < 14 && animationOffset > 0) {
          sourceY = py - 1;
          if (sourceY < 0) continue; // Skip pixels that would be above the sprite
        }
        const sourceX = flipHorizontal ? TILE_SIZE - 1 - px : px;
        const pixelIndex = sourceY * TILE_SIZE + sourceX;
        const color = pixels[pixelIndex] || RGBA.fromValues(0, 0, 0, 0);

        if (color.a > 0) {
          // Set pixel at the target position
          pixelBuffer.setPixel(px, py, color);
        }
      }
    }
  }
}
