import {
  _decorator, Node, assetManager, SpriteAtlas, Sprite, CircleCollider2D,
  math, Collider2D, Contact2DType, ParticleSystem2D, UITransform,
  tween, v3, v2, Component, Prefab, PhysicsGroup
} from "cc";
import { Global } from "./Global";
import { PoolManager } from "./PoolManager";
import { FoodType, DirectionType } from "./Enums";
import { isIntersect } from "./uitl";
import { MainGame } from "./MainGame";

const { ccclass, property } = _decorator;

@ccclass("Animal")
export class Animal extends Component {
  @property({
    type: Node,
    displayName: "头部"
  })
  private Head: Node = null;

  @property({
    type: Node,
    displayName: "水花"
  })
  private water: Node = null;

  @property(Prefab)
  private bodyPrefab: Prefab = null;

  private uiSanke: UITransform = null;
  private uiFondManger: UITransform = null;
  private eye: Collider2D = null;
  private Bodys: Node[] = [];

  // 蛇身图片资源
  private imgHead: any = null;
  private imgBody1: any = null;
  private imgBody2: any = null;
  private imgTait1: any = null;
  private imgTait2: any = null;

  // 蛇的属性
  private isLife: boolean = false;
  private scale: number = 0.2;
  private speed: number = 600;
  private direction: DirectionType = DirectionType.DEFAULT;
  private directionSpeed: number = 0;
  private taitLen: number = 2;
  private energy: number = 0;
  private isRunning: boolean = false;
  private tag: number = 0;

  // 视野范围
  private vw: number = Global.visibleSize.width / 2 + 100;
  private vh: number = Global.visibleSize.height / 2 + 100;

  private ready: boolean = false;
  private b: any = null;

  /**
   * 初始化蛇
   */
  init(x: number, y: number, angle: number, skinId: number = 1, scale: number = 0.2,
       length: number = 3, taitLen: number = 1, tag: number = 0) {
    this.ready = false;
    const skinPath = `skin/s${skinId}`;

    // 加载皮肤资源
    assetManager.loadBundle("resources", (err, bundle) => {
      if (err) {
        console.warn(err);
        return;
      }

      bundle.load(skinPath, SpriteAtlas, (err, atlas) => {
        if (err) {
          console.warn(err);
          return;
        }

        // 获取各部分图片
        this.imgHead = atlas.getSpriteFrame("head");
        this.imgBody1 = atlas.getSpriteFrame("body1");
        this.imgBody2 = atlas.getSpriteFrame("body2");
        this.imgTait1 = atlas.getSpriteFrame("tait1");
        this.imgTait2 = atlas.getSpriteFrame("tait2");

        // 初始化属性
        this.Bodys = [];
        this.scale = scale;
        this.speed = this.speed * scale;
        this.energy = 0;
        this.taitLen = taitLen;
        this.tag = tag;

        // 设置头部
        this.Head.angle = 0;
        this.Head.setPosition(x, y);
        this.Head.setScale(scale, scale);
        this.Head.getComponent(Sprite).spriteFrame = this.imgHead;
        this.Head.active = isIntersect(
          this.Head.getPosition(),
          MainGame.ins.camera.node.getPosition(),
          this.vw,
          this.vh
        );

        // 设置水花特效
        this.water.getComponent(CircleCollider2D).radius = math.randomRangeInt(100, 200);

        // 创建身体节点
        for (let i = 0; i < length; i++) {
          const body = PoolManager.instance.getNode(this.bodyPrefab, this.node);
          const collider = body.getComponent(Collider2D);
          if (collider) collider.tag = this.tag;

          body.setPosition(9999, 9999);

          // 设置身体图片
          if (i < length - taitLen) {
            body.getComponent(Sprite).spriteFrame = i % 2 == 0 ? this.imgBody2 : this.imgBody1;
          } else {
            body.getComponent(Sprite).spriteFrame = i == length - 1 ? this.imgTait2 : this.imgTait1;
          }

          body.active = false;
          this.Bodys.push(body);
        }

        this.isLife = true;
      });

      this.ready = true;
    });
  }

