package classifier;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Packet;
import weka.clusterers.SimpleKMeans;
import weka.core.Instance;
import weka.core.Instances;

/**
 * Unsupervised Example: K Means Clustering
 *
 * @author Andreas T. Meyer-Berg
 */
public class KMeansClustering extends BasicPacketClassifier {

	/**
	 * Clusterer
	 */
	private SimpleKMeans clusterer;

	/**
	 * Number of Clusters
	 */
	protected int NUMBER_OF_CLUSTERS = 16;
	protected double[] stdv = new double[NUMBER_OF_CLUSTERS];
	/**
	 * Initializes the k means clusterer
	 */
	public KMeansClustering() {
		super();
		clusterer = new SimpleKMeans();
		clusterer.setSeed(42);/*
		clusterer.setCanopyPeriodicPruningRate(100);
		clusterer.setCanopyT1(0.5);
		clusterer.setCanopyT2(1.0);*/
		try {
			clusterer.setNumClusters(this.NUMBER_OF_CLUSTERS);
		} catch (Exception e) {
			System.out.println("Error while building cluster");
			e.printStackTrace();
		}
	}

	@Override
	public void trainModel(Instances instances) {
		try {
			clusterer.buildClusterer(instances);
			double[] sumOfSquares = new double[NUMBER_OF_CLUSTERS];
			for(Instance i: instances) {
				/**
				 * Id of the closest cluster centroid
				 */
				int x = clusterer.clusterInstance(i);
				/**
				 * centroid instance
				 */
				Instance center = clusterer.getClusterCentroids().get(x);
				/**
				 * Distance
				 */
				double dist = clusterer.getDistanceFunction().distance(center, i);
				sumOfSquares[x] += dist*dist;
			}
			/**
			 * Calculate Standard Deviations
			 */
			for(int i = 0; i<NUMBER_OF_CLUSTERS; i++)
				this.stdv[i] = Math.sqrt(sumOfSquares[i]);
		} catch (Exception e) {
			System.out.println("Failed while training the classifier");
			e.printStackTrace();
		}
	}
	
	@Override
	public double classifyInstance(Instance instance, Packet origin) throws Exception {
		/**
		 * Id of the closest cluster centroid
		 */
		int x = clusterer.clusterInstance(instance);
		/**
		 * centroid instance
		 */
		Instance center = clusterer.getClusterCentroids().get(x);
		
		double dist = clusterer.getDistanceFunction().distance(center, instance);
		if(dist < stdv[x])
			return 0;
		else
			return Double.MAX_VALUE;
		
	}

	@Override
	public long getClassificationStart() {
		return 3600000;
	}

	@Override
	public String getAlgoName() {
		return "KNN";
	}
}
