import {
  VisualizerDef,
  VisualizerOptions,
  visualizers,
} from "../visualizers/index.ts";

const imageCache = new Map<string, HTMLImageElement>();

let cachedOpts: VisualizerOptions = {};

export function currentOptions(): VisualizerOptions {
  return cachedOpts;
}

export function buildParamsUI(name: string) {
  const container = document.querySelector("#vis-params")!;
  container.innerHTML = "";
  const def = visualizers[name];
  if (!def || def.params.length === 0) {
    cachedOpts = {};
    return;
  }

  // seed cache with defaults
  cachedOpts = {};
  for (const p of def.params) {
    if (p.type === "img") {
      const img = imageCache.get(p.key);
      if (img) cachedOpts[p.key] = img;
    } else {
      cachedOpts[p.key] = p.default;
    }
  }

  for (const p of def.params) {
    if (p.type === "img") continue; // handled below as group

    const label = document.createElement("label");
    label.className = "menu-label";
    label.textContent = p.label;

    if (p.type === "number") {
      const range = document.createElement("input");
      range.type = "range";
      range.id = `param-${p.key}`;
      range.min = String(p.min ?? 0);
      range.max = String(p.max ?? 100);
      if (p.step !== undefined) range.step = String(p.step);
      range.value = String(p.default);

      const number = document.createElement("input");
      number.type = "number";
      number.min = range.min;
      number.max = range.max;
      if (p.step !== undefined) number.step = range.step;
      number.value = range.value;
      number.addEventListener("change", () => {
        range.value = number.value;
        cachedOpts[p.key] = Number(range.value);
      });
      range.addEventListener("input", () => {
        number.value = range.value;
        cachedOpts[p.key] = Number(range.value);
      });

      container.appendChild(label);
      container.appendChild(range);
      container.appendChild(number);
    } else if (p.type === "color") {
      const input = document.createElement("input");
      input.type = "color";
      input.id = `param-${p.key}`;
      input.value = String(p.default);
      input.addEventListener("input", () => {
        cachedOpts[p.key] = input.value;
      });
      container.appendChild(label);
      container.appendChild(input);
    } else if (p.type === "select") {
      const select = document.createElement("select");
      select.id = `param-${p.key}`;
      for (const v of p.values ?? []) {
        const opt = document.createElement("option");
        opt.value = v;
        opt.textContent = v;
        if (v === String(p.default)) opt.selected = true;
        select.appendChild(opt);
      }
      select.addEventListener("change", () => {
        cachedOpts[p.key] = select.value;
      });
      container.appendChild(label);
      container.appendChild(select);
    } else if (p.type === "text") {
      const input = document.createElement("input");
      input.type = "text";
      input.id = `param-${p.key}`;
      input.maxLength = 1;
      input.value = String(p.default);
      if (p.inputStyle) Object.assign(input.style, p.inputStyle);
      input.addEventListener("input", () => {
        cachedOpts[p.key] = input.value;
      });
      container.appendChild(label);
      container.appendChild(input);
    }
  }

  // populate the images menu panel (static HTML, menubar system handles open/close)
  const imgGroup = document.querySelector<HTMLDivElement>("#img-group")!;
  const imgPanel = document.querySelector<HTMLDivElement>("#panel-images")!;
  imgPanel.innerHTML = "";
  const imgParams = def.params.filter((p) => p.type === "img");
  if (imgParams.length > 0) {
    imgGroup.style.display = "";
    for (const p of imgParams) {
      const rowLabel = document.createElement("div");
      rowLabel.className = "menu-label";
      rowLabel.textContent = p.label;

      const fileInput = document.createElement("input");
      fileInput.type = "file";
      fileInput.id = `param-${p.key}`;
      fileInput.accept = "image/*";
      fileInput.hidden = true;

      const uploadBtn = document.createElement("button");
      uploadBtn.className = "menu-item";
      uploadBtn.textContent = "upload";
      uploadBtn.addEventListener("click", () => fileInput.click());

      const clearBtn = document.createElement("button");
      clearBtn.className = "menu-item clear-btn hidden";
      clearBtn.textContent = "×";
      clearBtn.addEventListener("click", () => {
        imageCache.delete(p.key);
        delete cachedOpts[p.key];
        uploadBtn.textContent = "upload";
        clearBtn.classList.add("hidden");
        fileInput.value = "";
      });

      fileInput.addEventListener("change", () => {
        const file = fileInput.files?.[0];
        if (!file) return;
        uploadBtn.textContent = file.name;
        clearBtn.classList.remove("hidden");
        const reader = new FileReader();
        reader.onload = () => {
          const img = new Image();
          img.onload = () => {
            imageCache.set(p.key, img);
            cachedOpts[p.key] = img;
          };
          img.src = reader.result as string;
        };
        reader.readAsDataURL(file);
      });

      const row = document.createElement("div");
      row.className = "import-row";
      row.appendChild(uploadBtn);
      row.appendChild(clearBtn);

      imgPanel.appendChild(rowLabel);
      imgPanel.appendChild(row);
      imgPanel.appendChild(fileInput);
    }
  } else {
    imgGroup.style.display = "none";
  }
}

export function buildVisSelect(select: HTMLSelectElement) {
  select.innerHTML = "";
  const byCategory = new Map<string, Array<[string, VisualizerDef]>>();
  for (const [name, def] of Object.entries(visualizers)) {
    const cat = def.category ?? "Other";
    if (!byCategory.has(cat)) byCategory.set(cat, []);
    byCategory.get(cat)!.push([name, def]);
  }
  for (const [cat, entries] of byCategory) {
    const group = document.createElement("optgroup");
    group.label = cat;
    for (const [name] of entries) {
      const opt = document.createElement("option");
      opt.value = name;
      opt.textContent = name;
      group.appendChild(opt);
    }
    select.appendChild(group);
  }
  // restore last-used, then fall back to default, then first option
  const saved = localStorage.getItem("vis");
  if (saved && visualizers[saved]) {
    select.value = saved;
  } else {
    const def = Object.entries(visualizers).find(([, v]) => v.default);
    select.value = def ? def[0] : select.querySelector("option")!.value;
  }

  // persist selection
  select.addEventListener("change", () => {
    localStorage.setItem("vis", select.value);
  });
}
