/**
 * クォータービュー(2:1アイソメトリック)の投影と、ドット絵スプライトを描くための基本図形。
 *
 * 論理キャンバスは「1論理ピクセル = 1ドット」で持ち、CSS側で整数倍に引き伸ばす。
 * そのため座標は全て整数に丸めて描く（ここが滲むとドット絵に見えなくなる）。
 */
import { GRID_H, GRID_W } from '../core/config';

/** タイル1枚の菱形の幅と高さ */
export const TILE_W = 36;
export const TILE_H = 18;
/** 壁や機械の立ち上がり高さ */
export const WALL_H = 22;

/** 論理キャンバスのサイズ */
export const VIEW_W = (GRID_W + GRID_H) * (TILE_W / 2);
export const VIEW_H = (GRID_W + GRID_H) * (TILE_H / 2) + WALL_H + TILE_H + 20;

const ORIGIN_X = GRID_H * (TILE_W / 2);
const ORIGIN_Y = WALL_H + 8;

/** グリッド座標 → タイル中心のスクリーン座標 */
export function isoCenterX(x: number, y: number): number {
  return ORIGIN_X + (x - y) * (TILE_W / 2);
}

export function isoCenterY(x: number, y: number): number {
  return ORIGIN_Y + (x + y) * (TILE_H / 2) + TILE_H / 2;
}

/**
 * グリッド上のベクトルをスクリーン上のベクトルへ変換する。
 * コンベアの流れる向きなど「方向」を描くときに使う。
 */
export function isoVec(gx: number, gy: number): { x: number; y: number } {
  return { x: (gx - gy) * (TILE_W / 2), y: (gx + gy) * (TILE_H / 2) };
}

/** スクリーン座標 → グリッド座標 (小数)。ピッキングに使う。 */
export function unproject(sx: number, sy: number): { x: number; y: number } {
  const u = (sx - ORIGIN_X) / (TILE_W / 2);
  const v = (sy - ORIGIN_Y - TILE_H / 2) / (TILE_H / 2);
  return { x: (u + v) / 2, y: (v - u) / 2 };
}

// ------------------------------------------------------------------ 基本図形

/** 菱形(タイルの上面)。cx,cy は中心。 */
export function diamond(
  ctx: CanvasRenderingContext2D,
  cx: number,
  cy: number,
  w: number,
  h: number,
): void {
  ctx.beginPath();
  ctx.moveTo(Math.round(cx), Math.round(cy - h / 2));
  ctx.lineTo(Math.round(cx + w / 2), Math.round(cy));
  ctx.lineTo(Math.round(cx), Math.round(cy + h / 2));
  ctx.lineTo(Math.round(cx - w / 2), Math.round(cy));
  ctx.closePath();
}

export function fillDiamond(
  ctx: CanvasRenderingContext2D,
  cx: number,
  cy: number,
  w: number,
  h: number,
  color: string,
): void {
  ctx.fillStyle = color;
  diamond(ctx, cx, cy, w, h);
  ctx.fill();
}

export interface BoxColors {
  top: string;
  left: string;
  right: string;
}

/**
 * アイソメトリックな箱。cx,cy は「底面の菱形の中心」。
 * 上面・左面・右面の3色で立体に見せる。
 */
export function fillBox(
  ctx: CanvasRenderingContext2D,
  cx: number,
  cy: number,
  w: number,
  h: number,
  height: number,
  c: BoxColors,
): void {
  const x = Math.round(cx);
  const y = Math.round(cy);
  const hw = w / 2;
  const hh = h / 2;
  const top = y - height;

  // 左面
  ctx.fillStyle = c.left;
  ctx.beginPath();
  ctx.moveTo(x - hw, y - hh);
  ctx.lineTo(x, y);
  ctx.lineTo(x, y - height);
  ctx.lineTo(x - hw, y - hh - height);
  ctx.closePath();
  ctx.fill();

  // 右面
  ctx.fillStyle = c.right;
  ctx.beginPath();
  ctx.moveTo(x + hw, y - hh);
  ctx.lineTo(x, y);
  ctx.lineTo(x, y - height);
  ctx.lineTo(x + hw, y - hh - height);
  ctx.closePath();
  ctx.fill();

  // 上面
  fillDiamond(ctx, x, top - hh, w, h, c.top);
}

/** 影。地面に落とす薄い菱形。 */
export function shadow(
  ctx: CanvasRenderingContext2D,
  cx: number,
  cy: number,
  w: number,
): void {
  ctx.save();
  ctx.globalAlpha = 0.32;
  fillDiamond(ctx, cx, cy, w, w / 2, '#000000');
  ctx.restore();
}

// ------------------------------------------------------------------ スプライトキャッシュ

const cache = new Map<string, HTMLCanvasElement>();

/**
 * オフスクリーンにドット絵を1度だけ描いて使い回す。
 * @param key キャッシュキー
 */
export function sprite(
  key: string,
  w: number,
  h: number,
  draw: (ctx: CanvasRenderingContext2D) => void,
): HTMLCanvasElement {
  const hit = cache.get(key);
  if (hit) return hit;
  const cv = document.createElement('canvas');
  cv.width = Math.ceil(w);
  cv.height = Math.ceil(h);
  const ctx = cv.getContext('2d')!;
  ctx.imageSmoothingEnabled = false;
  draw(ctx);
  cache.set(key, cv);
  return cv;
}

/** スプライトを、指定した「足元の座標」を基準に貼る。 */
export function blit(
  ctx: CanvasRenderingContext2D,
  img: HTMLCanvasElement,
  footX: number,
  footY: number,
  anchorX: number,
  anchorY: number,
): void {
  ctx.drawImage(img, Math.round(footX - anchorX), Math.round(footY - anchorY));
}
