import {
  world,
  system,
  Vector3,
  MolangVariableMap,
  ChatSendAfterEvent,
  Dimension,
  TicksPerSecond,
  BlockVolume,
} from "@minecraft/server";
import { Mindkeeper, StoreType } from "./Commandeer/mindKeeper";
import Pupeteer from "./Commandeer/pupeteer";
import { Vector3Add, Vector3ToCommandString, Vector3ToFancyString, vector3 } from "./Commandeer/utils/vectorUtils";
import { delay } from "./Commandeer/utils/waitUtil";
import { PARTICLES, spawnParticle } from "./Commandeer/utils/particleUtils";
import { TrailMaker } from "./Commandeer/trail/trailMaker";
import * as CCTrigger from "./Commandeer/Trigger/CCTrigger";
import { Commands } from "./Commandeer/command/command";
import levelIntro from "./levels/levelIntro";
import { Trail } from "./Commandeer/trail/trailEngine";
import { startTrail } from "./trails/startTrail";
import { IntroToLevel1Trail } from "./trails/IntroToLevel1Trail";
import { MinecraftBlockTypes } from "./vanilla-data/mojang-block";
import mission1part1 from "./levels/mission1/mission1part1";
import mission1part2 from "./levels/mission1/mission1part2";
import mission1part3 from "./levels/mission1/mission1part3";
import mission2part1 from "./levels/mission2/mission2part1";
import { mission2part1Conditions } from "./levelConditions/mission2part1Conditions";
import mission2part2 from "./levels/mission2/mission2part2";
import { mission2part2Conditions } from "./levelConditions/mission2part2Conditions";
import Chalk from "./Commandeer/chalk";
import mission3part1 from "./levels/mission3/mission3part1";
import mission3part2 from "./levels/mission3/mission3part2";
import mission3part3 from "./levels/mission3/mission3part3";
import { runEntityEventOnTag, setNPCDialog } from "./Commandeer/utils/entityUtils";
import {
  animationPlaying,
  ClonePos,
  playAnimation,
  setAmimationPlaying,
  SetFrame,
} from "./Commandeer/animations/animation";
import { doNothingStupidTSIWantFilesNotAGiantAssMainFile } from "./triggers";
import { clearWall, fillWall, Wall } from "./Commandeer/utils/levelUtils";
// import { loadTriggers } from "./triggers";

const mindKeeper = new Mindkeeper(world);
const trailMaker: TrailMaker.Maker = new TrailMaker.Maker();
const triggerManager = new CCTrigger.Manager(mindKeeper);
const CURRENT_LEVEL = "currentLevel";
const AGENT_ID = "agentid";
const PREFIX = "!";
export { mindKeeper, CURRENT_LEVEL, triggerManager };

// loadTriggers();
// loadCommands();

triggerManager.RegisterFunctionTrigger("resetPath", (event) => {
  resetLightPath();
});

async function resetLightPath() {
  world.getDimension("overworld").runCommand("/fill 2467 9 87 2468 9 105 air");
}

let introToGlassBreakTimer: number = 0;
const glassBreakTime = 5 * TicksPerSecond;

let isIntroToLevel1Sqeuence = false;

let introTrail: Trail = new Trail("introTrail", 2, 4);
introTrail.fromTrail(startTrail);

let introToLevelTrail = new Trail("introToLevelTrail", 2, 2);
introToLevelTrail.fromTrail(IntroToLevel1Trail);

//fill 2466 0 48 2469 -1 48
let bilalBlock: Wall = {
  startPos: vector3(2466, 0, 48),
  endPos: vector3(2469, -1, 48),
};

// spawn a sphere of particle

