const DEBUG_MODE = false;

// Search
const { rerenderSearch, onSearch, onRandomName } = (() => {
  let results;
  if (DEBUG_MODE) {
    // set of some pokemon with interesting edge cases
    starting = [
      "latias",
      "wailmer",
      "luxray",
      "seaking",
      "blastoise",
      "oranguru",
      "victreebel",
      "grimmsnarl",
      "rapidash",
      "blacephalon",
      "crustle",
      "rolycoly",
      "lucario",
      "abomasnow",
      "typhlosion",
      "chinchou",
    ];
    starting.forEach(
      (x) => (clusterToggles[`${x}-search-pkmn-expand-toggle`] = true)
    );
    results = pokemonData.filter((p) => starting.includes(p.name));
  }
  const pokemonLookup = new Fuse(pokemonData, { keys: ["name"] });

  const rerenderSearch = (state) => {
    const node = document.getElementById("search-list");
    node.innerHTML = "";
    results?.forEach((pkmn) => {
      const li = document.createElement("li");
      li.innerHTML = renderPokemonTile(
        "search",
        pkmn.name,
        pkmn[state.space],
        currentScores[pkmn.name][state.space],
        state.useCluster
          ? currentBestClusterIndices[pkmn.name][state.space]
          : -1
      );
      node.appendChild(li);
    });
  };

  const onSearch = (state, newSearch) => {
    results = pokemonLookup
      .search(newSearch, { limit: 10 })
      .map(({ item }) => item);
    rerenderSearch(state);
  };

  const onRandomName = (state) => {
    results = Array.from(
      { length: 10 },
      () => pokemonData[Math.floor(Math.random() * pokemonData.length)]
    );
    rerenderSearch(state);
  };

  return { rerenderSearch, onSearch, onRandomName };
})();

// Sorting/Metrics
const scaleOptions = {
  none: () => [1, 1, 1],
  direct: (data) => data.clusters.map((c) => c.size / data.total.size),
  inverse: (data) => data.clusters.map((c) => data.total.size / c.size),
  size: (data) => [data.total.size, data.total.size, data.total.size],
  inverseSize: (data) => {
    const c = 1 / data.total.size;
    return [c, c, c];
  },
};

const scaleOptionsDisplay = {
  none: () => ["", "", ""],
  direct: (arg) => [
    `\\frac{\\left|${arg}\\right|}{\\left|P\\right|}`,
    "\\left(",
    "\\right)",
  ],
  inverse: (arg) => [
    `\\frac{\\left|P\\right|}{\\left|${arg}\\right|}`,
    "\\left(",
    "\\right)",
  ],
  size: () => ["\\left|P\\right|", "\\left(", "\\right)"],
  inverseSize: () => ["\\frac{1}{\\left|P\\right|}", "\\left(", "\\right)"],
};

const sortOrders = {
  max: (a, b) => b - a,
  min: (a, b) => a - b,
};

