package com.hectorarranz.java.checks;

import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.semantic.Type;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.Tree.Kind;
import org.sonar.plugins.java.api.tree.VariableTree;

import com.google.common.collect.ImmutableList;
@Rule(
		key = "ConstantsAsEnumRule", 
		name = "Constants As Enums",
		description = "Si se tienen varias constantes equivalentes puede que sea mejor agruparlas en un Enum",
		priority = Priority.INFO,
		tags = {"code-smell"})
public class ConstantsAsEnumRule extends IssuableSubscriptionVisitor {

	private static final int DEFAULT_VALUE = 2;

	@RuleProperty(
			defaultValue = "" + DEFAULT_VALUE, 
			description = "Minimo numero de constantes en una clase para que recomiende usar enum")
	protected int minimo = DEFAULT_VALUE;

	@Override
	public List<Kind> nodesToVisit() {
		return ImmutableList.of(Kind.CLASS);
	}
	
	@Override
	public void visitNode(Tree tree){
		Map<Type, Integer> contadoresPorTipo = new Hashtable<>();
		Map<Type, Tree> primeraOcurrenciaDelTipo = new Hashtable<>();
		ClassTree clase = (ClassTree) tree;
		List<Tree> miembrosDeLaClase = clase.members();
		for (Tree miembro : miembrosDeLaClase) {
			if(miembro.is(Kind.VARIABLE)){
				VariableTree variable = (VariableTree) miembro;
				if(esUnaConstante(variable)){
					Type tipo = variable.symbol().type();
					if(contadoresPorTipo.containsKey(tipo)){
						Integer ocurrenciasActuales = contadoresPorTipo.get(tipo);
						contadoresPorTipo.put(tipo, ocurrenciasActuales.intValue()+1);
					}else{
						contadoresPorTipo.put(tipo, 1);
						primeraOcurrenciaDelTipo.put(tipo, variable);
					}
				}
			}
		}
		
		Set<Type> tipos = contadoresPorTipo.keySet();
		for (Type tipo : tipos) {
			Integer ocurrencias = contadoresPorTipo.get(tipo);
			if(ocurrencias.intValue()>=minimo){
				Tree primeraOcurrencia = primeraOcurrenciaDelTipo.get(tipo);
				
				reportIssue(primeraOcurrencia, "Se estan usando demasiadoas constantes del tipo "+tipo.name()+" Puede que sea mejor agruparlas en un Enum");
			}
		}
	}

	private boolean esUnaConstante(VariableTree variable) {
		return variable.symbol().isStatic() && variable.symbol().isFinal();
	}

}
