<script setup lang="ts">
import { useLocalStorage } from "@vueuse/core";

const store = useSettingsStore();
const confirmationModal = useConfirmationModal();
const { assets } = useAssets();
const { onRender } = useScreen();

const handleActivateB = () => {
  store.closeSubMenu();
};

const handleActivateA = () => {
  if (isDead()) {
    resetBoard();
    confirmationModal.close();
    return;
  }
  confirmationModal.open({
    text: "Restart game?",
    onConfirm: () => {
      resetBoard();
      confirmationModal.close();
    },
    onCancel: () => {
      confirmationModal.close();
    },
  });
};

const TILE_COLORS: Record<number, { bg: string; fg: string }> = {
  [0]: { bg: "#ccc0b4", fg: "#776e65" },
  [2]: { bg: "#eee4db", fg: "#776e65" },
  [4]: { bg: "#eee0cb", fg: "#776e65" },
  [8]: { bg: "#f3b27a", fg: "#f9f6f2" },
  [16]: { bg: "#f69664", fg: "#f9f6f2" },
  [32]: { bg: "#f67c5f", fg: "#f9f6f2" },
  [64]: { bg: "#f7603c", fg: "#f9f6f2" },
  [128]: { bg: "#ecd072", fg: "#f9f6f2" },
  [256]: { bg: "#eecc62", fg: "#f9f6f2" },
  [512]: { bg: "#eec950", fg: "#f9f6f2" },
  [1024]: { bg: "#edc53f", fg: "#f9f6f2" },
  [2048]: { bg: "#edc12e", fg: "#f9f6f2" },
  [4046]: { bg: "#3c3a33", fg: "#f9f6f2" },
};
const LAST_TILE_COLOR =
  Object.values(TILE_COLORS)[Object.values(TILE_COLORS).length - 1]!;
const TILE_SIZE = 28;

const BORDER_COLOR = "#bbada0";
const BORDER_SIZE = 3;

const BOARD_X = 64;
const BOARD_Y = 32;
const BOARD_SIZE = 4;

const emptyBoard = () =>
  Array.from({ length: BOARD_SIZE }, () =>
    Array.from({ length: BOARD_SIZE }, () => 0),
  );

const savedBoard = useLocalStorage("nds-2048-board", emptyBoard());
const board = savedBoard.value.map((r) => [...r]);

const saveBoard = () => {
  savedBoard.value = board.map((r) => [...r]);
};

onRender((ctx) => {
  assets.images.home.topScreen.background.draw(ctx, 0, 0);

  ctx.font = "10px NDS10";
  ctx.textBaseline = "top";

  ctx.translate(BOARD_X, BOARD_Y);

  ctx.fillStyle = BORDER_COLOR;
  ctx.fillRect(
    0,
    0,
    BORDER_SIZE * (BOARD_SIZE + 1) + TILE_SIZE * BOARD_SIZE,
    BORDER_SIZE * (BOARD_SIZE + 1) + TILE_SIZE * BOARD_SIZE,
  );

  const renderTile = (col: number, row: number) => {
    const value = board[row]![col]!;
    const color = TILE_COLORS[value] ?? LAST_TILE_COLOR;

    const x = BORDER_SIZE + col * (TILE_SIZE + BORDER_SIZE);
    const y = BORDER_SIZE + row * (TILE_SIZE + BORDER_SIZE);

    ctx.fillStyle = color.bg;
    ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);

    if (value === 0) return;

    ctx.fillStyle = color.fg;
    ctx.font = value <= 2048 ? "10px NDS10" : "7px NDS7";
    fillTextHCentered(
      ctx,
      value.toString(),
      x + 1,
      y + (value <= 2048 ? 9 : 10),
      TILE_SIZE,
    );
  };

  for (let row = 0; row < BOARD_SIZE; row += 1) {
    for (let col = 0; col < BOARD_SIZE; col += 1) {
      renderTile(col, row);
    }
  }
});

const getCell = (row: number, col: number) => board[row]![col]!;
const setCell = (row: number, col: number, val: number) => {
  board[row]![col] = val;
};

