import { Trigger } from "./trigger";

import {
  Block,
  ChatSendAfterEvent,
  PaletteColor,
  EntityInventoryComponent,
  ItemStack,
  ItemUseOnBeforeEvent,
  MolangVariableMap,
  Player,
  Vector3,
  world,
} from "@minecraft/server";
import { vector3, vector3Distance } from "../utils/vectorUtils";
import { Mindkeeper, StoreType } from "../mindKeeper";
import { spawnParticle } from "../utils/particleUtils";
import { Manager } from "./manager";

export class Maker {
  private manager: Manager;

  log: Map<string, number> = new Map();

  currentTrigger: Trigger | null = null;
  waitingForPoint2: boolean = false;
  point1: Vector3 = vector3(0, 0, 0);
  point2: Vector3 = vector3(0, 0, 0);

  mindKeeper: Mindkeeper;

  constructor(mindKeeper: Mindkeeper, manager: Manager) {
    this.mindKeeper = mindKeeper;
    this.manager = manager;
  }

  OnChat(event: ChatSendAfterEvent) {
    if (event.message === "!toggleTriggers") {
      this.mindKeeper.set("ShowTriggers", (this.mindKeeper.get("ShowTriggers") as boolean) ? false : true);
    }
    if (event.message === "!TWand") {
      const item = new ItemStack("minecraft:stick");
      item.nameTag = "MakeTrigger";
      ((event.sender as Player).getComponent("inventory") as EntityInventoryComponent).container!.addItem(item);
      world.sendMessage("Thou shall have the Powah");
    }

    if (event.message === "!deleteCurrentTrigger") {
      let currentTriggerdTrigger = this.manager
        .GetTriggers()
        .filter((trigger) => trigger.IsPlayerInside(event.sender as Player));
      if (currentTriggerdTrigger.length > 0) {
        this.manager.GetTriggers().splice(this.manager.GetTriggers().indexOf(currentTriggerdTrigger[0]), 1);
        world.sendMessage("Trigger deleted");
      } else {
        world.sendMessage("No trigger to delete");
      }
      this.manager.Save();
    }
    const command = event.message.split(" ")[0];
    if (command == "!setFunction") {
      const name = event.message.split(" ")[1];
      const trigger = this.manager.GetCurrentActiveTriggers()[0];

      if (trigger === undefined) {
        world.sendMessage("No trigger selected");
        return;
      }

      trigger.eventToDispatch = name;
      this.manager.Save();
      world.sendMessage(`Function set to ${name}`);
    }
  }

  OnItemUse(event: ItemUseOnBeforeEvent) {
    const currentItemHeld: ItemStack = event.itemStack;
    const blockInteracted: Block = event.block;
    const player: Player = event.source as Player;
    const oldLog = this.log.get(player.name)!;
    this.log.set(player.name, Date.now());
    if (oldLog + 150 >= Date.now()) return;

    if (currentItemHeld.typeId == "minecraft:stick" && currentItemHeld.nameTag == "MakeTrigger") {
      if (this.waitingForPoint2) {
        this.point2 = vector3(blockInteracted.location.x, blockInteracted.location.y, blockInteracted.location.z);

        let minX = Math.min(this.point1.x, this.point2.x);
        let maxX = Math.max(this.point1.x, this.point2.x) + 1;
        let minY = Math.min(this.point1.y, this.point2.y);
        let maxY = Math.max(this.point1.y, this.point2.y) + 1;
        let minZ = Math.min(this.point1.z, this.point2.z);
        let maxZ = Math.max(this.point1.z, this.point2.z) + 1;

        //Take the outer bounds of the two points
        this.point1 = vector3(minX, minY, minZ);
        this.point2 = vector3(maxX, maxY, maxZ);

        this.currentTrigger = new Trigger(this.point1, this.point2);

        this.manager.AddTrigger(this.currentTrigger!);
        this.currentTrigger = null;
        this.waitingForPoint2 = false;
        world.sendMessage(`Trigger added`);
        this.manager.Save();
        return;
      } else {
        this.point1 = vector3(event.block.location.x, event.block.location.y, event.block.location.z);
        this.waitingForPoint2 = true;
        world.sendMessage(`Select a second point`);
      }
    }
  }