  onEnable() {
    // 注册碰撞事件
    const headCollider = this.Head.getComponent(Collider2D);
    if (headCollider) {
      headCollider.on(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);
    }

    this.eye = this.water.getComponent(Collider2D);
    if (this.eye) {
      this.eye.on(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
    }

    this.uiSanke = this.getComponent(UITransform);
    this.uiFondManger = MainGame.ins.fondManger.node.getComponent(UITransform);
  }

  onDisable() {
    // 注销碰撞事件
    const headCollider = this.Head.getComponent(Collider2D);
    if (headCollider) {
      headCollider.off(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);
    }
    if (this.eye) {
      this.eye.off(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
    }
  }

  /**
   * 头部碰撞回调
   */
  onBeginHead(selfCollider: Collider2D, otherCollider: Collider2D) {
    if (otherCollider.group === PhysicsGroup["Body"] &&
      otherCollider.tag != this.tag) {
      this.death();
    }
  }

  /**
   * 视野碰撞回调
   */
  onBeginEye(selfCollider: Collider2D, otherCollider: Collider2D) {
    // 碰到食物
    if (otherCollider.group === PhysicsGroup["Prop"]) {
      const foodType = otherCollider.tag;
      this.isRunning = true;

      // 计算朝向食物的角度
      const headPos = this.Head.getPosition();
      const foodPos = otherCollider.node.getPosition();
      const worldPos = this.uiFondManger.convertToWorldSpaceAR(foodPos);
      const nodePos = this.uiSanke.convertToNodeSpaceAR(worldPos);
      const angle = Math.atan2(nodePos.y - headPos.y, nodePos.x - headPos.x);
      this.setAngle(angle * (180 / Math.PI));

      // 吃掉食物的动画
      tween(otherCollider.node)
        .to(0.3, {
          position: this.Head.getPosition(),
          scale: v3(0, 0)
        })
        .call(() => {
          PoolManager.instance.putNode(otherCollider.node);
          if (foodType == FoodType.FOOD) {
            this.setEnergy(1);
          } else if (foodType == FoodType.MONEY) {
            this.setEnergy(5);
          }
        })
        .start();
    }
    // 碰到其他蛇身
    else if (otherCollider.group === PhysicsGroup["Body"] &&
      otherCollider.tag != this.tag) {
      this.setAngle(this.Head.angle + 180);
      this.isRunning = true;
    }
  }

  /**
   * 设置能量值
   */
  setEnergy(value: number) {
    this.energy += value;
    const needEnergy = Math.floor(this.scale * 10);

    if (this.energy >= needEnergy) {
      this.grow();
      this.energy -= needEnergy;
      if (this.scale < 0.8) {
        this.scale += 0.005;
      }
      this.speed = 600 * this.scale;
    }
  }

  /**
   * 成长(增加身体节点)
   */
  grow() {
    const len = this.Bodys.length;
    const body = PoolManager.instance.getNode(this.bodyPrefab, this.node);

    body.angle = this.Bodys[len - this.taitLen - 1].angle;
    body.setPosition(this.Bodys[len - this.taitLen - 1].getPosition());
    body.setScale(this.scale, this.scale);

    const isEven = (len - this.taitLen) % 2 == 0;
    body.getComponent(Sprite).spriteFrame = isEven ? this.imgBody2 : this.imgBody1;
    body.getComponent(Collider2D).tag = this.tag;

    body.active = isIntersect(
      body.getPosition(),
      MainGame.ins.camera.node.getPosition(),
      this.vw,
      this.vh
    );

    this.Bodys.splice(len - this.taitLen, 0, body);
  }

  /**
   * 设置角度
   */
  setAngle(angle: number) {
    if (this.isLife) {
      this.Head.angle = angle;
    }
  }

  /**
   * 快速移动
   */
  running(dt: number) {
    if (!this.isLife) return;

    const len = this.Bodys.length - 1;

    // 更新身体位置
    for (let i = len; i > 1; i--) {
      this.Bodys[i].angle = this.Bodys[i - 2].angle;
      this.Bodys[i].setPosition(this.Bodys[i - 2].getPosition());
      this.Bodys[i].setScale(this.scale, this.scale);
      this.Bodys[i].setSiblingIndex(len - i);
      this.Bodys[i].active = isIntersect(
        this.Bodys[i].getPosition(),
        MainGame.ins.camera.node.getPosition(),
        this.vw,
        this.vh
      );
    }

    // 更新第二节位置
    this.Bodys[1].angle = this.Head.angle;
    this.Bodys[1].setPosition(this.Head.getPosition());
    this.Bodys[1].setScale(this.scale, this.scale);
    this.Bodys[1].setSiblingIndex(len - 1);
    this.Bodys[1].active = isIntersect(
      this.Bodys[1].getPosition(),
      MainGame.ins.camera.node.getPosition(),
      this.vw,
      this.vh
    );

    // 更新第一节位置
    this.Bodys[0].angle = this.Head.angle;
    this.Bodys[0].setPosition(this.getNewPos(this.Head.angle, dt, this.Head.getPosition()));
    this.Bodys[0].setScale(this.scale, this.scale);
    this.Bodys[0].setSiblingIndex(len);
    this.Bodys[0].active = isIntersect(
      this.Bodys[0].getPosition(),
      MainGame.ins.camera.node.getPosition(),
      this.vw,
      this.vh
    );

    // 更新头部位置
    this.Head.setPosition(
      this.getNewPos(this.Head.angle, dt, this.Head.getPosition(), this.speed * 2)
    );
    this.Head.setScale(this.scale, this.scale);
    this.Head.setSiblingIndex(len + 1);
    this.Head.active = isIntersect(
      this.Head.getPosition(),
      MainGame.ins.camera.node.getPosition(),
      this.vw,
      this.vh
    );
  }

  /**
   * 普通移动
   */
  move(dt: number) {
    if (!this.ready || !this.isLife) return;

    const len = this.Bodys.length - 1;

    // 更新身体位置
    for (let i = len; i > 0; i--) {
      this.Bodys[i].angle = this.Bodys[i - 1].angle;
      this.Bodys[i].setPosition(this.Bodys[i - 1].getPosition());
      this.Bodys[i].setScale(this.scale, this.scale);
      this.Bodys[i].setSiblingIndex(len - i);
      this.Bodys[i].active = isIntersect(
        this.Bodys[i].getPosition(),
        MainGame.ins.camera.node.getPosition(),
        this.vw,
        this.vh
      );
    }

    // 更新第一节位置
    this.Bodys[0].angle = this.Head.angle;
    this.Bodys[0].setPosition(this.Head.getPosition());
    this.Bodys[0].setScale(this.scale, this.scale);
    this.Bodys[0].setSiblingIndex(len);
    this.Bodys[0].active = isIntersect(
      this.Bodys[0].getPosition(),
      MainGame.ins.camera.node.getPosition(),
      this.vw,
      this.vh
    );

    // 更新头部位置
    this.Head.setPosition(
      this.getNewPos(this.Head.angle, dt, this.Head.getPosition())
    );
    this.Head.setScale(this.scale, this.scale);
    this.Head.setSiblingIndex(len + 1);
    this.Head.active = isIntersect(
      this.Head.getPosition(),
      MainGame.ins.camera.node.getPosition(),
      this.vw,
      this.vh
    );
  }

  v = v3();

  /**
   * AI自动移动
   */
  autoMove(dt: number) {
    if (!this.isLife) return;

    // 预判断下一个位置
    this.v = this.getNewPos(
      this.Head.angle,
      dt,
      this.Head.getPosition(),
      this.speed * 20
    );

    const halfWidth = Global.MAP_WIDTH / 2;
    const halfHeight = Global.MAP_HIGHT / 2;

    // 如果要超出地图边界,则转向
    if (this.v.x <= -halfWidth || this.v.x >= halfWidth ||
      this.v.y <= -halfHeight || this.v.y >= halfHeight) {
      const angleAbs = Math.abs(this.Head.angle % 180);
      this.direction = DirectionType.DEFAULT;
      if (angleAbs > 90) {
        this.Head.angle += math.randomRangeInt(180 - angleAbs, 360);
      } else {
        this.Head.angle -= math.randomRangeInt(angleAbs, 180 + angleAbs);
      }
    }
    // 随机改变方向
    else if (math.randomRangeInt(0, 11) == 0) {
      this.direction = math.randomRangeInt(0, 3);
      this.directionSpeed = math.randomRangeInt(0, 6);
    }

    // 根据方向更新角度
    if (this.direction == DirectionType.DEFAULT) {
      // 保持当前角度
    } else if (this.direction == DirectionType.LEFT) {
      this.Head.angle += this.directionSpeed;
    } else {
      this.Head.angle -= this.directionSpeed;
    }

    // 随机切换移动状态
    if (this.isRunning) {
      this.running(dt);
      this.isRunning = math.randomRangeInt(1, 20) != 1;
    } else {
      this.move(dt);
    }
  }

  /**
   * 死亡
   */
  death() {
    if (!this.isLife) return;

    this.isLife = false;
    this.node.active = false;

    // 死亡后将身体变成食物
    this.initFond(this.Bodys.length).then(() => {
      const x = math.randomRangeInt(
        -(Global.MAP_WIDTH / 2 - 50),
        Global.MAP_WIDTH / 2 - 50
      );
      const y = math.randomRangeInt(
        -(Global.MAP_HIGHT / 2 - 50),
        Global.MAP_HIGHT / 2 - 50
      );
      this.reInit(x, y);
    });
  }

  /**
   * 重生
   */
  reInit(x: number, y: number) {
    this.Head.angle = 0;
    this.Head.setPosition(x, y);
    this.node.active = true;
    this.isLife = true;
  }

  /**
   * 计算新位置
   */
  getNewPos(angle: number, dt: number, pos: any, speed: number = this.speed) {
    const radian = angle / 180 * Math.PI;
    const dir = v2(Math.cos(radian), Math.sin(radian));
    return v3(
      pos.x + dt * dir.x * speed,
      pos.y + dt * dir.y * speed,
      0
    );
  }

  /**
   * 生成食物迭代器
   */
  private* _getItemGenerator(count: number) {
    for (let i = 0; i < count; i++) {
      yield this._initItem(i);
    }
  }

  /**
   * 初始化食物
   */
  private _initItem(index: number) {
    this.b = this.Bodys[index].getPosition();
    MainGame.ins.fondManger.setFood(
      math.randomRangeInt(this.b.x - 10, this.b.x + 11),
      math.randomRangeInt(this.b.y - 20, this.b.y + 21)
    );
    this.Bodys[index].setPosition(9999, 9999);
  }

  /**
   * 初始化食物
   */
  async initFond(count: number) {
    await this.executePreFrame(this._getItemGenerator(count), 1);
  }

  /**
   * 分帧执行
   */
  executePreFrame(generator: Generator, frameTime: number): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const iter = generator;
      const next = () => {
        const startTime = new Date().getTime();
        for (let result = iter.next(); ; result = iter.next()) {
          if (!result || result.done) {
            resolve(true);
            return;
          }
          if (new Date().getTime() - startTime > frameTime) {
            this.scheduleOnce(() => {
              next();
            });
            return;
          }
        }
      };
      next();
    });
  }
}