import { getAtlasIndexFromMetadata, getAtlasMetadata, makeAtlasImage } from "./atlas.js";
import { DomList } from "./dom-list.js";
import { getElements } from "./dom-utils.js";
import { emitEvent, listenTo } from "./events.js";
import { initialize3dArray } from "./js-utils.js";
import { recall, save } from "./storage.js";
import { registerLoadCallback } from "./main.js";
import { getBlockPickerActiveItem, getBlockPickerRecipe } from "./block-picker.js";
import { deleteSolverMetadata, getSolverMetadata, setSolverMetadata } from "./solver.js";
import { retainTooltipHover } from "./tooltip.js";
import { checkEquivalence, findFullEntry, findFullEntryAnywhere, lookupFromMetadata } from "./ncpf-utils.js";

const zoomOptions = [ 0.5, 1, 2, 3, 4, 5 ];
const zoomDefault = 2;
const zoomStorageKey = "zoom-size";

const casingStorageKey = "casing";
const casingDefault = false; // hidden by default

const defaultMultiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y)
const defaultMultiblockType = "nuclearcraft:overhaul_sfr";
export const maxMultiblockSize = {
  ["nuclearcraft:overhaul_sfr"]: [26, 26, 26],
}
export const minMultiblockSize = {
  ["nuclearcraft:overhaul_sfr"]: [3, 3, 3],
}

let suppressHover = false;
let multiblockSize = [...defaultMultiblockSize];
let multiblockType = defaultMultiblockType;

registerLoadCallback(() => {
  listenTo("config:end", buildPlanner);

  const casingShow = recall(casingStorageKey, casingDefault);
  document.body.dataset.casing = casingShow ? "show" : "hide";
  getElements("toggle", "planner:toggle-casing")
      .forEach(e => e.dataset.toggled = casingShow.toString());
  listenTo("planner:toggle-casing", e => {
    save(casingStorageKey, e.detail.state);
    document.body.dataset.casing = e.detail.state ? "show" : "hide";
  });

  const zoomLevel = recall(zoomStorageKey, zoomDefault);
  document.getElementById("planner").style
      .setProperty("--scale-factor", zoomLevel);
  document.querySelectorAll('select[data-input="planner:zoom"]').forEach(elem => {
    for (const op of zoomOptions) {
      const option = document.createElement("option");
      option.value = op.toString(10);
      option.innerText = `${op * 100}%`;
      option.selected = zoomLevel === op;
      elem.appendChild(option);
    }
  });
  listenTo("planner:zoom:change", e => {
    const value = Number(e.detail.currentTarget.value);
    if (zoomOptions.includes(value)) {
      emitEvent("planner:zoom:set", value);
    } else {
      console.warn("bad planner zoom value", e);
    }
  });
  listenTo(["planner:zoom:+", "planner:zoom:-"], e => {
    const zoomIn = e.type === "ncp:planner:zoom:+";
    const currentScale = Number(document.getElementById("planner").style
        .getPropertyValue("--scale-factor") ?? zoomDefault);
    const currentIndex = zoomOptions.indexOf(currentScale);
    if (zoomIn && currentIndex < zoomOptions.length - 1) {
      emitEvent("planner:zoom:set", zoomOptions[currentIndex + 1]);
    }
    if (!zoomIn && currentIndex > 0) {
      emitEvent("planner:zoom:set", zoomOptions[currentIndex - 1]);
    }
  });
  listenTo("planner:zoom:set", e => {
    const scale = e.detail;
    const scaleIndex = zoomOptions.indexOf(scale);
    document.getElementById("planner").style
        .setProperty("--scale-factor", scale.toString(10));

    document.querySelectorAll('select[data-input="planner:zoom"]')
        .forEach(elem => elem.value = scale);

    getElements("button", "planner:zoom:+")
        .forEach(e => e.disabled = scaleIndex >= zoomOptions.length - 1);
    getElements("button", "planner:zoom:-")
        .forEach(e => e.disabled = scaleIndex <= 0);

    save(zoomStorageKey, scale);
  });

  listenTo("block-picker:selection", clearSelectedCells);

  listenTo("dev:suppress-hover", () => {
    suppressHover = !suppressHover;
    console.log("Suppressing hover:", suppressHover);
  });
});