  DrawLine(pos1: Vector3, pos2: Vector3, color: PaletteColor): void {
    if (pos1 != null && pos2 != null) {
      //draw a line with particles
      const xStep = pos2.x - pos1.x;
      const yStep = pos2.y - pos1.y;
      const zStep = pos2.z - pos1.z;

      const steps = Math.max(Math.abs(xStep), Math.abs(yStep), Math.abs(zStep));
      for (let i = 0; i <= steps; i++) {
        const x = pos1.x + (xStep / steps) * i;
        const y = pos1.y + (yStep / steps) * i;
        const z = pos1.z + (zStep / steps) * i;

        //make the ends and starts a different color
        let map = new MolangVariableMap();
        if (i == 0 || i == steps) {
          map.setColorRGB("variable.color", { red: 0, green: 255, blue: 0 });
          const particleData: MolangVariableMap = new MolangVariableMap();
          particleData.setColorRGB("variable.color", {
            red: 0,
            green: 1,
            blue: 0,
          });
          spawnParticle(vector3(x, y + 0.1, z), "codecosmos:point", particleData);
        } else {
          map.setColorRGB("variable.color", { red: 0, green: 255, blue: 0 });
          const particleData: MolangVariableMap = new MolangVariableMap();
          if (color == PaletteColor.Red) {
            particleData.setColorRGB("variable.color", {
              red: 1,
              green: 0,
              blue: 0,
            });
          } else if (color == PaletteColor.White) {
            particleData.setColorRGB("variable.color", {
              red: 1,
              green: 1,
              blue: 1,
            });
          }
          spawnParticle(vector3(x, y + 0.1, z), "codecosmos:point", particleData);
        }
      }
    }
  }

  Update() {
    let shouldRender = this.mindKeeper.get("ShowTriggers") as boolean;
    this.manager.GetTriggers().forEach((trigger: Trigger) => {
      trigger.Update();

      if (shouldRender) {
        const players = world.getAllPlayers();
        const triggerCenter = vector3(
          (trigger.point1.x + trigger.point2.x) / 2,
          (trigger.point1.y + trigger.point2.y) / 2,
          (trigger.point1.z + trigger.point2.z) / 2
        );
        const distances = players.map((player) => {
          return {
            player: player,
            distance: vector3Distance(player.location, triggerCenter),
          };
        });

        distances.sort((a, b) => a.distance - b.distance);
        const closestPlayer = distances[0].player;
        const distance = distances[0].distance;

        if (distance > 25) return;

        const p1 = trigger.point1;
        const p2 = vector3(trigger.point1.x, trigger.point2.y, trigger.point1.z);
        const p3 = vector3(trigger.point2.x, trigger.point1.y, trigger.point1.z);
        const p4 = vector3(trigger.point2.x, trigger.point2.y, trigger.point1.z);
        const p5 = vector3(trigger.point1.x, trigger.point1.y, trigger.point2.z);
        const p6 = vector3(trigger.point1.x, trigger.point2.y, trigger.point2.z);
        const p7 = vector3(trigger.point2.x, trigger.point1.y, trigger.point2.z);
        const p8 = trigger.point2;

        //is there a player inside this trigger
        const color = trigger.IsAnyPlayerInside() ? PaletteColor.Red : PaletteColor.White;
        this.DrawLine(p1, p2, color);
        this.DrawLine(p2, p4, color);
        this.DrawLine(p4, p3, color);
        this.DrawLine(p3, p1, color);
        this.DrawLine(p5, p6, color);
        this.DrawLine(p6, p8, color);
        this.DrawLine(p8, p7, color);
        this.DrawLine(p7, p5, color);
        this.DrawLine(p1, p5, color);
        this.DrawLine(p2, p6, color);
        this.DrawLine(p3, p7, color);
        this.DrawLine(p4, p8, color);
      }
    });
  }
}
