import {
  _decorator,
  AssetManager,
  assetManager,
  color,
  Color,
  Component,
  instantiate,
  Label,
  Node,
  Prefab,
  RichText,
  Sprite,
  SpriteFrame,
  tween,
  v3,
  Vec2,
  Vec3,
  Widget,
} from "cc";
import { GameAudioMgr } from "./GameAudioMgr";
import { PlayerMovement2D } from "./PlayerMovement2D";
import { IGamePlayer } from "../../module_basic/shared/protocols/public/game/GameTypeDef";
import { CellBullet } from "./CellBullet";
import { GameMgr } from "../../module_basic/scripts/GameMgr";
import { CellMgr } from "./CellMgr";
import { UserMgr } from "../../module_basic/scripts/UserMgr";
import { TEAM_ID_MAP } from "../../module_basic/shared/types/TeamInfo";
import { ModuleDef } from "../../scripts/ModuleDef";
import { RoomMgr } from "../../module_basic/scripts/RoomMgr";
import { GameResMgr } from "./GameResMgr";
import { GenderIcon } from "../../module_basic/scripts/GenderIcon";
import { ExtraNode, NodeUtils } from "../../module_basic/scripts/NodeUtils";
import { SpriteUtils } from "../../module_basic/scripts/SpriteUtils";
import { utils } from "cc";
const { ccclass, property } = _decorator;

const tempV2 = new Vec2();

const tempV3 = new Vec3();

const tempColor = new Color();

@ccclass("Cell")
export class Cell extends Component {
  @property(Node) visualRoot: Node;
  @property(Node) body: Node;

  @property(Node) firePoint: Node;

  @property(Node) dir: Node;

  @property(Sprite) sprIcon: Sprite;

  @property(Sprite) sprEfx: Sprite;

  @property(Label) lblName: Label;

  @property(Label) lblTeam: Label;

  @property(Label) lblLocation: Label;

  @property(GenderIcon) genderIcon: GenderIcon;

  @property(Label) lblPos: Label;

  public player: IGamePlayer;
  public movement: PlayerMovement2D;

  private _transformsCache: number[][] = [];

  start() {
    this.movement = this.node.getComponent(PlayerMovement2D);
    CellMgr.inst.addCell(this);
  }

  setPlayer(player: IGamePlayer) {
    this.player = player;
    this.dir.active = player.uid == UserMgr.inst.uid;
    this.setRadius(player.radius);

    this.lblName.string = player.roleName;

    let teamName = TEAM_ID_MAP[player.teamId].name;

    this.lblTeam.string = `[${teamName}]`;

    if (GameMgr.inst.selfPlayer.teamId == player.teamId) {
      tempColor.fromHEX("#00EF00");
      this.lblTeam.color = tempColor;
      tempColor.fromHEX("#000000");
      this.lblTeam.outlineColor = tempColor;
    } else {
      tempColor.fromHEX("#FF0000");
      this.lblTeam.color = tempColor;
      tempColor.fromHEX("#FFFFFF");
      this.lblTeam.outlineColor = tempColor;
    }

    this.setColor(player.color);
    this.setState("normal");
    this.lblLocation.string = player.location || "保密";
    this.lblLocation.updateRenderData(true);
    this.genderIcon.setGender(RoomMgr.inst.getUser(player.uid)?.gender);
    this.lblLocation.node.children.forEach((v) => {
      v.getComponent(Widget).updateAlignment();
    });

    SpriteUtils.setTeamSkin(this.sprIcon, player.teamId);
    this.setColor(0xffffffff);
  }

  setState(state: "normal" | "destroyed") {
    if (state == "normal") {
      this.visualRoot.active = true;
    } else if (state == "destroyed") {
      this.visualRoot.active = false;
    }
  }

  displayPos() {
    let scale = this.body.scale.x;
    this.lblPos.string = `${this.node.worldPosition.x.toFixed(
      0
    )},${this.node.worldPosition.y.toFixed(0)}\n
    scale:${scale.toFixed(1)}
    radius:${this.player.radius.toFixed(0)}
    `;
  }

  private _scaleAnimDoing = false;
  setRadius(radius: number) {
    if (radius > 2000) {
      return;
    }
    this.player.radius = radius;

    let self = this;
    if (this._scaleAnimDoing) {
      return;
    }
    this._scaleAnimDoing = true;
    let scale = (radius * 2) / 150;
    // this.body.setScale(scale, scale, 1.0);
    this.lblLocation.node.active = radius > 75;

    tween(this.body)
      //   .to(0.1, { scale: new Vec3(scale * 1.05, scale * 1.05, 1.0) })
      .to(
        0.2,
        { scale: new Vec3(scale, scale, 1.0) },
        {
          onComplete: () => {
            (self.node as ExtraNode).__radius__ = radius;

            self._scaleAnimDoing = false;
          },
        }
      )
      .start();
  }

  setColor(colorVal: number) {
    Color.fromUint32(tempColor, colorVal | 0xff000000);
    this.sprIcon.color = tempColor;
  }

  protected onDestroy(): void {
    CellMgr.inst.removeCell(this);
  }

