import { CornerType, Game, PlayerColor } from "../routes/games/types";
import { layout } from "./layout";

const isRuleEnabled = (game: any, rule: string): boolean => {
  return rule in game.rules && game.rules[rule].enabled;
};

const getValidRoads = (game: any, color: string): number[] => {
  const limits: number[] = [];

  /* For each road, if the road is set, skip it.
   * If no color is set, check the two corners. If the corner
   * has a matching color, add this to the set. Otherwise skip.
   */
  layout.roads.forEach((road, roadIndex) => {
    // Skip if placements or roads missing, or if this road is already occupied
    // Treat the explicit sentinel "unassigned" as "not available" so only
    // consider a road occupied when a color is present and not "unassigned".
    if (
      !game.placements ||
      !game.placements.roads ||
      (game.placements.roads[roadIndex] &&
        game.placements.roads[roadIndex].color &&
        game.placements.roads[roadIndex].color !== "unassigned")
    ) {
      return;
    }
    let valid = false;
    for (let c = 0; !valid && c < road.corners.length; c++) {
      const cornerIndex = road.corners[c] as number;
      if (cornerIndex == null || (layout as any).corners[cornerIndex] == null) {
        continue;
      }
      const corner = (layout as any).corners[cornerIndex];
      const cornerColor =
        (game as any).placements &&
        (game as any).placements.corners &&
        (game as any).placements.corners[cornerIndex] &&
        (game as any).placements.corners[cornerIndex].color;
      /* Roads do not pass through other player's settlements.
       * Consider a corner with color === "unassigned" as empty. */
      if (cornerColor && cornerColor !== "unassigned" && cornerColor !== color) {
        continue;
      }
      for (let r = 0; !valid && r < (corner.roads || []).length; r++) {
        /* This side of the corner is pointing to the road being validated. Skip it. */
        if (!corner.roads || corner.roads[r] === roadIndex) {
          continue;
        }
        const rr = corner.roads[r];
        if (rr == null) {
          continue;
        }
        const placementsRoads = (game as any).placements && (game as any).placements.roads;
        if (placementsRoads && placementsRoads[rr] && placementsRoads[rr].color === color) {
          valid = true;
        }
      }
    }
    if (valid) {
      limits.push(roadIndex);
    }
  });

  return limits;
};

const getValidCorners = (game: Game, color: PlayerColor = "unassigned", type?: CornerType): number[] => {
  const limits: number[] = [];

  console.log("getValidCorners", color, type);
  /* For each corner, if the corner already has a color set, skip it if type
   * isn't set. If type is set and is a match, and the color is a match,
   * add it to the list.
   *
   * If we are limiting based on active player, a corner is only valid
   * if it connects to a road that is owned by that player.
   *
   * If no color is set, walk each road that leaves that corner and
   * check to see if there is a settlement placed at the end of that road
   *
   * If so, this location cannot have a settlement.
   *
   * If still valid, and we are in initial settlement placement, and if
   * Volcano is enabled, verify the tile is not the Volcano.
   */
  layout.corners.forEach((corner, cornerIndex) => {
    const placement = game.placements && game.placements.corners ? game.placements.corners[cornerIndex] : undefined;
    if (!placement) {
      // Treat a missing placement as unassigned (no owner)
      // Continue processing using a falsy placement where appropriate
    }
    if (type) {
      if (placement && placement.color === color && placement.type === type) {
        limits.push(cornerIndex);
      }
      return;
    }

    // If the corner has a color set and it's not the explicit sentinel
    // "unassigned" then it's occupied and should be skipped.
    // Note: placement.color may be undefined (initial state), treat that
    // the same as unassigned.
    if (placement && placement.color && placement.color !== "unassigned") {
      return;
    }

    let valid;
    // Treat either a falsy color (""/undefined) or the explicit sentinel
    // "unassigned" as meaning "do not filter by player".
    if (!color || color === "unassigned") {
      valid = true; /* Not filtering based on current player */
    } else {
      valid = false;
      for (let r = 0; !valid && r < (corner.roads || []).length; r++) {
        const rr = corner.roads[r];
        if (rr == null) {
          continue;
        }
        const placementsRoads = game.placements && game.placements.roads;
        valid = !!(placementsRoads && placementsRoads[rr] && placementsRoads[rr].color === color);
      }
    }

    for (let r = 0; valid && r < (corner.roads || []).length; r++) {
        if (!corner.roads) {
          break;
        }
        const ridx = corner.roads[r];
        if (ridx == null || layout.roads[ridx] == null) {
          continue;
        }
        const road = layout.roads[ridx];
        for (let c = 0; valid && c < (road.corners || []).length; c++) {
          /* This side of the road is pointing to the corner being validated.
           * Skip it. */
          if (road.corners[c] === cornerIndex) {
            continue;
          }
          /* There is a settlement within one segment from this
           * corner, so it is invalid for settlement placement */
          const cc = road.corners[c] as number;
          const ccPlacement = game.placements && game.placements.corners ? game.placements.corners[cc] : undefined;
          const ccColor = ccPlacement ? ccPlacement.color : undefined;
          if (ccColor && ccColor !== "unassigned") {
            valid = false;
          }
        }
      }
    if (valid) {
      /* During initial placement, if volcano is enabled, do not allow
       * placement on a corner connected to the volcano (robber starts
       * on the volcano) */
      if (
        !(
          game.state === "initial-placement" &&
          isRuleEnabled(game, "volcano") &&
          game.robber &&
          layout.tiles &&
          layout.tiles[game.robber] &&
          Array.isArray(layout.tiles[game.robber]?.corners) &&
          layout.tiles[game.robber]?.corners.indexOf(cornerIndex as number) !== -1
        )
      ) {
        limits.push(cornerIndex);
      }
    }
  });

  return limits;
};

export { getValidCorners, getValidRoads, isRuleEnabled };
