import { CellType } from "./constants/CellType";
import { Direction } from "./constants/Direction";
import { ExitType } from "./constants/ExitType";
import { Cell } from "./types/Cell";
import { Exit } from "./types/Exit";

const boardSize = 7;
const universityLocations = [
  [2, 0],
  [4, 1],
  [3, 3],
];
const factoryLocations = [
  [0, 0],
  [5, 2],
  [4, 6],
];
const houseLocations = [
  [0, 2],
  [1, 5],
  [2, 4],
  [5, 4],
];
const mapPosition = (position: number[], type: CellType) => {
  return {
    row: position[0],
    col: position[1],
    type: type,
  };
};
const specialUniversityCells = universityLocations.map((position) =>
  mapPosition(position, CellType.UNIVERSITY),
);
const specialFactoryCells = factoryLocations.map((position) =>
  mapPosition(position, CellType.FACTORY),
);
const specialHouseCells = houseLocations.map((position) =>
  mapPosition(position, CellType.HOUSE),
);
const specialCells = specialUniversityCells
  .concat(specialFactoryCells)
  .concat(specialHouseCells);

const specialExitIndexes1 = [1, 5];
const specialExitIndexes2 = [3];

function createBoard(): Cell[][] {
  const indexes = Array.from(Array(boardSize).keys());
  return indexes.map((rowIndex) =>
    indexes.map((colIndex) => {
      const specialCell = specialCells.find(
        (specialCell) =>
          specialCell.row === rowIndex && specialCell.col === colIndex,
      );
      return new Cell(specialCell ? specialCell.type : CellType.NORMAL);
    }),
  );
}

function connectAdjacentCells(board: Cell[][]) {
  const indexes = Array.from(Array(boardSize).keys());
  for (const rowIndex of indexes.slice(0, -1)) {
    for (const colIndex of indexes) {
      const cell = board[rowIndex][colIndex];
      const bottomCell = board[rowIndex + 1][colIndex];
      cell
        .getNodeAt(Direction.SOUTH)
        .linkToNode(bottomCell.getNodeAt(Direction.NORTH));
    }
  }

  for (const rowIndex of indexes) {
    for (const colIndex of indexes.slice(0, -1)) {
      const cell = board[rowIndex][colIndex];
      const rightCell = board[rowIndex][colIndex + 1];
      cell
        .getNodeAt(Direction.EAST)
        .linkToNode(rightCell.getNodeAt(Direction.WEST));
    }
  }
}

function addExits(board: Cell[][]) {
  // Add exits to top row
  board[0].forEach((cell, colIndex) => {
    let exitType = ExitType.AMBIVALENT;
    if (specialExitIndexes1.includes(colIndex)) exitType = ExitType.ROAD;
    if (specialExitIndexes2.includes(colIndex)) exitType = ExitType.RAIL;
    cell.getNodeAt(Direction.NORTH).linkToNode(new Exit(exitType));
  });

  // Add exits to bottom row
  board[boardSize - 1].forEach((cell, colIndex) => {
    let exitType = ExitType.AMBIVALENT;
    if (specialExitIndexes1.includes(colIndex)) exitType = ExitType.ROAD;
    if (specialExitIndexes2.includes(colIndex)) exitType = ExitType.RAIL;
    cell.getNodeAt(Direction.SOUTH).linkToNode(new Exit(exitType));
  });

  // Add exits to left and right columns
  board.forEach((row, rowIndex) => {
    let exitType = ExitType.AMBIVALENT;
    if (specialExitIndexes1.includes(rowIndex)) exitType = ExitType.RAIL;
    if (specialExitIndexes2.includes(rowIndex)) exitType = ExitType.ROAD;
    row[0].getNodeAt(Direction.WEST).linkToNode(new Exit(exitType));
    row[boardSize - 1].getNodeAt(Direction.EAST).linkToNode(new Exit(exitType));
  });
}

export function buildBoard(): Cell[][] {
  const board: Cell[][] = createBoard();
  connectAdjacentCells(board);
  addExits(board);
  return board;
}