const onControlsChanged = (state) => {
  // Update q vector display
  document.getElementById("qvec").innerHTML =
    TeXZilla.toMathMLString(String.raw`
    \vec{q}_{\text{${state.space === "rgb" ? "RGB" : "Jab"}}} 
    = \left(\text{${state.target[state.space].vector
      .map((c) => c.toFixed(state.space === "rgb" ? 0 : 2))
      .join(", ")}}\right)
  `);
  // Update metric displays
  document.getElementById("collapsed-sort").innerHTML =
    state.sortOrder === "min" ? "<" : ">";
  document.getElementById("collapsed-fn-name").innerHTML =
    TeXZilla.toMathMLString(metrics[state.sortMetric].displayName);
  document.getElementById("collapsed-cluster-sort").innerHTML = state.useCluster
    ? state.clusterSettings.sortOrder === "min"
      ? "<"
      : ">"
    : "";
  document.getElementById("collapsed-cluster").innerHTML = state.useCluster
    ? TeXZilla.toMathMLString(
        metrics[state.clusterSettings.sortMetric].displayName
      )
    : "";
  document.getElementById("collapsed-cluster-scale").innerHTML =
    state.useCluster
      ? TeXZilla.toMathMLString(
          scaleOptionsDisplay[state.clusterSettings.scaleOption]("K")[0] || "1"
        )
      : "";
  document.getElementById("collapsed-cluster-mult-total").innerHTML =
    state.useCluster
      ? TeXZilla.toMathMLString(
          state.clusterSettings.multWithTotal
            ? metrics[state.sortMetric].displayName
            : "1"
        )
      : "";
  document.getElementById("collapsed-scale").innerHTML = state.useCluster
    ? TeXZilla.toMathMLString(
        scaleOptionsDisplay[state.scaleOption]("K")[0] || "1"
      )
    : "";
  document.getElementById("metric-display").innerHTML =
    TeXZilla.toMathMLString(`
    ${metrics[state.sortMetric].displayName}\\left(P\\right) = ${metrics[
      state.sortMetric
    ].displayBody("P", state.space)}
  `);
  let arg;
  if (state.useCluster) {
    arg = "K\\left(P\\right)";
    const [clusterScaleDisplay, clusterScaleDisplayL, clusterScaleDisplayR] =
      scaleOptionsDisplay[state.clusterSettings.scaleOption]("P_i");
    document.getElementById("cluster-metric-display").innerHTML =
      TeXZilla.toMathMLString(`
      K\\left(P\\right) = \\arg\\${state.clusterSettings.sortOrder}_{P_i}\\left[
        ${clusterScaleDisplay} ${clusterScaleDisplayL} ${metrics[
        state.clusterSettings.sortMetric
      ].displayBody("P_i", state.space)} ${clusterScaleDisplayR}
      \\right]
    `);
  } else {
    arg = "P";
    document.getElementById("cluster-metric-display").innerHTML = "";
  }
  const [scaleDisplay, scaleDisplayL, scaleDisplayR] = state.useCluster
    ? scaleOptionsDisplay[state.scaleOption](arg)
    : ["", "", ""];
  document.getElementById("final-metric-display").innerHTML =
    TeXZilla.toMathMLString(`
    \\begin{aligned}
    &\\${state.sortOrder}_{P}\\left[
      ${scaleDisplay} ${
      metrics[state.sortMetric].displayName
    }\\left(${arg}\\right)
      ${
        state.useCluster && state.clusterSettings.multWithTotal
          ? `${metrics[state.sortMetric].displayName}\\left(P\\right)`
          : ""
      }
    \\right]\\\\
    = 
    &\\${state.sortOrder}_{P}\\left[
      \\begin{aligned}
        ${scaleDisplay ? `&${scaleDisplay}\\\\ *` : ""}
        &${scaleDisplayL} ${metrics[state.sortMetric].displayBody(
      arg,
      state.space
    )} ${scaleDisplayR} 
        ${
          state.useCluster && state.clusterSettings.multWithTotal
            ? `\\\\ \n *&\\left(${metrics[state.sortMetric].displayBody(
                "P",
                state.space
              )}\\right)`
            : ""
        }
      \\end{aligned}
    \\right]
    \\end{aligned}
  `);

  // Update the current best
  const resultList = document.getElementById("result-list");
  resultList.innerHTML = "";
  getBest(
    state.number,
    state.space,
    state.useCluster
      ? {
          sortMetric: state.clusterSettings.sortMetric,
          sortOrder: sortOrders[state.clusterSettings.sortOrder],
          scaleOption: scaleOptions[state.clusterSettings.scaleOption],
          multWithTotal: state.clusterSettings.multWithTotal,
        }
      : null,
    {
      sortMetric: state.sortMetric,
      sortOrder: sortOrders[state.sortOrder],
      scaleOption: scaleOptions[state.scaleOption],
    }
  ).forEach((pkmn) => {
    const li = document.createElement("li");
    li.innerHTML = renderPokemonTile(
      "results",
      pkmn.name,
      pkmn[state.space],
      currentScores[pkmn.name][state.space],
      state.useCluster ? currentBestClusterIndices[pkmn.name][state.space] : -1
    );
    resultList.appendChild(li);
  });

  // Also rerender search just in case
  rerenderSearch(state);
};

// Color
const onColorChanged = (state, newValue) => {
  const colorInput = "#" + (newValue?.replace("#", "") ?? "FFFFFF");

  if (colorInput.length !== 7) {
    return;
  }

  const rgb = d3.color(colorInput);
  if (!rgb) {
    return;
  }
  const { J, a, b } = d3.jab(rgb);

  if (state.target) {
    const lastColor = document.createElement("div");
    lastColor.innerHTML = state.target.rgb.hex;
    lastColor.style = `color: ${getContrastingTextColor(
      state.target.rgb.hex
    )}; background-color: ${
      state.target.rgb.hex
    }; width: 6em; text-align: center;`;
    document.getElementById("prev-colors").prepend(lastColor);
  }

  state.target = {
    jab: buildVectorData([J, a, b], jab2hue, jab2lit, jab2chroma, jab2hex),
    rgb: buildVectorData(
      [rgb.r, rgb.g, rgb.b],
      rgb2hue,
      rgb2lit,
      rgb2chroma,
      rgb2hex
    ),
  };

  const rootElem = document.querySelector(":root");
  rootElem.style.setProperty("--background", state.target.rgb.hex);
  rootElem.style.setProperty(
    "--highlight",
    getContrastingTextColor(state.target.rgb.hex)
  );

  rescoreAll(state.target);
  onControlsChanged(state);
};

const onRandomColor = (state) => {
  const color = d3
    .hsl(Math.random() * 360, Math.random(), Math.random())
    .formatHex();
  document.getElementById("color-input").value = color;
  onColorChanged(state, color); // triggers rescore
};
