<script setup>
  import { onMounted, watch } from "vue";
  import { globalStore } from "../stores/index.js";
  import {
    createBoard,
    conwayRules,
    overpopulationRules,
    lonelinessRules,
    threebornRules,
    highLifeRules,
    servietteRules,
    evolve2d,
  } from "../modules/core.js";
  import {
    create1dInitialState,
    create2dRandomGrid,
  } from "../modules/board.js";
  import { picToBoard } from "../modules/picture.js";

  const store = globalStore();
  const available2dRules = {
    conway: conwayRules,
    overpopulation: overpopulationRules,
    loneliness: lonelinessRules,
    threeborn: threebornRules,
    highlife: highLifeRules,
    serviette: servietteRules,
  };

  // used to determine the dimensions of the board
  // TODO: should be a Board method
  const max = () => {
    return Math.max(store.board.width, store.board.height);
  };

  const selectedRules = () => {
    return available2dRules[store.selected2dRules.id];
  };

  watch(
    () => store.draw1d,
    (value) => {
      if (value == true) draw1d();
    }
  );

  watch(
    () => store.draw2d,
    (value) => {
      if (value == true) draw2dNew();
    }
  );

  watch(
    () => store.draw2dLast,
    async (value) => {
      if (value == true) await draw2dLast();
    }
  );

  watch(
    () => store.draw2dpicture,
    (value) => {
      if (value == true) draw2dPicture();
    }
  );

  watch(
    () => store.reset,
    (value) => {
      if (value == true) reset();
    }
  );

  onMounted(() => {
    // TODO : butt ugly.
    const canvas = document.getElementById("board-canvas");
    store.renderer.width = canvas.parentElement.clientWidth;
    store.renderer.height = canvas.parentElement.clientHeight;
    store.renderer.canvas = canvas;
    store.renderer.ctx = store.renderer.canvas.getContext("2d", {
      willReadFrequently: true,
    });
    if (typeof OffscreenCanvas != "undefined") {
      store.renderer.workCanvas = new OffscreenCanvas(
        canvas.parentElement.clientWidth,
        canvas.parentElement.clientHeight
      );
    }
    // https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/OffscreenCanvas#Browser_compatibility
    // Fallback for when offscreenCanvas is unsupported or disabled (Firefox < 44, default for Firefox?)
    else {
      store.renderer.workCanvas = document.createElement("canvas");
      store.renderer.workCanvas.width = canvas.parentElement.clientWidth;
      store.renderer.workCanvas.height = canvas.parentElement.clientHeight;
    }
    store.renderer.workCtx = store.renderer.workCanvas.getContext("2d", {
      willReadFrequently: true,
    });
    store.setBoardWidth();
    store.setBoardHeight();
  });

  // draws the board on the canvas
  const drawCanvas = async (board) => {
    store.renderer.render(board);
  };

  // draw elementary automaton on the canvas based on selected ruleset
  const draw1d = () => {
    const initialState = create1dInitialState(
      store.board,
      store.initial1dState
    );
    const board = createBoard(initialState, store.ruleset1d.rules, max());
    store.board.grid = Object.freeze(board);
    drawCanvas(store.board);
    store.toggleStop();
  };

  // draw 2D automaton on the canvas in a loop
  const draw2d = (board) => {
    drawCanvas(store.board);
    const newBoard = Object.freeze(evolve2d(board.grid, selectedRules()));
    if (store.board.grid == newBoard) store.toggleStop();
    store.board.grid = newBoard;
  };

  // draw 2d automaton in a loop, starting from passed state
  const draw2dNext = async (board) => {
    setTimeout(() => {
      if (!store.canDraw) return;
      draw2d(board);
      return draw2dNext(store.board);
    }, store.renderer.refreshRate);
  };

  // draw 2d automaton from a new state
  const draw2dNew = async () => {
    if (!store.canDraw) return;
    const initialGrid = create2dRandomGrid(
      store.board.width,
      store.board.height
    );
    store.board.grid = Object.freeze(evolve2d(initialGrid, selectedRules()));
    if (store.loop) return draw2dNext(store.board);
    else draw2d(store.board);
    store.toggleStop();
  };

  // draw 2d automaton from the last known generated board
  const draw2dLast = async () => {
    if (!store.canDraw) return;
    if (store.loop) return draw2dNext(store.board);
    else draw2d(store.board);
    store.toggleStop();
  };

  // draw 2d automaton from an uploaded picture.
  // use the picture representation as an initial state
  const draw2dPicture = async () => {
    store.renderer.renderImage(
      store.picture,
      store.renderer.ctx,
      store.renderer.width,
      store.renderer.height
    );
    const resized = store.renderer.renderImage(
      store.picture,
      store.renderer.workCtx,
      store.board.width,
      store.board.height
    );
    const newBoard = picToBoard(resized.data, store.board);
    store.board.grid = Object.freeze(newBoard);
    store.toggleStop();
  };

  const reset = () => {
    store.toggleStop();
    store.board.grid = null;
    store.renderer.reset();
    store.toggleReset();
  };
</script>
<template>
  <canvas
    id="board-canvas"
    ref="board-canvas"
    :width="store.renderer.width"
    :height="store.renderer.height"
  />
</template>
<style>
  #canvas-board {
    flex: 1;
    margin: 0 auto;
  }
</style>