const DEVELOPER_MODE = true;
let tickCounter = 0;
system.runInterval(async () => {
  tickCounter++;
  if (mindKeeper.initialised) {
    trailMaker.Update();
    if (tickCounter % 2 == 0) {
      triggerManager.Update();
    }

    const currentLevel = mindKeeper.get(CURRENT_LEVEL);
    switch (currentLevel) {
      case 0:
        // Show goto airlock text
        Pupeteer.setActionBar("%message.goto.airlock");
        introTrail.spawnNext();
        break;
      case 1:
        Pupeteer.setActionBar("%message.enter.airlock");
        break;
      case 2:
        Pupeteer.setActionBar("%message.talkto.chanel");
        //Wait for the scriptevent cc:startIntro
        break;
      case 3:
        levelIntro.update();
        break;
      case 4:
        if (!isIntroToLevel1Sqeuence) {
          isIntroToLevel1Sqeuence = true;
          // world.sendMessage("Intro to level1 sequence start");
          await lightUpPath();
          await randomExplosions();
          blowUpDoor();
          mindKeeper.increment(CURRENT_LEVEL);
          triggerAlarm();
        }
        break;
      case 5:
        introToGlassBreakTimer++;
        // world.sendMessage("Glass break timer: " + introToGlassBreakTimer);
        if (introToGlassBreakTimer > glassBreakTime) {
          introToGlassBreakTimer = 0;
          mindKeeper.increment(CURRENT_LEVEL);
        }
      case 6:
        // Show Goto Level 1
        if (glassWindowState == GlassWindowState.Fixed) {
          glassWindowState = GlassWindowState.Broken;
          system.run(async () => {
            if (DEVELOPER_MODE) {
              world.sendMessage("Breaking glass");
            }
            await delay(10);
            await breakGlassWindow();
            mindKeeper.increment(CURRENT_LEVEL);
            setNPCDialog("chanel1", "chanel_goto_temple");
          });
        }
        break;
      case 7:
        introToLevelTrail.spawnNext();
        Pupeteer.setActionBar("%message.follow.path.temple");
        //Talk to Bilal
        break;
      case 8:
        Pupeteer.setActionBar("%message.goto.temple");
        //Talk to Suki
        break;
      case 9:
        mission1part1.update();
        break;
      case 10:
        mission1part2.update();
        break;
      case 11:
        mission1part3.update();
        break;
      case 12:
        Pupeteer.setActionBar("%message.goto.level2");
      case 13:
        mission2part1.update();
        break;
      case 14:
        mission2part2.update();
        break;
      case 15:
        mission3part1.update();
        break;
      case 15:
        mission3part2.update();
        break;
      case 16:
        mission3part3.update();
        break;
      case 17:
        Pupeteer.setActionBar("Goto the door");
        break;
    }
  }
});

triggerManager.RegisterFunctionTrigger("lightPath", (event) => {
  lightUpPath();
});

//fill 2467 9 87 2468 9 105 redstone_block
async function lightUpPath() {
  let overworld: Dimension = world.getDimension("overworld");
  let pos1 = vector3(2467, 9, 87);
  let pos2 = vector3(2468, 9, 105);

  for (let z = pos2.z; z >= pos1.z; z--) {
    let volume: BlockVolume = new BlockVolume(vector3(pos1.x, pos1.y, z), vector3(pos1.x + 1, pos1.y, z));
    overworld.fillBlocks(volume, MinecraftBlockTypes.RedstoneBlock);
    await delay(4);
  }
  // let pos2 = vector3(2468, 9, 105);
}

let triggerAlarm = () => runEntityEventOnTag("alarmLights", "cc:add_rotating");

let resetAlarm = () => runEntityEventOnTag("alarmLights", "cc:remove_rotating");

world.afterEvents.worldInitialize.subscribe(() => {
  mindKeeper.registerStore(CURRENT_LEVEL, StoreType.number);
  mindKeeper.registerStore(AGENT_ID, StoreType.string);
  // mindKeeper.set("ShowTriggers", true);
  triggerManager.RegisterStores();
  mindKeeper.registerToWorld();

  triggerManager.Load();

  doNothingStupidTSIWantFilesNotAGiantAssMainFile();
  // loadCommands();
});

world.beforeEvents.itemUseOn.subscribe((event) => {
  trailMaker.OnItemUse(event);
  triggerManager.OnItemUse(event);
});

async function randomExplosions() {
  let pos1 = vector3(2465, 10, 82);
  let pos2 = vector3(2471, 18, 90);

  for (let i = 0; i < 5; i++) {
    let pos = vector3(
      pos1.x + Math.random() * (pos2.x - pos1.x),
      pos1.y + Math.random() * (pos2.y - pos1.y),
      pos1.z + Math.random() * (pos2.z - pos1.z)
    );
    //create a particle
    system.run(() => {
      spawnParticle(pos, "minecraft:huge_explosion_emitter", new MolangVariableMap());
    });
  }
}

let originalDoorPos = vector3(2465, 10, 82);
let closedDoorPos = vector3(2465, 10, 82);
let openDoorPos = vector3(2465, 10, 82);

let doorSize = vector3(7, 7, 7);
///clone 2463 -10 81 2470 -3 87 2463 10 81
///clone 2463 -30 81 2470 -23 87 2463 10 81

function blowUpDoor() {
  if (DEVELOPER_MODE) {
    world.sendMessage("Blowing up door");
  }
  world.getDimension("overworld").runCommand("/clone 2463 -10 81 2470 -3 87 2463 10 81");
}
function restoreDoor() {
  if (DEVELOPER_MODE) {
    world.sendMessage("Restoring door");
  }
  world.getDimension("overworld").runCommandAsync("/clone 2463 -30 81 2470 -23 87 2463 10 81");
}

