import { emitEvent } from "./events.js";
import { checkEquivalence } from "./ncpf-utils.js";

export let atlases = [];

async function getTextureSize(data) {
  const texture = data.texture;
  if (!texture) {
    data.size = [ 0, 0 ];
    return;
  }
  await new Promise(resolve => {
    const image = new Image();
    image.addEventListener("load", () => {
      data.size = [ image.width, image.height ];
      data.image = image;
      resolve();
    });
    image.src = `data:image/png;base64,${texture}`;
  });
}

function putTextureOnCanvas(ctx, atlasSize, index, data) {
  const atlasIndexX = index % atlasSize;
  const atlasIndexY = (index - atlasIndexX) / atlasSize;
  const x = atlasIndexX * data.size[0];
  const y = atlasIndexY * data.size[1];
  ctx.drawImage(data.image, x, y);

  // free up image data
  delete data.image;
}

export async function buildAtlas(data) {
  // TODO: atlas caching
  
  emitEvent("config:process:atlas:start");
  const textures = [
    ...Object.entries(data.configuration),
    ...data.addons.flatMap(a => Object.entries(a.configuration)),
  ]
      .map(([t, d]) => [t, d.modules["plannerator:configuration_metadata"], d])
      // .map(v => {console.log(v); return v})
      .flatMap(([config, addon, data]) => Object.entries(data)
          .map(([type, entries]) => [config, type, addon, entries]))
      .flatMap(([c, t, a, e]) => Array.isArray(e)
          ? [[c, t, a, e]]
          : t === "modules" && e["plannerator:global_elements"]
              ? [[c, "plannerator:global_elements", a, e["plannerator:global_elements"].elements]]
              : [])
      .flatMap(([c, t, a, l]) => l.map((v) => [c, t, a, v]))
      .filter(([,,, b]) => b && b.modules && b.modules["plannerator:texture"])
      .map(([c, t, a, b]) => ({
        configuration: c,
        configurationList: t,
        configurationAddon: a,
        texture: b.modules["plannerator:texture"].texture,
        ...(Object.fromEntries(Object.entries(b).filter(([k]) => k !== "modules")))
      }));

  if (textures.length === 0) {
    throw new Error("Configuration processing failed - no textured blocks found");
  }

  await Promise.all(textures.map(getTextureSize));

  atlases = [];
  let offset = 0;
  for (const t of Object.values(Object.groupBy(textures, t => t.size.toString()))) {
    if (t[0].size[0] !== t[0].size[1]) {
      throw new Error("Configuration processing failed - textures must be square");
    }

    const atlasEdgeSizeBlocks = 2 ** Math.ceil(Math.log2(Math.sqrt(t.length)));
    const atlasEdgeSize = atlasEdgeSizeBlocks * t[0].size[0];

    const canvas = new OffscreenCanvas(atlasEdgeSize, atlasEdgeSize);
    const ctx = canvas.getContext("2d");

    await Promise.all(t.map((b, i) => putTextureOnCanvas(ctx, atlasEdgeSizeBlocks, i, b)));

    t.forEach((m, i) => {
      m.index = offset + i;
      delete m.texture;
    });

    const blob = await canvas.convertToBlob({ type: "image/png" });
    atlases.push({
      offset,
      url: URL.createObjectURL(blob),
      metadata: t,
      size: atlasEdgeSizeBlocks,
      resolution: atlasEdgeSize,
      length: t.length,
    });
    offset += t.length;
  }

  console.log(atlases);

  emitEvent("config:process:atlas:end");
}

export function makeAtlasImage(index) {
  if (index < -1) {
    return null;
  }
  if (index === -1) {
    const div = document.createElement("div");
    div.classList.add("atlas-air");
    return div;
  }

  const originalIndex = index;
  for (const atlas of atlases) {
    if (index < atlas.length) {
      const atlasIndexX = index % atlas.size;
      const atlasIndexY = (index - atlasIndexX) / atlas.size;
      const size = atlas.metadata[index].size;
      const x = atlasIndexX * size[0];
      const y = atlasIndexY * size[1];

      const div = document.createElement("div");
      div.draggable = false;
      div.dataset.originalIndex = originalIndex;
      div.classList.add("atlas-texture");
      div.style.backgroundImage = `url(${atlas.url})`;
      div.style.setProperty("--atlas-resolution", `${atlas.resolution}px`);
      div.style.setProperty("--raw-x", `${x}px`);
      div.style.setProperty("--raw-y", `${y}px`);
      div.style.setProperty("--size-x", `${size[0]}px`);
      div.style.setProperty("--size-y", `${size[1]}px`);

      return div;
    }
    index -= atlas.length;
  }

  return null;
}

export function getAtlasMetadata(index) {
  if (index < -1) return null;
  if (index === -1) return {};

  for (const atlas of atlases) {
    if (index < atlas.length - 1) {
      return atlas.metadata[index];
    }
    index -= atlas.length;
  }

  return null;
}

export function getAtlasIndexFromMetadata(metadata, configuration, configurationList = null) {
  let index = 0;
  for (const atlas of atlases) {
    for (const atlasItemMeta of atlas.metadata) {
      if (checkEquivalence(metadata, atlasItemMeta)
          && atlasItemMeta.configuration === configuration
          && (!configurationList || atlasItemMeta.configurationList === configurationList)) {
        return index;
      }
      index++;
    }
  }
  return -1;
}
