package model;

import util.Position;

import java.util.*;

public class FirefighterBoard implements Board<List<ModelElement>> {
  private final int columnCount;
  private final int rowCount;
  private final int initialFireCount;
  private final int initialFirefighterCount;
  private final int initialCloudCount;
  private final model.TargetStrategy targetStrategy = new model.TargetStrategy();
  private List<Firefighter> firefighters;
  private Fire fire;
  private final Map<Position, List<Position>> neighbors = new HashMap<>();
  private List<Cloud> clouds;
  private final Position[][] positions;
  private int step = 0;
  private final Random randomGenerator = new Random();

  public FirefighterBoard(int columnCount, int rowCount, int initialFireCount, int initialFirefighterCount, int initialCloudCount) {
    this.columnCount = columnCount;
    this.rowCount = rowCount;
    this.positions = new Position[rowCount][columnCount];
    for (int column = 0; column < columnCount; column++)
      for (int row = 0; row < rowCount; row++)
        positions[row][column] = new Position(row, column);
    for (int column = 0; column < columnCount; column++)
      for (int row = 0; row < rowCount; row++) {
        List<Position> list = new ArrayList<>();
        if (row > 0) list.add(positions[row - 1][column]);
        if (column > 0) list.add(positions[row][column - 1]);
        if (row < rowCount - 1) list.add(positions[row + 1][column]);
        if (column < columnCount - 1) list.add(positions[row][column + 1]);
        neighbors.put(positions[row][column], list);
      }
    this.initialFireCount = initialFireCount;
    this.initialFirefighterCount = initialFirefighterCount;
    this.initialCloudCount = initialCloudCount;
    initializeElements();
  }

  public void initializeElements() {
    firefighters = new ArrayList<>();
    clouds = new ArrayList<>();
    Set<Position> firePositions = new HashSet<>();
    for (int index = 0; index < initialCloudCount; index++) {
      clouds.add(new Cloud(randomPosition(), neighbors));
    }
    for (int index = 0; index < initialFireCount; index++)
      firePositions.add(randomPosition());
    fire = new Fire(firePositions, neighbors);
    for (int index = 0; index < initialFirefighterCount; index++)
      firefighters.add(new Firefighter(randomPosition()));
    for (int index = 0; index < initialCloudCount; index++) {
      clouds.add(new Cloud(randomPosition(), neighbors));
    }
  }

  private Position randomPosition() {
    return new Position(randomGenerator.nextInt(rowCount), randomGenerator.nextInt(columnCount));
  }

  @Override
  public List<ModelElement> getState(Position position) {
    List<ModelElement> result = new ArrayList<>();
    for (Cloud cloud : clouds) {
      if (cloud.getPosition().equals(position)) {
        result.add(ModelElement.CLOUD);
      }
    }

    for (Firefighter firefighter : firefighters)
      if (firefighter.getPosition().equals(position))
        result.add(ModelElement.FIREFIGHTER);

    if (fire.getFirePositions().contains(position))
      result.add(ModelElement.FIRE);
    return result;
  }

  @Override
  public int rowCount() {
    return rowCount;
  }

  @Override
  public int columnCount() {
    return columnCount;
  }

  public List<Position> updateToNextGeneration() {
    List<Position> modifiedPositions = updateFirefighters();
    if (step % 2 == 0){
      modifiedPositions.addAll(fire.spreadFire());
    }
    // Déplacer les nuages et éteindre les feux
    for (Cloud cloud : clouds) {
      //cloud.moveRandomly();
      //cloud.extinguishFire(fire);
      cloud.moveAndExtinguishFire(fire);
      cloud.extinguishFireCloud(fire);
      //modifiedPositions.addAll(fire.spreadFire());

    }
    step++;

    return modifiedPositions;
  }

  @Override
  public int stepNumber() {
    return step;
  }

  private List<Position> updateFirefighters() {
    List<Position> modifiedPosition = new ArrayList<>();
    for (Firefighter firefighter : firefighters) {

      modifiedPosition.add(firefighter.getPosition());

      Position newPosition = firefighter.moveToBestPosition(targetStrategy, fire.getFirePositions(), neighbors);
      firefighter.setPosition(newPosition);

      modifiedPosition.add(newPosition);

      firefighter.extinguish(newPosition, fire.getFirePositions());

      // Éteindre les feux dans les positions voisines
      List<Position> adjacentFires = neighbors.get(newPosition).stream()
              .filter(fire.getFirePositions()::contains)
              .toList();
      for (Position firePosition : adjacentFires) {
        firefighter.extinguish(firePosition, fire.getFirePositions());
        modifiedPosition.add(firePosition);
      }

    }
    return modifiedPosition;
  }

  @Override
  public void reset() {
    step = 0;
    initializeElements();
  }

  @Override
  public void setState(List<ModelElement> state, Position position) {
    fire.getFirePositions().remove(position);
    firefighters.removeIf(f -> f.getPosition().equals(position));
    clouds.removeIf(c -> c.getPosition().equals(position));

    for (ModelElement element : state) {
      switch (element) {
        case FIRE -> fire.getFirePositions().add(position);
        case FIREFIGHTER -> firefighters.add(new Firefighter(position));
        case CLOUD -> clouds.add(new Cloud(position, neighbors));
      }
    }
  }
}