  shootAnim(cb?: Function) {
    let t = GameMgr.inst.selfPlayer.transform;
    tempV3.set(t[0], t[1], t[2]);
    let volume = GameAudioMgr.getVolumeByDist(this.node.worldPosition, tempV3);
    GameAudioMgr.playOneShot("sounds/sfx_shoot", volume);

    //animation
  }

  fireBullet(
    pos: { x: number; y: number; z: number },
    rotationDegrees: number
  ) {
    let bullet = instantiate(GameResMgr.inst.bulletPrefab);
    this.node.parent.addChild(bullet);
    bullet.setWorldPosition(pos.x, pos.y, pos.z);
    bullet.setRotationFromEuler(0, 0, rotationDegrees);

    let rotationAngle = (rotationDegrees / 180) * Math.PI;
    let dir = tempV2.set(Math.cos(rotationAngle), Math.sin(rotationAngle));
    bullet.getComponent(CellBullet).moveDir = dir;
    return bullet;
  }

  private computeTargetDist(x1: number, y1: number, x2: number, y2: number) {
    let dx = x1 - x2;
    let dy = y1 - y2;
    let dist = Math.sqrt(dx * dx + dy * dy);
    return dist;
  }
  syncTransform(trans: number[], forceSync: boolean) {
    if (forceSync) {
      this._transformsCache = [];
      this.node.setWorldPosition(trans[0], trans[1], trans[2]);
      if (this.movement) {
        this.movement.onMovement(trans[3], 1.0);
      }
    } else {
      this._transformsCache.push(trans);
      let dist = 0;
      let pos = this.node.worldPosition;
      let lastX = pos.x;
      let lastY = pos.y;
      this._transformsCache.forEach((v) => {
        dist += this.computeTargetDist(lastX, lastY, v[0], v[1]);
        lastX = v[0];
        lastY = v[1];
      });
      //distance is too far
      if (dist >= this.player.speed) {
        this._transformsCache = [];
        this.node.setWorldPosition(trans[0], trans[1], trans[2]);
        console.log("distance is too far,force sync transform");
      }
    }
  }

  protected update(dt: number): void {
    if (this.player.protectedTime > 0) {
      this.sprEfx.node.active = true;
      tempColor.set(this.sprEfx.color);
      //clamp to 0.2 ~ 0.8
      tempColor.a = 255 * (Math.sin(Date.now() / 50 / Math.PI) * 0.6 + 0.2);
      this.sprEfx.color = tempColor;
    } else {
      this.sprEfx.node.active = false;
    }
  }

  private _lastRotation = NaN;
  private _lastSyncTime = 0;
  protected lateUpdate(dt: number): void {
    if (GameMgr.inst.gameData && GameMgr.inst.gameData.gameState != "playing") {
      return;
    }

    if (this.player.playerId == GameMgr.inst.selfPlayer.playerId) {
      this.lateUpdate_SelfPlayer(dt);
    } else {
      this.lateUpdate_OtherPlayers(dt);
    }

    this.displayPos();
  }

  lateUpdate_SelfPlayer(dt: number) {
    if (this.movement?.getRealSpeed() > 0) {
      let pos = this.node.worldPosition;
      this.dir.setWorldRotationFromEuler(0, 0, this.movement.realDegree);
      let rotation = this.movement.rotDegree;
      if (
        rotation != this._lastRotation ||
        Date.now() - this._lastSyncTime > 2000
      ) {
        this._lastRotation = rotation;
        this._lastSyncTime = Date.now();
        //console.log(rotation);
        GameMgr.inst.sendMsg_CellDataChange(pos.x, pos.y, pos.z, rotation);
      }
    }
  }

  lateUpdate_OtherPlayers(dt: number) {
    if (this.player.state != "moving") {
      return;
    }

    if (this._transformsCache.length == 0) {
      let rotation = (this.player.transform[3] / 180) * Math.PI;
      let dirX = Math.cos(rotation);
      let dirY = Math.sin(rotation);
      let len = Math.sqrt(dirX * dirX + dirY * dirY);
      if (len > 0) {
        dirX /= len;
        dirY /= len;
      }

      let pos = this.node.worldPosition;
      let targetX = pos.x + dirX * this.player.speed * dt;
      let targetY = pos.y + dirY * this.player.speed * dt;
      this.node.setWorldPosition(targetX, targetY, pos.z);
    } else {
      //加速处理
      let speed = this._transformsCache.length * this.player.speed;
      let movement = speed * dt;
      while (movement > 0 && this._transformsCache.length > 0) {
        let pos = this.node.worldPosition;
        let target = this._transformsCache[0];
        let dist = this.computeTargetDist(pos.x, pos.y, target[0], target[1]);
        if (dist > movement) {
          let factor = movement / dist;
          let targetX = pos.x * (1 - factor) + target[0] * factor;
          let targetY = pos.y * (1 - factor) + target[1] * factor;
          this.node.setWorldPosition(targetX, targetY, pos.z);
          movement = 0;
        } else {
          movement -= dist;
          this.node.setWorldPosition(target[0], target[1], pos.z);
          this._transformsCache.shift();
        }
      }
    }
    NodeUtils.clampInMapBoundary(this.node as ExtraNode);
  }
}
