import Dog from "../Objects/Dog";
import Hive from "../Objects/Hive";
import LimitBar from "../Objects/LimitBar";
import LocalStorage from "../Data/LocalStorage";
import PracticeLine from "../Objects/PracticeLine";
import Utils from "../Tools/Utils";
import Drawer from "../Tools/Drawer";
import { GlobalVariables } from "../Data/GlobalVariables";
import Levels from "./Levels";
import StageController from "./StageController";

const { ccclass, property } = cc._decorator;

@ccclass
export default class GamePlay extends cc.Component {
  @property lineMinSegLength = 10;
  @property lineWidth = 10;

  @property(Array(cc.Prefab)) prefabToLoad: Array<cc.Prefab> =
    new Array<cc.Prefab>();
  @property(cc.Graphics) graphic: cc.Graphics = null;
  @property(cc.Label) timerLabel: cc.Label = null;
  @property(cc.Node) clock: cc.Node = null;
  @property(cc.Node) levelLabel: cc.Node = null;
  @property(cc.Node) levelComp: cc.Node = null;
  @property(cc.Node) rewardSprite: cc.Node = null;
  @property(cc.Node) shadow: cc.Node = null;
  @property([cc.SpriteFrame]) starWinSprites: cc.SpriteFrame[] = [];
  @property(cc.Prefab) fadePrefab: cc.Prefab = null;

  static instance: GamePlay;

  drew: boolean = false;
  draw: cc.Node = null;
  moved: boolean = false;
  rb: cc.RigidBody = null;
  rbCol: cc.PhysicsPolygonCollider = null;
  countdown: number = 7;
  startCount: boolean = false;
  currentLevel: number = 1;
  highestLevel: number;

  isGameover: boolean = false;
  isCompleted: boolean = false;

  lastTouchPos: cc.Vec2 = cc.v2(0, 0);
  leftEdge: cc.Vec2 = null;
  rightEdge: cc.Vec2 = null;

  linePoints: Array<cc.Vec2> = new Array<cc.Vec2>();
  leftEdgePoints: Array<cc.Vec2> = new Array<cc.Vec2>();
  rightEdgePoints: Array<cc.Vec2> = new Array<cc.Vec2>();

  blockedObjects: Array<cc.Node> = new Array<cc.Node>();

