import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

type Rect = [number, number, number, number];

let atlasImage: HTMLImageElement | null = null;
const modelCache = new Map<string, THREE.Group>();

const loaded = ref(0);
const total = ref({{TOTAL}});
const isReady = computed(() => loaded.value === total.value);

if (import.meta.client) {
  atlasImage = document.createElement('img');
  atlasImage.src = '/nds/atlas.webp';

  if (atlasImage.complete) {
    loaded.value += 1;
  } else {
    atlasImage.onload = () => {
      loaded.value += 1;
    };
  }
}

const drawAtlasImage = (
  ctx: CanvasRenderingContext2D,
  [sx, sy, sw, sh]: Rect,
  [dx, dy, dw, dh]: Rect,
  opts?: Partial<{ colored: boolean }>,
): void => {
  if (!atlasImage) return;

  if (opts?.colored) {
    const app = useAppStore();
    sh /= 16;
    sy += (app.color.row * 4 + app.color.col) * sh;
    dh = sh;
  }

  ctx.drawImage(atlasImage, sx, sy, sw, sh, dx, dy, dw, dh);
};

const createModel = (path: string) => {
  const cached = modelCache.get(path);
  if (cached) {
    return cached;
  }

  const model = new THREE.Group();
  modelCache.set(path, model);

  new GLTFLoader().load(
    path,
    (gltf) => {
      for (const child of [...gltf.scene.children]) {
        model.add(child);
      }

      loaded.value += 1;
    },
    undefined,
    (error) => {
      console.error(`Error loading model ${path}:`, error);
      loaded.value += 1;
    }
  );

  return model;
};

export type AtlasImage = {
  draw: (
    ctx: CanvasRenderingContext2D,
    x: number,
    y: number,
    opts?: Partial<{ colored: boolean }>,
  ) => void;
  rect: { x: number; y: number; width: number; height: number };
};

type ImageTree = {
  [key: string]: AtlasImage | ImageTree;
};

type ModelTree = {
  [key: string]: THREE.Group | ModelTree;
};

const assets = {
  images: {{IMAGES}} as const satisfies ImageTree,
  models: {{MODELS}} as const satisfies ModelTree,
};

type Assets = typeof assets;

export const useAssets = <T = Assets>(selector: (assets: Assets) => T = (a) => a as T) => {
  return {
    assets: selector(assets),
    loaded,
    total,
    isReady,
  };
};