world.afterEvents.chatSend.subscribe(async (event: ChatSendAfterEvent) => {
  const command = event.message.split(" ")[0];

  trailMaker.OnChat(event);
  mindKeeper.chatCommands(event);
  triggerManager.OnChat(event);

  if (command === "!reset") {
    world.sendMessage("Resetting");

    mindKeeper.set(CURRENT_LEVEL, 0);

    restoreDoor();
    resetAirLock();
    restoreGlassWindow(true);

    resetLightPath();

    levelIntro.reset();
    mission1part1.reset();
    mission1part2.reset();
    mission1part3.reset();

    mission2part1.reset();
    mission2part2.reset();

    //Reset the combination to a random squence

    resetAlarm();
    deactivateAirlockLights();

    setNPCDialog("chanel1", "chanel_greeting_1");
    setNPCDialog("bilal1", "bilal_greeting_1");
    setNPCDialog("suki1", "suki_greeting_1");

    fillWall(bilalBlock, MinecraftBlockTypes.Barrier);

    isIntroToLevel1Sqeuence = false;
  }
});

system.afterEvents.scriptEventReceive.subscribe((event) => {
  if (event.id == "cc:getId") {
    let id = event.message;
    if (DEVELOPER_MODE) {
      world.sendMessage("Script got the id " + id);
    }
    mindKeeper.set(AGENT_ID, id);
  }

  if (event.id == "cc:startIntro") {
    if (mindKeeper.get(CURRENT_LEVEL) == 2) {
      mindKeeper.increment(CURRENT_LEVEL);
    } else {
      if (DEVELOPER_MODE) {
        world.sendMessage("Intro already started?");
      }
    }
  }

  if (event.id == "cc:startTemple") {
    if (mindKeeper.get(CURRENT_LEVEL) == 7) {
      mindKeeper.increment(CURRENT_LEVEL);
      fillWall(bilalBlock, MinecraftBlockTypes.Air);
    }
  }

  if (event.id == "cc:startLevel1") {
    if (mindKeeper.get(CURRENT_LEVEL) == 8) {
      mindKeeper.increment(CURRENT_LEVEL);
    }
  }

  if (event.id == "cc:afterLevel1") {
  }
});

Commands.register(PREFIX, "fien", (arg) => {
  system.run(() => {
    arg.player.teleport(vector3(2468, 11, 114));
  });
});

Commands.register(PREFIX, "openDoor", () => openDoor());
Commands.register(PREFIX, "closeDoor", () => closeDoor());

Commands.register(PREFIX, "info", (arg) => {
  world.sendMessage("-----------------");
  world.sendMessage("Current level: " + mindKeeper.get(CURRENT_LEVEL));
  world.sendMessage("Agent ID: " + mindKeeper.get(AGENT_ID));
  world.sendMessage("Engine Version: 1.0.3");
  world.sendMessage("Engine is running, duh :)");
  world.sendMessage("Active players: " + world.getPlayers().length);
  world.sendMessage("Current dimension: " + arg.player.dimension.id);
  world.sendMessage("Current position: " + Vector3ToFancyString(arg.player.location));
  world.sendMessage("-----------------");
});

Commands.register(PREFIX, "kboom", (arg) => {
  (async () => {
    for (let i = 0; i < 10; i++) {
      randomExplosions();
      await delay(1);
    }
    blowUpDoor();
  })();
});

let openDoor = () => {
  runEntityEventOnTag("door", "cc:add_open");
  if (DEVELOPER_MODE) {
    world.sendMessage("Opening door");
  }
};
let closeDoor = () => {
  runEntityEventOnTag("door", "cc:remove_open");
  if (DEVELOPER_MODE) {
    world.sendMessage("Closing door");
  }
};

Commands.register(PREFIX, "restore", (arg) => {
  restoreDoor();
});

//#region Airlock

enum GlassWindowState {
  Broken,
  Fixed,
}

enum AirLockState {
  Open,
  Closed,
}

enum AirLockChamberState {
  Full,
  Empty,
}

enum ExplosiveDoorState {
  Closed,
  Open,
}

let glassWindowState: GlassWindowState = GlassWindowState.Fixed;
let explosiveDoorState: ExplosiveDoorState = ExplosiveDoorState.Closed;

const doorRoot: Vector3 = vector3(2465, 11, 94);

