package model;


import java.util.List;
import java.util.Map;
import java.util.Random;

import app.SimulatorApplication;
import model.firefighterscenario.Fire;
import model.firefighterscenario.Mountain;
import model.firefighterscenario.Road;
import model.firefighterscenario.Rockery;
import util.Matrix;
import util.PathGenerator;
import util.Position;
import util.PositionUtil;

public class Scenario implements Board<Square>{

  private Matrix<Square> matrix;
  protected int step;
  protected int turnsToSpawnAirTanker;

  private Map<EntityFactory, Integer> initialMap;

  public Scenario(int columns, int rows, Map<EntityFactory, Integer> initialMap) {
    this.matrix = new Matrix<Square>(columns, rows);
    initScenario(matrix);
    this.turnsToSpawnAirTanker = SimulatorApplication.TURNS_FOR_SPAWNING_AIRTANKER;
    this.step = 0;
    this.initialMap = initialMap;
    placeInitialEntities(initialMap);
  }
  public void initScenario(Matrix<Square> matrix){
    for(int x = 0; x < matrix.getRows(); x++){
        for(int y = 0; y < matrix.getColumns(); y++){
            Square s = new Square(new Position(x, y), new EmptySquare(new Position(x,y)));
            matrix.set(x,y, s);
        }
    }
}
  protected Matrix<Square> getMatrix(){
    return this.matrix;
  }

  public void placeInitialEntities(Map<EntityFactory, Integer> initialMap) {
    EntitySpawner spawner = new EntitySpawner(this);
    spawner.spawnEntities(initialMap);
    generateRoads();
    
  }

  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 (doesPositionExist(position)) {
      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 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);
    placeInitialEntities(initialMap);
  }

  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();
  }

  @Override
  public boolean isPositionFree(Position position, int priority) {
    List<Entity> entities = matrix.get(position.x(), position.y()).getEntities();
    for (Entity e : entities) {
      if (e.getPriority() == priority) {
        return false;
      }
    }
    return true;
  }

  private void generateRoads() {
    if(columnCount() < 10 || rowCount() < 10){
      return;
    }
    Random random = new Random();

    // Get board dimensions
    int rowCount = rowCount();      // Number of rows (vertical axis)
    int columnCount = columnCount(); // Number of columns (horizontal axis)

    // Decide randomly whether to set x or y to 0
    boolean setXToZero = random.nextBoolean();

    int x = 0;
    int y = 0;

    if (setXToZero) {
        // x is set to 0, y is random within column bounds
        x = 0;
        y = random.nextInt(columnCount);
    } else {
        // y is set to 0, x is random within row bounds
        x = random.nextInt(rowCount);
        y = 0;
    }

    Position startPosition = new Position(x, y);
    PathGenerator pathGenerator = new PathGenerator(this, startPosition, 4, columnCount());
    // Call generateEntitiesInLine to place the roads
    List<Position> snake = pathGenerator.generate();
    for(Position p : snake){
      List<Entity> entitiesAtSquare = List.copyOf(getStates(p).getEntities());
      for(Entity e: entitiesAtSquare){
        if(e instanceof Mountain || e instanceof Rockery || e instanceof Fire){
          clearCaseFrom(e, e.getPosition());
        }
      }
      addEntityAtSquare(new Road(p), p);
    }
}

}