function buildPlanner(textures) {
  // handle case when 1st argument is event
  if (!Array.isArray(textures)) textures = undefined;

  const planner = document.getElementById("planner");

  const list = new DomList(planner);
  list.clear();

  for (let y = 0; y < multiblockSize[1]; y++) {
    const item = list.makeItem();
    createPlannerPlane(item, y, textures);
    list.addItem(item);
  }
}
export function createPlannerPlane(dom, y, textures) {
  dom.style.setProperty("--width", multiblockSize[0]);
  dom.dataset.y = y.toString();
  if (y === 0 || y === multiblockSize[1] - 1) {
    dom.dataset.casing = "";
  }
  dom.addEventListener("mousemove", retainTooltipHover);
  const grid = new DomList(dom);
  grid.clear();
  for (let z = 0; z < multiblockSize[2]; z++) {
    for (let x = 0; x < multiblockSize[0]; x++) {
      const texture = textures ? textures[x][y][z] : -1;
      if (textures && !texture) {
        console.log(x, y, z, textures);
      }
      const cell = grid.makeItem();
      createPlannerCell(cell, x, y, z, texture);
      grid.addItem(cell);
    }
  }
}
export function createPlannerCell(dom, x, y, z, texture) {
  dom.dataset.x = x.toString();
  dom.dataset.y = y.toString();
  dom.dataset.z = z.toString();
  dom.dataset.tile = texture.toString();
  if (x === 0 || x === multiblockSize[0] - 1
      || y === 0 || y === multiblockSize[1] - 1
      || z === 0 || z === multiblockSize[2] - 1) {
    dom.dataset.casing = "";
  }
  dom.dataset.tooltip = "auto";
  dom.appendChild(makeAtlasImage(texture));
  dom.addEventListener("mouseenter", cellHoverEvent);
  dom.addEventListener("mouseleave", cellHoverEvent);
  dom.addEventListener("mousedown", cellClickEvent);
  dom.addEventListener("mouseenter", cellClickEvent);
  dom.addEventListener("mouseleave", cellClickEvent);
  dom.addEventListener("contextmenu", cellClickEvent);
}

function cellHoverEvent(e) {
  if (suppressHover) return;
  if (getBlockPickerActiveItem() === -1) return;
  if (document.body.dataset.resizing) return;

  const cells = document.querySelectorAll("#planner [data-x]");
  cells.forEach(c => {
    delete c.dataset.highlight;
    delete c.dataset.highlightDistance;
  });
  if (e.type === "mouseenter") {
    cells.forEach(c => {
      if (c.dataset.y === e.target.dataset.y && c.dataset.z === e.target.dataset.z) {
        c.dataset.highlight = "x";
        c.dataset.highlightDistance = Math.abs(c.dataset.x - e.target.dataset.x).toString(10);
      }
      if (c.dataset.x === e.target.dataset.x && c.dataset.z === e.target.dataset.z) {
        c.dataset.highlight = "y";
        c.dataset.highlightDistance = Math.abs(c.dataset.y - e.target.dataset.y).toString(10);
      }
      if (c.dataset.x === e.target.dataset.x && c.dataset.y === e.target.dataset.y) {
        c.dataset.highlight = "z";
        c.dataset.highlightDistance = Math.abs(c.dataset.z - e.target.dataset.z).toString(10);
      }
      if (c === e.target) {
        delete c.dataset.highlight;
      }
    });
  }
}