const glassWindowClonePos: ClonePos = {
  point1: vector3(2465, 14, 94),
  point2: vector3(2470, 11, 94),
};

const glassBreakingFrames: ClonePos[] = [
  ///fill 2465 7 94 2470 4 94
  {
    point1: vector3(2465, 7, 94),
    point2: vector3(2470, 4, 94),
  },
  //fill 2465 0 94 2470 -3 94
  {
    point1: vector3(2465, 0, 94),
    point2: vector3(2470, -3, 94),
  },
  {
    //fill 2465 -7 94 2470 -10 94
    point1: vector3(2465, -7, 94),
    point2: vector3(2470, -10, 94),
  },
  //fill 2465 -14 94 2470 -17 94
  {
    point1: vector3(2465, -14, 94),
    point2: vector3(2470, -17, 94),
  },
  //fill 2465 -21 94 2470 -24 94
  {
    point1: vector3(2465, -21, 94),
    point2: vector3(2470, -24, 94),
  },
];

const FrontAirLockOpenFrames: ClonePos[] = [
  {
    point1: vector3(2476, 9, 94),
    point2: vector3(2474, 7, 94),
  },
  {
    point1: vector3(2476, 5, 94),
    point2: vector3(2474, 3, 94),
  },
  {
    point1: vector3(2476, 1, 94),
    point2: vector3(2474, -1, 94),
  },
  {
    point1: vector3(2476, -3, 94),
    point2: vector3(2474, -5, 94),
  },
];

const BackAirLockOpenFrames: ClonePos[] = [
  {
    point1: vector3(2472, 9, 100),
    point2: vector3(2472, 7, 102),
  },
  {
    point1: vector3(2472, 5, 100),
    point2: vector3(2472, 3, 102),
  },
  {
    point1: vector3(2472, 1, 100),
    point2: vector3(2472, -1, 102),
  },
  {
    point1: vector3(2472, -3, 100),
    point2: vector3(2472, -5, 102),
  },
];

const AirLockWaterFrames: ClonePos[] = [
  {
    point1: vector3(2473, 11, 95),
    point2: vector3(2477, 11, 103),
  },
  {
    point1: vector3(2473, 12, 95),
    point2: vector3(2477, 12, 103),
  },
  {
    point1: vector3(2473, 13, 95),
    point2: vector3(2477, 13, 103),
  },
  {
    point1: vector3(2473, 14, 95),
    point2: vector3(2477, 14, 103),
  },
];

Commands.register(PREFIX, "breakGlass", (arg) => {
  breakGlassWindow();
  glassWindowState = GlassWindowState.Broken;
});
Commands.register(PREFIX, "restoreGlass", (arg) => {
  restoreGlassWindow();
  glassWindowState = GlassWindowState.Fixed;
});

async function breakGlassWindow() {
  playAnimation(glassBreakingFrames, 5, false, doorRoot);
  glassWindowState = GlassWindowState.Broken;
}
async function restoreGlassWindow(force: boolean = false) {
  playAnimation(glassBreakingFrames, 5, true, doorRoot, force);
}

async function CycleAirLockWater(fill: boolean, delayTime: number) {
  if (animationPlaying) {
    world.sendMessage("Animation already playing");
    return;
  }
  setAmimationPlaying(true);
  let frameCount = AirLockWaterFrames.length;

  for (let i = 0; i < frameCount; i++) {
    let frame = fill ? AirLockWaterFrames[i] : AirLockWaterFrames[frameCount - i - 1];
    let blockToFill = fill ? "minecraft:water" : "minecraft:air";
    await world
      .getDimension("overworld")
      .runCommandAsync(
        `/fill ${Vector3ToCommandString(frame.point1)} ${Vector3ToCommandString(frame.point2)} ${blockToFill}`
      );
    await delay(delayTime);
  }
  setAmimationPlaying(false);
}

let frontAirlockState: AirLockState = AirLockState.Closed;
let backAirlockState: AirLockState = AirLockState.Closed;

let chamberState: AirLockChamberState = AirLockChamberState.Full;

//The airlock sequence is
//Player enters FrontAirLockOutside
//Front door opens
triggerManager.RegisterFunctionTrigger("FrontAirLockOutside", async (event) => {
  if (chamberState == AirLockChamberState.Empty) {
    if (DEVELOPER_MODE) {
      world.sendMessage("Filling airlock");
    }
    await FillAirlockWater();
    chamberState = AirLockChamberState.Full;
  }

  if (frontAirlockState == AirLockState.Closed) {
    await PlayFrontDoorOpenAnimation();
    frontAirlockState = AirLockState.Open;
  }
});