const compact = (rowDir: number, colDir: number) => {
  const reversed = rowDir > 0 || colDir > 0;
  const start = reversed ? BOARD_SIZE - 1 : 0;
  const step = reversed ? -1 : 1;

  for (let major = 0; major < BOARD_SIZE; major += 1) {
    for (let times = 0; times < BOARD_SIZE - 1; times += 1) {
      for (let minor = start; minor >= 0 && minor < BOARD_SIZE; minor += step) {
        const row = rowDir !== 0 ? minor : major;
        const col = colDir !== 0 ? minor : major;
        const nextRow = row - rowDir;
        const nextCol = col - colDir;

        if (nextRow < 0 || nextRow >= BOARD_SIZE) continue;
        if (nextCol < 0 || nextCol >= BOARD_SIZE) continue;

        if (getCell(row, col) === 0 && getCell(nextRow, nextCol) !== 0) {
          setCell(row, col, getCell(nextRow, nextCol));
          setCell(nextRow, nextCol, 0);
        }
      }
    }
  }
};

const merge = (rowDir: number, colDir: number) => {
  const reversed = rowDir > 0 || colDir > 0;
  const start = reversed ? BOARD_SIZE - 1 : 0;
  const step = reversed ? -1 : 1;

  for (let major = 0; major < BOARD_SIZE; major += 1) {
    for (let minor = start; minor >= 0 && minor < BOARD_SIZE; minor += step) {
      const row = rowDir !== 0 ? minor : major;
      const col = colDir !== 0 ? minor : major;
      const nextRow = row - rowDir;
      const nextCol = col - colDir;

      if (nextRow < 0 || nextRow >= BOARD_SIZE) continue;
      if (nextCol < 0 || nextCol >= BOARD_SIZE) continue;

      if (
        getCell(row, col) !== 0 &&
        getCell(row, col) === getCell(nextRow, nextCol)
      ) {
        setCell(row, col, getCell(row, col) * 2);
        setCell(nextRow, nextCol, 0);
      }
    }
  }
};

const spawnTile = () => {
  const empty: [number, number][] = [];
  for (let row = 0; row < BOARD_SIZE; row += 1) {
    for (let col = 0; col < BOARD_SIZE; col += 1) {
      if (getCell(row, col) === 0) empty.push([row, col]);
    }
  }
  if (empty.length === 0) return;

  const [row, col] = empty[Math.floor(Math.random() * empty.length)]!;
  setCell(row, col, Math.random() < 0.9 ? 2 : 4);
};

const resetBoard = () => {
  for (let row = 0; row < BOARD_SIZE; row += 1) {
    for (let col = 0; col < BOARD_SIZE; col += 1) {
      setCell(row, col, 0);
    }
  }

  spawnTile();
  spawnTile();
  saveBoard();
};

const isDead = () => {
  for (let row = 0; row < BOARD_SIZE; row += 1) {
    for (let col = 0; col < BOARD_SIZE; col += 1) {
      if (
        getCell(row, col) === 0 ||
        (col < BOARD_SIZE - 1 && getCell(row, col) === getCell(row, col + 1)) ||
        (row < BOARD_SIZE - 1 && getCell(row, col) === getCell(row + 1, col))
      )
        return false;
    }
  }
  return true;
};

if (board.every((r) => r.every((c) => c === 0))) {
  resetBoard();
}

const slide = (rowDir: number, colDir: number) => {
  const before = board.map((r) => [...r]);

  compact(rowDir, colDir);
  merge(rowDir, colDir);
  compact(rowDir, colDir);

  const moved = board.some((r, i) => r.some((c, j) => c !== before[i]![j]));
  if (!moved) return;

  spawnTile();
  saveBoard();
  if (isDead()) {
    confirmationModal.open({
      text: "Game Over!\nRestart?",
      onConfirm: () => {
        resetBoard();
        confirmationModal.close();
      },
      onCancel: () => {
        confirmationModal.close();
      },
    });
  }
};

useKeyDown((key) => {
  switch (key) {
    case "NDS_UP":
      slide(-1, 0);
      break;
    case "NDS_DOWN":
      slide(1, 0);
      break;
    case "NDS_LEFT":
      slide(0, -1);
      break;
    case "NDS_RIGHT":
      slide(0, 1);
      break;
  }
});
</script>

<template>
  <CommonButtons
    :y-offset="confirmationModal.buttonsYOffset"
    b-label="Cancel"
    a-label="Restart"
    @activate-b="handleActivateB()"
    @activate-a="handleActivateA()"
  />
</template>
