package projet.iut.jeu_de_la_vie.model.cellulesVivantes;

import projet.iut.jeu_de_la_vie.model.cellule.Cellule;
import projet.iut.jeu_de_la_vie.model.cellule.observer.ObserverCellule;
import projet.iut.jeu_de_la_vie.model.cellule.Position;
import projet.iut.jeu_de_la_vie.model.cellulesVivantes.observer.ObservableCV;


import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Représentation des cellules vivantes sur le plateau. Elle se met à jours automatiquement.
 * @author Yohann Breuil
 * @author Allan Point
 */
public class CellulesVivantes extends ObservableCV implements ObserverCellule {

	/**
	 * Dictionaire contenant toutes les cellules vivantes
	 */
	private ConcurrentHashMap<Position, Cellule> cellVivantes;

	public CellulesVivantes(){
		this(new ConcurrentHashMap<>());
	}

	private CellulesVivantes(ConcurrentHashMap<Position, Cellule> cellVivantes){
		this.cellVivantes = cellVivantes;
	}

	/**
	 * Récuperer une cellule vivante
	 * @param x Absisse de la cellule
	 * @param y Ordonée de la cellule
	 * @return La cellule (x; y) si elle est vivante. Sinon null
	 */
	public Cellule getAt(int x, int y){
		Position p = new Position(x, y);
		return cellVivantes.get(p);
	}

	/**
	 * Ajoute une paire clef:valeur (Postion:Cellule) dans le dictionaire contenant les cellules vivantes
	 * @param cell Cellule à ajouter
	 * @see Position
	 * @see Cellule
	 */
	private void addPeer(Cellule cell){
		cellVivantes.put(cell.getPosition(), cell);
		notifier(cell);
	}

	/**
	 * Retir une paire clef:valeur (Postion:Cellule) du dictionaire contenant les cellules vivantes
	 * @param cell Cellule à retirer
	 * @see Position
	 * @see Cellule
	 */
	private void rmPeer(Cellule cell){
		cellVivantes.remove(cell.getPosition());
		notifier(cell);
	}

	/**
	 * Comportement lors ce que le cellule notifie l'objet CellulesVivantes.
	 * Ici on ajoute ou retire la cellule du dictionaire qui contient les cellules vivante en fonction de la cellule qui à notifiée.
	 * @param cellule Cellule qui à notifiée
	 */
	@Override
	public void update(Cellule cellule) {
		if(cellule.isAlive()){
			addPeer(cellule);
		} else {
			rmPeer(cellule);
		}
	}

	/**
	 * Cloner l'objet
	 * @return Le meme objet CellulesVivantes avec une référence diférente
	 */
	public CellulesVivantes clone(){
		return new CellulesVivantes(new ConcurrentHashMap<>(cellVivantes));
	}

	/**
	 * Nétoie le dictionaire contenant les cellules vivantes
	 */
	public void reset(){
		cellVivantes = new ConcurrentHashMap<>();
	}

	public ConcurrentHashMap<Position, Cellule> getCells(){
		return cellVivantes;
	}
}