function cellClickEvent(e) {
  if (e.type === "contextmenu") {
    e.preventDefault();
    return;
  }

  if (document.body.dataset.resizing) {
    return;
  }

  if (e.buttons !== 1 && e.buttons !== 2 && e.type !== "mouseup") {
    return;
  }

  const activeItem = getBlockPickerActiveItem();
  if (activeItem === -1) {
    cellSelectEvent(e);
    return;
  }

  if (e.type === "mousedown") {
    window.addEventListener("mouseup", cellClickEvent);
    emitEvent("planner:drag:start");
    clearSelectedCells();
  }
  if (e.type === "mouseup") {
    window.removeEventListener("mouseup", cellClickEvent);
    emitEvent("planner:drag:end");

    const cells = document.querySelectorAll("#planner [data-x]");
    cells.forEach(c => {
      if (c.dataset.preview) {
        c.dataset.tile = c.dataset.preview;
        emitEvent("planner:cell:change", c);
        delete c.dataset.preview;
      }
      if (c.dataset.previewRemove) {
        delete c.dataset.previewRemove;
        c.replaceChildren(makeAtlasImage(-1));
        c.dataset.tile = (-1).toString(10);
        emitEvent("planner:cell:change", c);
      }
    });
    return;
  }

  if (e.buttons === 2) {
    // remove

    if (e.target.tile === "-1") return;

    e.target.dataset.previewRemove = "yes";
    deleteSolverMetadata(e.target);
  } else if (e.buttons === 1) {
    // add
    if (e.target.dataset.preview === activeItem.toString(10)) return;

    e.target.dataset.preview = activeItem.toString(10);
    e.target.replaceChildren(makeAtlasImage(activeItem));
    const pickerRecipe = getBlockPickerRecipe();
    if (pickerRecipe) {
      setSolverMetadata(e.target, { block_recipe: pickerRecipe });
    }
  }
}
function cellSelectEvent(e) {
  if (e.type === "mousedown" && !e.ctrlKey && e.buttons !== 2) {
    clearSelectedCells();
  }
  if (e.buttons === 2) {
    // remove
    const wasSelected = "selected" in e.target.dataset;
    delete e.target.dataset.selected;
    if (wasSelected) {
      emitEvent("planner:selection:removed", e.target);
    }
  } else if (e.buttons === 1) {
    // add
    const wasSelected = "selected" in e.target.dataset;
    e.target.dataset.selected = "";
    if (!wasSelected) {
      emitEvent("planner:selection:added", e.target);
    }
  }
}
function clearSelectedCells() {
  getSelectedCells()
      .forEach(cell => {
        delete cell.dataset.selected;
        emitEvent("planner:selection:removed", cell);
      });
}
export function getSelectedCells() {
  return [...document.querySelectorAll("#planner [data-selected]")];
}

export function clearPlanner(size = null, type = null) {
  multiblockSize = size ?? [...defaultMultiblockSize];
  multiblockType = type ?? defaultMultiblockType;
  buildPlanner();
}
export function loadPlanner(size, type, textures) {
  multiblockSize = size;
  multiblockType = type;
  buildPlanner(textures);
}

export function readPlannerCellData() {
  const planner = document.getElementById("planner");
  const tiles = initialize3dArray(...multiblockSize, -1);
  const grids = [...planner.children].filter(e => e.dataset.y);
  for (let y = 0; y < multiblockSize[1]; y++) {
    const grid = grids[y];
    const gridTiles = [...grid.children].filter(e => e.dataset.tile);
    for (let z = 0; z < multiblockSize[2]; z++) {
      for (let x = 0; x < multiblockSize[0]; x++) {
        tiles[x][y][z] = { ...gridTiles[x + z * multiblockSize[0]].dataset };
      }
    }
  }
  return tiles;
}
export function getPlannerDom() {
  return document.getElementById("planner");
}
export function getPlannerCellsDom() {
  return document.querySelectorAll("#planner [data-tile]");
}
export function getPlannerCellDom(x, y, z) {
  return document.querySelector(`#planner [data-x="${x}"][data-y="${y}"][data-z="${z}"]`);
}

export const getPlannerMultiblockType = () => multiblockType;
export const getPlannerMultiblockSize = () => multiblockSize;

export function* getCellTooltip(cell) {
  if (cell.dataset.tile === "-1") {
    return;
  }
  const metadata = getAtlasMetadata(cell.dataset.tile)
  const data = lookupFromMetadata(metadata);
  const solverMetadata = getSolverMetadata(cell);

  yield data.modules["plannerator:display_name"].display_name;

  if (solverMetadata) {
    if (solverMetadata.block_recipe) {
      const recipeList = data.modules["ncpf:block_recipes"].recipes;
      const recipe = recipeList.find(r => checkEquivalence(r, solverMetadata.block_recipe));
      yield {
        type: "item",
        name: recipe.modules["plannerator:display_name"].display_name,
        atlasIndex: getAtlasIndexFromMetadata(recipe, multiblockType, "ncpf:block_recipes"),
      }
    }
  }

  yield {
    type: "italics",
    text: metadata.configurationAddon.name,
  };
}