triggerManager.RegisterFunctionTrigger("TempleFrontDoor", async (event) => {
  if (mindKeeper.get(CURRENT_LEVEL) == 8) {
    mindKeeper.increment(CURRENT_LEVEL);
    closeDoor();
  }

  if (mindKeeper.get(CURRENT_LEVEL) == 15) {
    openDoor();
  }
});

//Player enters FrontAirLockInside
//Front door closes
//Water drains
//Back door opens
triggerManager.RegisterFunctionTrigger("FrontAirLockInside", async (event) => {
  if (frontAirlockState == AirLockState.Open) {
    await PlayFrontDoorCloseAnimation();
    frontAirlockState = AirLockState.Closed;
  }
  if (chamberState == AirLockChamberState.Full) {
    activateAirlockLights();
    await delay(50);
    await EmptyAirlockWater();
    deactivateAirlockLights();
    chamberState = AirLockChamberState.Empty;
  }

  if (backAirlockState == AirLockState.Closed) {
    await delay(7);
    await PlayBackDoorOpenAnimation();
    backAirlockState = AirLockState.Open;
  }
});

//Player enters BackAirLockOutside
//Back door closes

triggerManager.RegisterFunctionTrigger("BackAirLockOutside", async (event) => {
  if (backAirlockState == AirLockState.Open) {
    world.sendMessage("Closing back door");
    await PlayBackDoorCloseAnimation();
    backAirlockState = AirLockState.Closed;
  }
});

//#endregion Airlock

let activateAirlockLights = () => runEntityEventOnTag("airlockLights", "cc:add_rotating");
let deactivateAirlockLights = () => runEntityEventOnTag("airlockLights", "cc:remove_rotating");

function resetAirLock() {
  SetFrontDoorClosed();
  SetBackDoorClosed();
  FillAirlockWater();

  frontAirlockState = AirLockState.Closed;
  backAirlockState = AirLockState.Closed;
  chamberState = AirLockChamberState.Full;
  glassWindowState = GlassWindowState.Fixed;
}

Commands.register(PREFIX, "resetAirlock", (arg) => resetAirLock);

let frontDoorRoot: Vector3 = vector3(2474, 11, 94);
let backDoorRoot: Vector3 = vector3(2472, 11, 100);

const frameDuration = 7;

let SetFrontDoorClosed = () => SetFrame(FrontAirLockOpenFrames[0], frontDoorRoot);
let SetFrontDoorOpen = () => SetFrame(FrontAirLockOpenFrames[FrontAirLockOpenFrames.length - 1], frontDoorRoot);
let PlayFrontDoorOpenAnimation = async () => {
  playAnimation(FrontAirLockOpenFrames, frameDuration, false, frontDoorRoot);
  if (mindKeeper.get(CURRENT_LEVEL) == 0) {
    mindKeeper.increment(CURRENT_LEVEL);
  }
};
let PlayFrontDoorCloseAnimation = async () => playAnimation(FrontAirLockOpenFrames, frameDuration, true, frontDoorRoot);

let SetBackDoorClosed = () => SetFrame(BackAirLockOpenFrames[0], backDoorRoot);
let SetBackDoorOpen = () => SetFrame(BackAirLockOpenFrames[BackAirLockOpenFrames.length - 1], backDoorRoot);
let PlayBackDoorOpenAnimation = async () => playAnimation(BackAirLockOpenFrames, frameDuration, false, backDoorRoot);
let PlayBackDoorCloseAnimation = async () => playAnimation(BackAirLockOpenFrames, frameDuration, true, backDoorRoot);

let FillAirlockWater = async () => CycleAirLockWater(true, frameDuration / 1.5);
let EmptyAirlockWater = async () => {
  await CycleAirLockWater(false, frameDuration / 1.5);
  if (mindKeeper.get(CURRENT_LEVEL) == 1) {
    mindKeeper.increment(CURRENT_LEVEL);
  }
};

triggerManager.RegisterFunctionTrigger("openFrontDoor", (event) => PlayFrontDoorOpenAnimation());
triggerManager.RegisterFunctionTrigger("closeFrontDoor", (event) => PlayFrontDoorCloseAnimation());

triggerManager.RegisterFunctionTrigger("openBackDoor", (event) => PlayBackDoorOpenAnimation());
triggerManager.RegisterFunctionTrigger("closeBackDoor", (event) => PlayBackDoorCloseAnimation());

triggerManager.RegisterFunctionTrigger("fillAirlock", (event) => FillAirlockWater());
triggerManager.RegisterFunctionTrigger("emptyAirlock", (event) => EmptyAirlockWater());
