package projects.coloriage.nodes.nodeImplementations;

import java.awt.Color;
import java.awt.Graphics;
import java.util.*;

import sinalgo.configuration.WrongConfigurationException;
import sinalgo.gui.transformation.PositionTransformation;
import sinalgo.nodes.Node;
import sinalgo.nodes.edges.Edge;
import sinalgo.nodes.messages.Inbox;
import projects.coloriage.nodes.timers.*;
import projects.coloriage.nodes.messages.*;
import sinalgo.nodes.messages.Message;

/*
 * La classe "donnee" ci-dessous est utilisŽe  
 * pour stocker l'Žtat d'un voisin, ici sa couleur.
 * Les Žtats de tous les voisins 
 * seront ensuite stockŽs dans une table de hachage 
 */

class donnee 
{
	int couleur; 

	donnee(int couleur){
		this.couleur=couleur;
	}
}

/*
 * La classe "CNode" ci-dessous implŽmente le code de chaque noeud
 */

public class CNode extends Node {
	
	private int couleur; // la couleur du noeud
	
	/*
	 *  ci-dessous "nb" reprŽsente le nombre de couleurs total
	 *  le tableau "tab" stocke les codes couleur
	 */
	
	private final int nb = 10;
	private final Color tab[] = {Color.BLUE,Color.CYAN,Color.GREEN,Color.LIGHT_GRAY,Color.MAGENTA,Color.ORANGE,Color.PINK,Color.RED,Color.WHITE,Color.YELLOW};

	/* La table de hachage "etatvoisin" stocke le dernier Žtat connu de chauqe voisin
	 * 
	 */
	private Hashtable<Integer,donnee> etatvoisin;
	
	public int getCouleur(){
		return couleur;
	}

	public Color RGBCouleur(){
		return tab[getCouleur()];
	}
	
	public void setCouleur(int c) {
		this.couleur=c;
	}
	
	
	/* La fonction ci-dessous
	 * est utilisŽe pour tirer au hasard une couleur 
	 * parmi les nb disponibles
	 */
	public void initCouleur(int range){
		setCouleur((int) (Math.random() * range) % range);	
	}
	
	/*
	 * La fonction "compute" est lancŽe ˆ chaque rŽception 
	 * de message. Elle permet de changer la couleur du noeud si nŽcessaire
	 */
	public void compute(){
		boolean same=false;
		Iterator<Edge> it=this.outgoingConnections.iterator();
		boolean SC[]=new boolean[nb];
		
		for (int i=0;i<SC.length;i++)
			SC[i]=false;
		
		while(it.hasNext()){
			Edge e=it.next();
			donnee tmp=etatvoisin.get(new Integer(e.endNode.ID));
			if(tmp!=null){
				if(tmp.couleur==this.getCouleur()){
					same=true;
				}
			SC[tmp.couleur]=true;
			}
		}
			
		if (same){
			int dispo=0;	
			for (int i=0;i<SC.length;i++)
				if(SC[i]==false) dispo++; 
			if (dispo == 0) return;
			
			int choix= ((int) (Math.random() * 10000)) % dispo + 1; 
			int i=0;
			
			
			while(choix > 0){
				if(SC[i]==false)
					choix--;
				if(choix>0) i++;			
			}			
			this.setCouleur(i);
		}
	}
	
	/* La fonction ci-dessous est appelŽe
	 * ˆ chque rŽception de message.
	 */
	public void handleMessages(Inbox inbox) {
		
		if(inbox.hasNext()==false) return;
		
		while(inbox.hasNext()){
			
			Message msg=inbox.next();
			
			if(msg instanceof CMessage){
			/*
			 * Chaque message contient l'Žtat d'un voisin.
			 * On mets alors la table de hachage ˆ jour
			 * Puis on rŽŽvalue la couleur du noeud.
			 */
			donnee tmp=new donnee(((CMessage) msg).couleur);	
            etatvoisin.put(new Integer(((CMessage) msg).id),tmp);
            compute();
			}
		}		

	}

	public void preStep() {}

	/*
	 * La fonction ci-dessous est appelŽe au dŽmarrage uniquement
	 * On initialise la couleur du noeud au hasard
	 * On charge le premier timer
	 * On crŽe la table de hachage
	 */
	
	public void init() {
		initCouleur(nb);
		(new CTimer(this,50)).startRelative(50,this); 
		this.etatvoisin=new Hashtable<Integer, donnee>(this.outgoingConnections.size());
		
	}

	public void neighborhoodChange() {}

	public void postStep() {}
	
	public String toString() {
		String s = "Node(" + this.ID + ") [";
		Iterator<Edge> edgeIter = this.outgoingConnections.iterator();
		while(edgeIter.hasNext()){
			Edge e = edgeIter.next();
			Node n = e.endNode;
			s+=n.ID+" ";
		}
		return s + "]";
	}

	public void checkRequirements() throws WrongConfigurationException {}
	
	/*
	 * La fonction ci-dessous affiche le noeud
	 */
	
	public void draw(Graphics g, PositionTransformation pt, boolean highlight) {
		Color c;
		this.setColor(this.RGBCouleur());
		
		String text = ""+this.ID;
		c=Color.BLACK;		
		
		super.drawNodeAsDiskWithText(g, pt, highlight, text, 20, c);
	}


}
