import {
  Block,
  ChatSendAfterEvent,
  EntityInventoryComponent,
  ItemStack,
  ItemUseOnBeforeEvent,
  MolangVariableMap,
  Player,
  world,
} from "@minecraft/server";
import { Vector3 } from "@minecraft/server";
import { Vector3Add, Vector3ToString, vector3 } from "../utils/vectorUtils";
import { spawnParticle } from "../utils/particleUtils";
import Pupeteer from "../pupeteer";
import Chalk from "../chalk";

export namespace TrailMaker {
  export class Maker {
    currentTrail: Trail;
    log: Map<string, number> = new Map();
    selectedIndex: number = 0;

    //Line
    point1: Vector3 = vector3(0, 0, 0);
    point2: Vector3 = vector3(0, 0, 0);

    waitingForPoint2: boolean = false;

    OnChat(event: ChatSendAfterEvent) {
      if (event.message == "!trailWand") {
        let item: ItemStack = new ItemStack("minecraft:stick");
        item.nameTag = "AddPoint";
        Pupeteer.givePlayerItem(event.sender as Player, item);
        world.sendMessage("Thou shall have the Trailing Powah");
      }
      if (event.message == "!trailDeleteWand") {
        let item: ItemStack = new ItemStack("minecraft:stick");
        item.nameTag = "DeletePoint";
        Pupeteer.givePlayerItem(event.sender as Player, item);
        world.sendMessage("Luke, i'm NOT your father");
      }
      if (event.message == "!trailLineWand") {
        let item: ItemStack = new ItemStack("minecraft:stick");
        item.nameTag = "AddLine";
        Pupeteer.givePlayerItem(event.sender as Player, item);
        world.sendMessage(`This is where i draw the ${Chalk.red("Line")} >:(`);
      }
      if (event.message == "!trailExport") {
        this.Export();
      }
    }
    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 (event.itemStack.typeId == "minecraft:stick" && event.itemStack.nameTag == "AddPoint") {
        let block = event.block;
        let pos = this.BlockToParticlePosition(block);
        world.sendMessage(`Added Point ${Vector3ToString(pos)}`);
        this.currentTrail.points.push(new Point(pos, this.currentTrail.currentPoint));

        this.currentTrail.currentPoint++;
        Pupeteer.setActionBarTimed(`Added point ${this.currentTrail.currentPoint}`, 3);
      }

      if (event.itemStack.typeId == "minecraft:stick" && event.itemStack.nameTag == "DeletePoint") {
        let block = event.block;
        let pos = this.BlockToParticlePosition(block);

        let point = this.currentTrail.points.find((point) => Vector3ToString(point.position) == Vector3ToString(pos));
        if (point) {
          this.currentTrail.points.splice(this.currentTrail.points.indexOf(point), 1);
          Pupeteer.setActionBarTimed(`Deleted point ${point.index}`, 3);
        } else {
          Pupeteer.setActionBarTimed(`No point found`, 3);
        }
      }

      if (event.itemStack.typeId == "minecraft:stick" && event.itemStack.nameTag == "AddLine") {
        world.sendMessage(`Waiting for point 2: ${this.waitingForPoint2}`);
        if (!this.waitingForPoint2) {
          // this.point1 = vector3(blockInteracted.location.x, blockInteracted.location.y, blockInteracted.location.z);
          this.point1 = this.BlockToParticlePosition(blockInteracted);

          this.waitingForPoint2 = true;
          Pupeteer.setActionBar("Select a second point");
          world.sendMessage("Select a second point");

          return;
        } else {
          this.point2 = this.BlockToParticlePosition(blockInteracted);

          //Calculate the blocks between these 2 points, andd add them to the trail
          //Assume point1 is the start, and point2 is the end
          let x1 = this.point1.x;
          let x2 = this.point2.x;
          let y1 = this.point1.y;
          let y2 = this.point2.y;
          let z1 = this.point1.z;
          let z2 = this.point2.z;

          //Find out what axis is the movement on, throw an error if it's on more than one axis

          let xDiff = Math.abs(x2 - x1);
          let yDiff = Math.abs(y2 - y1);
          let zDiff = Math.abs(z2 - z1);

          let axis = "";

          if (xDiff > 0 && yDiff == 0 && zDiff == 0) {
            axis = "x";
          } else if (xDiff == 0 && yDiff > 0 && zDiff == 0) {
            axis = "y";
          } else if (xDiff == 0 && yDiff == 0 && zDiff > 0) {
            axis = "z";
          } else {
            Pupeteer.setActionBarTimed("Invalid line", 3);
            return;
          }

          let start = 0;
          let end = 0;

          if (axis == "x") {
            start = Math.min(x1, x2);
            end = Math.max(x1, x2);
          } else if (axis == "y") {
            start = Math.min(y1, y2);
            end = Math.max(y1, y2);
          } else if (axis == "z") {
            start = Math.min(z1, z2);
            end = Math.max(z1, z2);
          }

          for (let i = start; i <= end; i++) {
            let pos = vector3(0, 0, 0);
            if (axis == "x") {
              pos = vector3(i, y1, z1);
            } else if (axis == "y") {
              pos = vector3(x1, i, z1);
            } else if (axis == "z") {
              pos = vector3(x1, y1, i);
            }

            this.currentTrail.points.push(new Point(pos, this.currentTrail.currentPoint));
            this.currentTrail.currentPoint++;
          }

          this.waitingForPoint2 = false;
          Pupeteer.setActionBarTimed("Added line", 3);
        }
      }
    }

    Update() {
      this.currentTrail.points.forEach((point) => {
        spawnParticle(point.position, "minecraft:balloon_gas_particle", new MolangVariableMap());
      });
    }

    Export() {
      let output = "";
      for (let i = 0; i < this.currentTrail.points.length; i++) {
        let point = this.currentTrail.points[i];
        let actualPos = point.position;
        output += `{ index: ${point.index}, position: vector3(${actualPos.x}, ${actualPos.y}, ${actualPos.z}) },\n`;
      }
      console.warn(output);
    }

    private BlockToParticlePosition(block: Block): Vector3 {
      let pos = vector3(block.location.x, block.location.y, block.location.z);
      pos = Vector3Add(pos, vector3(0.5, 0, 0.5));
      //If block is a slab or stair, offset by half a block

      let isHalfBlock = block.typeId.includes("slab") || block.typeId.includes("stair");
      if (isHalfBlock) {
        pos = Vector3Add(pos, vector3(0, 0.5, 0));
      } else {
        pos = Vector3Add(pos, vector3(0, 1.1, 0));
      }
      return pos;
    }

    constructor() {
      this.currentTrail = new Trail();
    }
  }

  class Trail {
    points: Point[] = [];
    currentPoint: number = 0;
    nextParticleTimer: number = 0;
    currentParticleCounter: number = 0;

    constructor() {}
  }

  class Point {
    position: Vector3;
    index: number;

    constructor(position: Vector3, index: number) {
      this.position = position;
      this.index = index;
    }
  }
}
