import { awaitEvent, emitEvent, listenTo } from "./events.js";
import { checkEquivalence, findFullEntry, stripEntryToMinimums } from "./ncpf-utils.js";
import {
  clearPlanner,
  getPlannerDom,
  getPlannerMultiblockSize,
  getPlannerMultiblockType,
  loadPlanner,
  readPlannerCellData
} from "./planner.js";
import { getAtlasIndexFromMetadata, getAtlasMetadata } from "./atlas.js";
import { getSolverMetadata, resetSolver } from "./solver.js";
import { registerLoadCallback } from "./main.js";
import { DevError, UserError } from "./errors.js";

registerLoadCallback(() => {
  listenTo("file:load", () => showOpenFileDialog(({ data, filename }) => loadFile(data, filename)));
  listenTo("file:new", () => {
    clearPlanner();
    resetSolver();
  });
  listenTo("file:save", () => saveFile());
});

export function showOpenFileDialog(callback) {
  const input = document.createElement("input");
  input.type = "file";
  input.accept = ".ncpf.json";
  input.addEventListener("change", async () => {
    const basename = input.files[0].name.split(/[/\\]/).pop();
    const filename = basename.replace(/\.json$/, "").replace(/\.ncpf$/, "");
    const data = await new Promise(resolve => {
      const reader = new FileReader();
      reader.addEventListener("load", () => {
        resolve(JSON.parse(reader.result));
      });
      reader.readAsText(input.files[0]);
    });
    callback({ data, filename });
  });
  input.click();
}

export async function loadExampleFile(filename) {
  const data = await fetch(`./examples/${filename}.ncpf.json`)
      .then(data => data.json());
  return loadFile(data);
  // return loadFile(data, filename);
}

export async function loadFile(data, name) {
  if (!data.designs) throw new UserError("File must contain a list of designs!");
  if (data.designs.length === 0) throw new UserError("Fils must contain at least one design!");

  emitEvent("fileLoad:start");

  let design = data.designs[0];
  if (data.designs.length > 1) {
    const prepChoice = awaitEvent("fileLoad:chooseDesign:choice");
    emitEvent("fileLoad:chooseDesign", data);
    const res = await prepChoice;
    design = data.designs[res.detail];
  }

  if (!design) throw new DevError("Must choose a design");

  const blocks = data.configuration[design.type].blocks
      .map(block => getAtlasIndexFromMetadata(block, design.type, "blocks"));

  const designWithAtlas = design.design.map(a => a.map(b => b.map(c => c === -1 ? -1 : blocks[c])));

  loadPlanner(design.dimensions, design.type, designWithAtlas);
  resetSolver(design, data.configuration[design.type]);

  if (name) {
    getPlannerDom().dataset.name = name;
  }

  emitEvent("fileLoad:end");

  console.log("load file", data);
}

export async function saveFile(name = undefined) {
  if (!name) {
    name = getPlannerDom().dataset.name;
  }

  if (!name) {
    emitEvent("fileSave:nameDesign");
    const nameEvent = await awaitEvent("fileSave:nameDesign:named");
    name = nameEvent.detail;
  }

  if (!name) {
    return;
  }

  const multiblockType = getPlannerMultiblockType();

  const designAsCellData = readPlannerCellData();
  const designAsAtlas = designAsCellData
      .map(xPlane => xPlane.map(yRow => yRow.map(c => Number(c.tile))));
  const designBlocksAtlasMap = Object.fromEntries(
      [...new Set(designAsAtlas.flat(3))]
          .filter(v => v !== -1)
          .sort((a, b) => a - b)
          .map((ai, di) => [ ai, { ...getAtlasMetadata(ai), designIndex: di } ])
  );
  const designAsBlocks = designAsAtlas
      .map(xPlane => xPlane
          .map(yRow => yRow.map(atlasIndex => designBlocksAtlasMap[atlasIndex]))
      );
  const designAsBlockIndices = designAsBlocks
      .map(xPlane => xPlane.map(yRow => yRow.map(b => b?.designIndex ?? -1)));

  const minimizedBlocks = Object.entries(designBlocksAtlasMap)
      .sort(([a], [b]) => a - b)
      .map(([, m]) => stripEntryToMinimums(m));

  const blockRecipes = designAsBlocks
      .map((xPlane, x) => xPlane
          .map((yRow, y) => yRow
              .map((block, z) => {
                if (!block) return;
                const fullBlock = findFullEntry(
                    "blocks",
                    block,
                    { configurationHint: multiblockType },
                );
                if (!fullBlock || !fullBlock.modules || !("ncpf:block_recipes" in fullBlock.modules)) return;

                const designIndex = designBlocksAtlasMap[Number(designAsCellData[x][y][z].tile)].designIndex;
                const solverMetadata = getSolverMetadata(designAsCellData[x][y][z].solverMetadata);
                const minimizedBlock = minimizedBlocks[designIndex];
                let recipeIndex = -1;

                if (!solverMetadata) return -1;

                if (multiblockType === "nuclearcraft:overhaul_sfr") {
                  if (!minimizedBlock.modules) {
                    minimizedBlock.modules = {};
                  }
                  if (!minimizedBlock.modules["ncpf:block_recipes"]) {
                    minimizedBlock.modules["ncpf:block_recipes"] = {
                      recipes: [],
                    };
                  }
                  const recipes = minimizedBlock.modules["ncpf:block_recipes"].recipes;
                  const recipe = solverMetadata["block_recipe"];
                  recipeIndex = recipes.findIndex(r => checkEquivalence(r, recipe));
                  if (recipeIndex === -1) {
                    recipes.push(stripEntryToMinimums(recipe));
                    recipeIndex = recipes.length - 1;
                  }
                }

                return recipeIndex;
              })
              .filter(b => b != null)
          )
          .filter(r => r.length > 0)
      )
      .filter(p => p.length > 0);

  let fileData = null;

  if (multiblockType === "nuclearcraft:overhaul_sfr") {
    const coolantRecipes = [];
    let coolantRecipeIndex;
    const plannerMetadata = getSolverMetadata(getPlannerDom());

    coolantRecipes.push(stripEntryToMinimums(plannerMetadata.coolant_recipe));
    coolantRecipeIndex = 0;

    fileData = {
      addons: [],
      configuration: {
        [multiblockType]: {
          blocks: minimizedBlocks,
          coolant_recipes: coolantRecipes,
        },
      },
      designs: [
        {
          block_recipes: blockRecipes,
          coolant_recipe: coolantRecipeIndex,
          design: designAsBlockIndices,
          dimensions: getPlannerMultiblockSize(),
          type: multiblockType,
        },
      ],
      version: 1,
    };
  }

  console.log(`save file (${name})`, fileData);

  if (fileData) {
    const blob = new Blob([ JSON.stringify(fileData) ], {
      type: "application/json",
    });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `${name}.ncpf.json`;
    a.click();
  }
}
