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) starLabel: cc.Node = null;
  @property(cc.Node) coinLabel: cc.Node = null;
  @property(cc.Node) coinEarned: 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;
  currentAllStar: number;
  currentCoin: number;

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

  lastTouchPos: cc.Vec2 = cc.v2(0, 0);
  lastDrawnPos: 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 = LocalStorage.getCurrentLevel();
    this.currentCoin = LocalStorage.getAllCoin();
    this.coinLabel.getComponent(
      cc.Label
    ).string = `x${this.currentCoin.toString()}`;

    this.updateAllStarLabel();
    this.loadPrefab(GlobalVariables.levelToLoad);

    const dataBlocked = StageController.instance.blockedObjects;
    for (let i = 0; i < dataBlocked.length; i++) {
      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);
  }

  updateAllStarLabel() {
    let sum = 0;
    for (const item of LocalStorage.getAllStars()) {
      if (item.hasOwnProperty("star")) {
        sum += item.star;
      }
    }
    this.starLabel.getComponent(cc.Label).string = `${sum}/90`;
  }

  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;
    this.lastDrawnPos = 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 newBoundBox = node.getBoundingBoxToWorld();

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

    if (!posValid) return;

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

        let leftEdgeLast = this.lastTouchPos.add(
          cc.v2(leftEdgeNormal).multiplyScalar(this.lineWidth / 2)
        );

        let rightEdgeLast = this.lastTouchPos.add(
          cc.v2(rightEdgeNormal).multiplyScalar(this.lineWidth / 2)
        );

        let leftEdgeCurrent = touchPos.add(
          cc.v2(leftEdgeNormal).multiplyScalar(this.lineWidth / 2)
        );

        let rightEdgeCurrent = touchPos.add(
          cc.v2(rightEdgeNormal).multiplyScalar(this.lineWidth / 2)
        );

        let newPolyCol = this.draw.addComponent(cc.PhysicsPolygonCollider);

        newPolyCol.points = [
          leftEdgeLast,
          leftEdgeCurrent,
          rightEdgeCurrent,
          rightEdgeLast,
        ];

        newPolyCol.apply();

        this.lastTouchPos = touchPos;
        this.lastDrawnPos = 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) {
      this.rb.type = cc.RigidBodyType.Dynamic;
      StageController.instance.onHunting();
      this.clock.active = true;
      this.startCount = true;
      this.drew = true;
    }
  }

  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();
    }
  }

  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;
    this.addCoin();
    StageController.instance.onCompleted();
    if (this.currentLevel + 1 == this.highestLevel) {
      LocalStorage.setHighestLevel((this.highestLevel += 1));
    }
    this.levelComp.active = true;
    this.shadow.active = true;
    let levelName = parseInt(this.prefabToLoad[level].name);

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

  addCoin() {
    let randomCoin = Math.floor(Math.random() * (30 - 26)) + 25;
    this.coinEarned.getComponent(cc.Label).string = `x${randomCoin}`;
    LocalStorage.setAllCoin((this.currentCoin += randomCoin));
    this.coinLabel.getComponent(
      cc.Label
    ).string = `x${LocalStorage.getAllCoin()}`;
  }

  onNextLevel() {
    LocalStorage.setCurrentLevel((this.currentLevel += 1));
    this.currentLevel = LocalStorage.getCurrentLevel();
    if (this.currentLevel > 29) return cc.director.loadScene("start");
    this.drew = false;
    GlobalVariables.levelToLoad = this.currentLevel;
    cc.director.loadScene("gameplay");
  }
}
