package model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import util.Matrix;
import util.Position;
import util.PositionUtil;

public class FireFighterScenario extends EntityScenario implements Board<Square> {

  private Matrix<Square> matrix;
  private int step;

  private int initialFireCount;
  private int initialFireFightersCount;
  private int intialCloudCount;
  private int initialMoutainCount;

  public FireFighterScenario(int columns, int rows, int initialFireCount, int initialFireFightersCount, int initialCloudCount, int initialMoutainCount) {
    this.matrix = new Matrix<Square>(columns, rows);
    this.initialFireCount = initialFireCount;
    this.initialFireFightersCount = initialFireFightersCount;
    this.intialCloudCount = initialCloudCount;
    this.initialMoutainCount = initialMoutainCount;
    initScenario(matrix);
    placeInitialActors(initialFireCount, initialFireFightersCount, initialCloudCount, initialMoutainCount);
    this.step = 0;
  }

  private void placeInitialActors(int initialFireCount, int initialFireFightersCount, int initialCloudCount, int initialMoutainCount) {
    int fireCount = 0;
    int fireFighterCount = 0;
    int cloudCount = 0;
    int moutainCount = 0;
    int chance = 5; // Chance initiale en pourcentage
    Random random = new Random();
    List<Position> positions = new ArrayList<>();
    for (int x = 0; x < matrix.getRows(); x++) {
      for (int y = 0; y < matrix.getColumns(); y++) {
        positions.add(new Position(x, y));
      }
    }
    setSquare(new Square(new Position(0,0), new MotorizedFireFighter(new Position(0,0), this)));
    while (fireCount < initialFireCount || fireFighterCount < initialFireFightersCount || cloudCount < intialCloudCount) {
      Collections.shuffle(positions); // Mélange les positions pour un parcours aléatoire

      for (Position pos : positions) {
        if (getStates(pos).isEmpty()) {
          if (fireCount < initialFireCount && random.nextInt(100) < chance) {
            setSquare(new Square(pos, new Fire(pos, this, 1)));
            fireCount++;
            if (fireCount == initialFireCount && fireFighterCount == initialFireFightersCount && cloudCount == initialCloudCount  && moutainCount == initialMoutainCount) {
              return;
            }
            continue;
          }

          if (fireFighterCount < initialFireFightersCount && random.nextInt(100) < chance) {
            setSquare(new Square(pos, new FireFighter(pos, this, 1)));
            fireFighterCount++;
            if (fireCount == initialFireCount && fireFighterCount == initialFireFightersCount && cloudCount == initialCloudCount  && moutainCount == initialMoutainCount) {
              return;
            }
          }

          if (cloudCount < intialCloudCount && random.nextInt(100) < chance) {
            setSquare(new Square(pos, new Cloud(pos, this, 1)));
            cloudCount++;
            if (fireCount == initialFireCount && fireFighterCount == initialFireFightersCount && cloudCount == initialCloudCount  && moutainCount == initialMoutainCount) {
              return;
            }
          }

          if (moutainCount < initialMoutainCount && random.nextInt(100) < chance) {
            setSquare(new Square(pos, new Mountain(pos, 1)));
            moutainCount++;
            if (fireCount == initialFireCount && fireFighterCount == initialFireFightersCount && cloudCount == initialCloudCount && moutainCount == initialMoutainCount) {
              return;
            }
          }
        }
      }

      // Augmente la chance de placement après chaque parcours complet
      chance = Math.min(chance + 5, 100); // Ne dépasse pas 100%
    }
  }

  public Square getStates(Position position) {
    if (position.x() > matrix.size() || position.y() > matrix.size()) {
      throw new IllegalArgumentException(
          "The position x:" + position.x() + " y:" + position.y() + " is out of the board.");
    }
    return matrix.get(position.x(), position.y());
  }

  public void setSquare(Square square) {
    Position position = square.getPosition();
    if (!(getStates(position).isEmpty())) {
      return;
    }
    if (doesPositionExist(position)) {
      matrix.set(position.x(), position.y(), square);
    }
  }

  public void setSquare(Square square, boolean replaceStates) {
    Position position = square.getPosition();
    if (!(getStates(position).isEmpty()) && !replaceStates) {
      return;
    }
    matrix.set(position.x(), position.y(), square);
  }

  public void addEntityAtSquare(Entity entity, Position position) {
    if (!(getStates(position).isEmpty())) {
      return;
    }
    if (doesPositionExist(position)) {
      matrix.get(position.x(), position.y()).addEntity(entity);
    }
  }

  public void addEntityAtSquare(Entity entity, Position position, boolean replaceStates) {
    if (!(getStates(position).isEmpty()) && !replaceStates) {
      return;
    }
    matrix.get(position.x(), position.y()).addEntity(entity);
    ;
  }

  public int rowCount() {
    return matrix.getRows();
  }

  public int columnCount() {
    return matrix.getColumns();
  }

  @Override
  public void clearCaseFrom(Entity entity, Position position) {
    matrix.get(position.x(), position.y()).getEntities().removeIf(element -> element.equals(entity));
  }

  public List<Position> updateToNextGeneration() {
    ArrayList<Position> changedPositions = new ArrayList<>();
    Iterator<Square> iterator = matrix.iterator();
    while (iterator.hasNext()) {
      Square s = iterator.next();
      if (s.isEmpty())
        continue;
      if (s.getMaxAge() == 0) {
        s.incrementAllAges();
        continue;
      }
      if (s.getMaxAge() == step + 1) {
        continue;
      }
      List<Entity> entities = new ArrayList<>(s.getEntities());
      for (Entity e : entities) {
        e.incrementAge();
        changedPositions.addAll(e.nextTurn(this));
      }

    }
    this.step = this.step + 1;
    // matrix.displayMatrix();
    return changedPositions;
  }

  public Position getNearestEntity(Position fromPos, Class<?> entityType) {
    int rows = matrix.getRows();
    int cols = matrix.getColumns();
    Position nearestPosition = fromPos;

    // Définir la distance maximale possible
    int maxDistance = rows + cols;
    // Parcourir les distances croissantes à partir de 1
    for (int distance = 1; distance < maxDistance; distance++) {
      List<Position> positionsAtDistance = PositionUtil.getPositionsAtManhattanDistance(fromPos, distance, rows, cols);

      for (Position currentPos : positionsAtDistance) {
        Square currentSquare = matrix.get(currentPos.x(), currentPos.y());
        for (Entity currentEntity : currentSquare.getEntities()) {
          if (entityType.isInstance(currentEntity)) {
            // Dès qu'une entité est trouvée à cette distance, elle est la plus proche
            // possible
            return currentPos;
          }
        }
      }
    }

    return nearestPosition; // Retourne null si aucune entité n'est trouvée
  }

  public void reset() {
    step = 0;
    matrix.clear();
    initScenario(matrix);
    placeInitialActors(initialFireCount, initialFireFightersCount, intialCloudCount, initialMoutainCount);
  }

  public int stepNumber() {
    return this.step;
  }

  @Override
  public boolean doesPositionExist(Position position) {
    return matrix.validateIndex(position);
  }

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

  @Override
  public boolean doesSquareContainEntity(Position squarePos, Class<?> entityType) {
    return getStates(squarePos).getEntities().stream().anyMatch(entityType::isInstance);
  }

  @Override
  public boolean isPositionEmpty(Position position) {
    return getStates(position).isEmpty();
  }
}