import WebFont from "webfontloader";
import * as Hammer from "hammerjs";
import { render } from "solid-js/web";

import { BoardStyle, PiecesStyle } from "./types";

import Board from "./board/Board";
import Game from "./game/Game";
import Player from "./player/Player";
import App from "./ui/App";

import { state, setState } from "./state";

import createImage from "./encoders/createImage";
import createAnimation from "./encoders/createAnimation";
import readFile from "./utils/readFile";
import download from "./utils/download";
import { compressPGN } from "./game/PGNHelpers";
import extractUrlData from "./persistance/extractUrlData";
import importFromLink from "./imports/importFromLink";

const main = async () => {
  const board = new Board(state.boardConfig);
  const player = new Player(board, state.gameConfig);

  /* Register handlers */

  const handlers = {
    prev() {
      player.pause();
      player.prev();
    },
    next() {
      player.pause();
      player.next();
    },
    first() {
      player.pause();
      player.first();
    },
    last() {
      player.pause();
      player.last();
    },
    toggleBorder() {
      board.toggleBorder();
      setState("boardConfig", "showBorder", !state.boardConfig.showBorder);
    },

    showBorder() {
      board.showBorder();
      setState("boardConfig", "showBorder", true);
    },
    hideBorder() {
      board.hideBorder();
      setState("boardConfig", "showBorder", false);
    },
    toggleExtraInfo() {
      board.toggleExtraInfo();
      setState(
        "boardConfig",
        "showExtraInfo",
        !state.boardConfig.showExtraInfo
      );
    },
    toggleAnonymous() {
      setState("boardConfig", "anonymous", !state.boardConfig.anonymous);
      board.updateConfig({ anonymous: state.boardConfig.anonymous });
    },
    toggleTitleScreen() {
      setState("gameConfig", "titleScreen", !state.gameConfig.titleScreen);
    },
    flip() {
      console.log("FLIP");
      board.flip();
      setState("boardConfig", "flipped", !state.boardConfig.flipped);
    },
    togglePlay() {
      player.playing ? player.pause() : player.play();
    },
    goto(ply: number) {
      player.pause();
      player.goto(ply);
    },
    changeBoardStyle(style: BoardStyle) {
      board.setStyle(style);
      setState("boardConfig", "boardStyle", style);
    },
    changePiecesStyle(style: PiecesStyle) {
      board.setPiecesStyle(style);
      setState("boardConfig", "piecesStyle", style);
    },
    async loadPGN(pgn: string) {
      const game = new Game().loadPGN(pgn);
      setState({
        pgn: game.pgn,
        fen: "",
        moves: game.getMoves(),
        ply: 0,
        game,
      });
      window.location.hash = `v1/pgn/${compressPGN(game.pgn)}`;

      await player.load(game);
      setState("activeTab", "game");
    },
    async loadFEN(fen: string) {
      const game = new Game().loadFEN(fen);
      setState({ pgn: "", fen, moves: game.getMoves(), ply: 0, game });
      await player.load(game);
    },
    async importPGN(link: string) {
      const result = await importFromLink(link);

      if (result.error) {
        return;
      }

      await this.loadPGN(result.pgn);
    },
    async downloadImage() {
      const data = await createImage(
        state.fen,
        state.pgn,
        state.ply,
        state.boardConfig,
        state.gameConfig.picSize
      );
      download(data, "fen", "png");
    },
    async downloadAnimation() {
      const data = await createAnimation(
        state.pgn,
        state.boardConfig,
        state.gameConfig.format,
        state.gameConfig.animationSize
      );
      download(data, "game", state.gameConfig.format.toLowerCase());
    },
  };

  /* Render the page */

  render(
    () => <App handlers={handlers} state={state} />,
    document.getElementById("root") as HTMLElement
  );

  const $board = document.querySelector<HTMLImageElement>("#board");
  $board?.appendChild(board.canvas);

  /* Restore game from the url */

  const { pgn, fen } = extractUrlData();

  await (pgn
    ? handlers.loadPGN(pgn)
    : fen
    ? handlers.loadFEN(fen)
    : handlers.loadFEN(
        "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
      ));

  /* Register events */

  const keyMapping: { [key: string]: () => void } = {
    ArrowLeft: handlers.prev,
    ArrowRight: handlers.next,
    ArrowUp: handlers.first,
    ArrowDown: handlers.last,
    " ": handlers.togglePlay,
    b: handlers.toggleBorder,
    f: handlers.flip,
    e: handlers.toggleExtraInfo,
  };

  document.addEventListener("keydown", (e) => {
    const target = e.target as HTMLElement | null;

    if (
      keyMapping[e.key] &&
      target?.nodeName !== "INPUT" &&
      target?.nodeName !== "TEXTAREA"
    ) {
      keyMapping[e.key]();
    }
  });

  const preventDefaults = (e: Event) => {
    e.preventDefault();
    e.stopPropagation();
  };

  ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
    document.addEventListener(eventName, preventDefaults, false);
  });

  document.addEventListener("drop", async (e) => {
    if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
      const content = await readFile(e.dataTransfer.files[0]);
      handlers.loadPGN(content);
    }
  });

  const hammer = new Hammer.Manager(board.canvas);
  hammer.add(new Hammer.Swipe());
  hammer.add(new Hammer.Pinch());
  hammer.add(new Hammer.Press({ time: 500 }));
  hammer.add(new Hammer.Tap({ taps: 1 }));

  hammer.on("swiperight", handlers.next);
  hammer.on("swipeleft", handlers.prev);
  hammer.on("swipeup", handlers.first);
  hammer.on("swipedown", handlers.last);
  hammer.on("pinchin", handlers.showBorder);
  hammer.on("pinchout", handlers.hideBorder);
  hammer.on("tap", handlers.next);
  hammer.on("press", handlers.flip);
};

/* Boot */

WebFont.load({
  google: {
    families: ["Ubuntu:500,700", "Fira Mono"],
  },
  custom: {
    families: ["Chess"],
    urls: ["/fonts.css"],
  },
  active: main,
});