  protected onLoad(): void {
    GamePlay.instance = this;
    cc.director.getPhysicsManager().enabled = true;
    cc.director.getCollisionManager().enabled = true;

    // cc.director.getPhysicsManager().debugDrawFlags = 1;
    this.highestLevel = LocalStorage.getHighestLevel();
    this.currentLevel = GlobalVariables.levelData;
    this.loadPrefab(this.currentLevel);

    const dataBlocked = this.prefabToLoad[this.currentLevel].data.children;
    for (let i = 0; i < dataBlocked.length; i++) {
      if (dataBlocked[i].name == "Dog" || dataBlocked[i].name == "Blocks") {
        this.blockedObjects.push(dataBlocked[i]);
      }
    }

    this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchDown, this);
    this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
    this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchUp, this);
    this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchUp, this);
  }

  loadPrefab(level) {
    const newNode = cc.instantiate(this.prefabToLoad[level]);
    newNode.parent = this.node;
    newNode.position = cc.Vec3.ZERO;
    this.levelLabel.getComponent(
      cc.Label
    ).string = `Level ${this.prefabToLoad[level].name}`;
    this.isCompleted = false;
    this.isGameover = false;
  }

  onTouchDown(event: cc.Event.EventTouch) {
    let touchPos = event.getLocation();

    if (this.currentLevel == 0) {
      PracticeLine.instance.node.active = false;
    }

    this.draw = new cc.Node();
    this.draw.parent = this.node;
    this.draw.zIndex = 100;
    let graphic = this.draw.addComponent(cc.Graphics);
    graphic.lineCap = this.graphic.lineCap;
    graphic.lineJoin = this.graphic.lineJoin;
    this.rb = this.draw.addComponent(cc.RigidBody);
    this.rb.type = cc.RigidBodyType.Static;

    this.linePoints.length = 0;

    this.linePoints.push(touchPos);
    this.lastTouchPos = touchPos;
  }

  onTouchMove(event: cc.Event.EventTouch) {
    this.moved = true;
    let touchPos = event.getLocation();

    let posValid = true;
    for (let i = 0; i < this.blockedObjects.length; i++) {
      let node = this.blockedObjects[i];

      let nodeWorldPos = node.convertToWorldSpaceAR(cc.Vec2.ZERO);
      let nodeSize = node.getContentSize();
      let nodeRect = new cc.Rect(
        nodeWorldPos.x - nodeSize.width / 2,
        nodeWorldPos.y - nodeSize.height / 2,
        nodeSize.width * GlobalVariables.ratioDiffX,
        nodeSize.height * GlobalVariables.ratioDiffY
      );

      // console.log(node.name, node.getBoundingBoxToWorld().toString());
      // let newBoundBox = node.getBoundingBoxToWorld();
      // newBoundBox.x *= 2;
      // newBoundBox.y *= 2;

      // if (newBoundBox.contains(touchPos)) {
      //   posValid = false;
      // }
    }

    // if (!posValid) return;

    if (
      cc.Vec2.distance(touchPos, this.lastTouchPos) >= this.lineMinSegLength
    ) {
      if (!this.drew) {
        let touchNormal = touchPos.sub(this.lastTouchPos).normalize();
        let leftEdgeNormal = Utils.RotateVec2CounterClockwise(touchNormal);
        let rightEdgeNormal = Utils.RotateVec2Clockwise(touchNormal);

        this.leftEdgePoints.push(
          this.lastTouchPos.add(
            leftEdgeNormal.multiplyScalar(this.lineWidth / 2)
          )
        );
        this.rightEdgePoints.push(
          this.lastTouchPos.add(
            rightEdgeNormal.multiplyScalar(this.lineWidth / 2)
          )
        );

        this.lastTouchPos = touchPos;
        this.linePoints.push(touchPos);

        LimitBar.instance.fillTheBar();
        if (LimitBar.instance.progress <= 0) {
          this.onTouchUp(event);
        } else this.drawLine();
      }
    }
  }

  onTouchUp(event: cc.Event.EventTouch) {
    if (!this.drew && this.moved) {
      let touchPos = event.getLocation();

      let touchNormal = touchPos.sub(this.lastTouchPos).normalize();
      let leftEdgeNormal = Utils.RotateVec2CounterClockwise(touchNormal);
      let rightEdgeNormal = Utils.RotateVec2Clockwise(touchNormal);

      this.rbCol = this.draw.addComponent(cc.PhysicsPolygonCollider);

      this.leftEdgePoints.push(
        touchPos.add(leftEdgeNormal.multiplyScalar(this.lineWidth / 2))
      );

      this.rightEdgePoints.push(
        touchPos.add(rightEdgeNormal.multiplyScalar(this.lineWidth / 2))
      );

      this.rbCol.points.length = 0;

      this.rightEdgePoints.forEach((point) => {
        this.rbCol.points.push(this.rbCol.node.convertToNodeSpaceAR(point));
      });

      this.leftEdgePoints.reverse();
      this.leftEdgePoints.forEach((point) => {
        this.rbCol.points.push(this.rbCol.node.convertToNodeSpaceAR(point));
      });

      Drawer.drawArray(this.leftEdgePoints);
      Drawer.drawArray(this.rightEdgePoints);

      this.rbCol.apply();

      StageController.instance.onHunting();

      this.rb.type = cc.RigidBodyType.Dynamic;

      this.clock.active = true;
      this.startCount = true;
      this.drew = true;
    }
  }

  protected update(dt: number): void {
    if (!this.startCount) return;

    this.countdown -= dt;
    this.onStartCountdown();
  }

  onStartCountdown() {
    this.timerLabel.string = Math.floor(this.countdown).toString();

    if (this.countdown > 0 && this.isGameover) {
      this.startCount = false;

      this.scheduleOnce(() => {
        this.onReloadLevel();
      }, 2);
    }
    if (!Dog.instance.attacked && this.countdown <= 1) {
      this.startCount = false;
      this.onLevelCompleted(this.currentLevel);
    }
  }

  onReloadLevel() {
    this.drew = false;
    cc.director.loadScene("gameplay");
  }

  onLevelCompleted(level) {
    this.isCompleted = true;
    StageController.instance.onCompleted();
    if (this.currentLevel + 1 == this.highestLevel) {
      LocalStorage.setHighestLevel((this.highestLevel += 1));
    }
    this.levelComp.active = true;
    this.shadow.active = true;

    if (LimitBar.instance.winStar == 3) {
      LocalStorage.setStarAtLevel(parseInt(this.prefabToLoad[level].name), 3);
    }
    if (LimitBar.instance.winStar == 2) {
      this.rewardSprite.getComponent(cc.Sprite).spriteFrame =
        this.starWinSprites[1];
      if (
        LocalStorage.getStarAtLevel(parseInt(this.prefabToLoad[level].name)) >=
        2
      )
        return;
      LocalStorage.setStarAtLevel(parseInt(this.prefabToLoad[level].name), 2);
    }
    if (LimitBar.instance.winStar == 1) {
      this.rewardSprite.getComponent(cc.Sprite).spriteFrame =
        this.starWinSprites[0];
      if (
        LocalStorage.getStarAtLevel(parseInt(this.prefabToLoad[level].name)) >=
        1
      )
        return;
      LocalStorage.setStarAtLevel(parseInt(this.prefabToLoad[level].name), 1);
    }
  }

  onNextLevel() {
    LocalStorage.setCurrentLevel((this.currentLevel += 1));
    this.currentLevel = LocalStorage.getCurrentLevel();

    if (this.currentLevel > 29) return cc.director.loadScene("start");

    Levels.instance.loadScene(null, this.currentLevel.toString());
  }

  drawLine() {
    let lineDrawer = this.draw.getComponent(cc.Graphics);
    if (lineDrawer == null) lineDrawer = this.draw.addComponent(cc.Graphics);

    lineDrawer.strokeColor = this.graphic.strokeColor;
    lineDrawer.lineWidth = this.graphic.lineWidth;

    for (let i = 0; i < this.linePoints.length - 1; i++) {
      let p1 = this.linePoints[i];
      let p2 = this.linePoints[i + 1];
      let p1InLocal = lineDrawer.node.parent.convertToNodeSpaceAR(p1);
      let p2InLocal = lineDrawer.node.parent.convertToNodeSpaceAR(p2);
      lineDrawer.moveTo(p1InLocal.x, p1InLocal.y);
      lineDrawer.lineTo(p2InLocal.x, p2InLocal.y);
      lineDrawer.stroke();
    }
  }
}
