package model;

import util.Position;
import util.TargetStrategy;

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 int initialRoadCount;
  private List<FireFighter> firefighters;


  private List<Element> elements;
  private Map<Position, Fire> fires = new HashMap<>();
  private List<Cloud> clouds;
  private final Map<Position, List<Position>> neighbors = new HashMap<>();
  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,int initialRoadCount) {
    this.columnCount = columnCount;
    this.rowCount = rowCount;
    this.positions = new Position[rowCount][columnCount];

    initializePositionsAndNeighbors();

    this.initialFireCount = initialFireCount;
    this.initialFirefighterCount = initialFirefighterCount;
    this.initialCloudCount = initialCloudCount;
    this.initialRoadCount = initialRoadCount;
    initializeElements();
  }

  private void initializePositionsAndNeighbors() {
    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++) {
        neighbors.put(positions[row][column], calculateNeighbors(row, column));
      }
    }
  }



  public void initializeElements() {
    elements = new ArrayList<>();

    fires.clear();


    for (int index = 0; index < initialFireCount; index++) {
      Position position = randomPosition();
      fires.put(position, new Fire(position));
    }

    for (int index = 0; index < initialFirefighterCount; index++) {
      elements.add(new FireFighter(randomPosition()));
    }
    for (int index = 0; index < initialCloudCount; index++) {

      elements.add(new Cloud(randomPosition()));
    }

    for (int index = 0; index < initialRoadCount; index++) {
      elements.add(new Road(randomPosition()));
    }

  }

  private List<Position> calculateNeighbors(int row, int column) {
    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]);
    return list;
  }

  private Position randomPosition() {
    return positions[randomGenerator.nextInt(rowCount)][randomGenerator.nextInt(columnCount)];
  }

  @Override
  public List<ModelElement> getState(Position position) {
    List<ModelElement> result = new ArrayList<>();

    for(Element element : elements){
      if(element.getPosition().equals(position))result.add(element.getElement());
    }
    if (fires.containsKey(position)) result.add(ModelElement.FIRE);

    return result;
  }






  private List<Position> updateFires() {
    List<Position> modifiedPositions = new ArrayList<>();
    if (step % 2 == 0) {
      Set<Position> newFirePositions = new HashSet<>();
      for (Position fire : new HashSet<>(fires.keySet())) {
        List<Position> neighboursAvaiable = neighbors.get(fire);
        neighboursAvaiable.removeAll(getRoadsPosition());
        newFirePositions.addAll(neighbors.get(fire));
      }
      for (Position position : newFirePositions) {
        if (!fires.containsKey(position)) {
          fires.put(position, new Fire(position));
          modifiedPositions.add(position);
        }
      }
    }
    return modifiedPositions;
  }








  public List<Position> updateElements(){
    List<Position> modifiedPositions = new ArrayList<>();
    for (Element element : elements) {
      if(element.getClass().equals(Road.class)){
        continue;
      }
      modifiedPositions.addAll(element.update(this));
    }
    return modifiedPositions;
  }


  public Position randomNeighbor(Position position) {
    List<Position> neighborPositions = neighbors.get(position);
    return neighborPositions.get(randomGenerator.nextInt(neighborPositions.size()));
  }




  public List<Position> updateToNextGeneration() {
    List<Position> modifiedPositions = updateElements();
    modifiedPositions.addAll(updateFires());
    step++;
    return modifiedPositions;
  }


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

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

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

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

  public void extinguish(Position position) {
    if (fires.containsKey(position)) {
      fires.remove(position);
    }
  }


  public Map<Position,Fire> getFires(){
    return this.fires;
  }

  public Map<Position, List<Position>> getNeighbors() {
    return neighbors;
  }

  public List<Position> getRoadsPosition(){
    List<Position>roadPosition = new ArrayList<>();
    for(Element road: elements){
      if(road.getClass().equals(Road.class)){
        roadPosition.add(road.getPosition());
      }
    }
    return roadPosition;
  }


  @Override
  public void setState(List<ModelElement> state, Position position) {
    extinguish(position);
    elements.removeIf(element -> element.getPosition().equals(position));


    for (ModelElement element : state) {
      switch (element) {
        case FIRE -> fires.put(position, new Fire(position));
        case FIREFIGHTER -> firefighters.add(new FireFighter(position));
        case CLOUD ->  clouds.add(new Cloud(position));
      }
    }
  }
}