package gr.uom.java.ast.util.math;

import static gr.uom.java.ast.util.math.DoubleArray.deleteColumns;
import static gr.uom.java.ast.util.math.DoubleArray.deleteRows;
import static gr.uom.java.ast.util.math.DoubleArray.insertColumns;
import static gr.uom.java.ast.util.math.DoubleArray.insertRows;

import gr.uom.java.distance.Entity;

import java.util.ArrayList;
import java.util.HashSet;

public class Hierarchical extends Clustering {
	
	private HashSet<Cluster> clusterSet;
	
	public Hierarchical(double[][] distanceMatrix) {
		this.distanceMatrix = distanceMatrix;
		this.clusterSet = new HashSet<Cluster>();
	}

	public HashSet<Cluster> clustering(ArrayList<Entity> entities) {
		ArrayList<Cluster> clusters = new ArrayList<Cluster>();
		for(Entity entity : entities) {
			Cluster cluster = new Cluster();
			cluster.addEntity(entity);
			clusters.add(cluster);
		}
		while(clusters.size()>2) {
			HashSet<Cluster> clustersNotPresentable = new HashSet<Cluster>();
			double minVal = 2.0;
			int minRow = 0;
			int minCol = 1;
			for(int i=1; i<distanceMatrix.length;i++) {
				for(int j=0;j<i;j++) {
					if (distanceMatrix[i][j] < minVal) {
						minVal = distanceMatrix[i][j];
						minRow = i;
						minCol = j;
					}
				}
			}
			
			if(minVal >= 1.0)
				break;
			if(minRow < minCol) {
				if(clusters.get(minCol).getEntities().size() == 1 && clusters.get(minRow).getEntities().size() > 1) {
					clustersNotPresentable.add(new Cluster(clusters.get(minRow).getEntities()));
				}
				else if(clusters.get(minCol).getEntities().size() > 1 && clusters.get(minRow).getEntities().size() == 1) {
					clustersNotPresentable.add(new Cluster(clusters.get(minCol).getEntities()));
				}
				clusters.get(minRow).addEntities(clusters.get(minCol).getEntities());
				double[] newDistances = new double[distanceMatrix.length-1];
				for(int i=0;i<distanceMatrix.length;i++) {
					if (i != minCol) {
						if (i != minRow) {
							if (distanceMatrix[minRow][i] < distanceMatrix[minCol][i]) {
								if (i > minCol) {
									newDistances[i - 1] = distanceMatrix[minRow][i];
								} else {
									newDistances[i] = distanceMatrix[minRow][i];
								}
							} else {
								if (i > minCol) {
									newDistances[i - 1] = distanceMatrix[minCol][i];
								} else {
									newDistances[i] = distanceMatrix[minCol][i];
								}
							}
						}
						else {
							newDistances[i] = 0.0;
						}
					}
				}
				distanceMatrix = deleteRows(distanceMatrix, minRow, minCol);
				distanceMatrix = deleteColumns(distanceMatrix, minCol);
				distanceMatrix = insertRows(distanceMatrix, minRow, newDistances);
				distanceMatrix = deleteColumns(distanceMatrix, minRow);
				distanceMatrix = insertColumns(distanceMatrix, minRow, newDistances);
				clusters.remove(minCol);
			}
			else {
				if(clusters.get(minRow).getEntities().size() == 1 && clusters.get(minCol).getEntities().size() > 1) {
					clustersNotPresentable.add(new Cluster(clusters.get(minCol).getEntities()));
				}
				else if(clusters.get(minRow).getEntities().size() > 1 && clusters.get(minCol).getEntities().size() == 1) {
					clustersNotPresentable.add(new Cluster(clusters.get(minRow).getEntities()));
				}
				clusters.get(minCol).addEntities(clusters.get(minRow).getEntities());
				double[] newDistances = new double[distanceMatrix.length-1];
				for(int i=0;i<distanceMatrix.length;i++) {
					if (i != minRow) {
						if (i != minCol) {
							if (distanceMatrix[minRow][i] < distanceMatrix[minCol][i]) {
								if (i > minRow) {
									newDistances[i - 1] = distanceMatrix[minRow][i];
								} else {
									newDistances[i] = distanceMatrix[minRow][i];
								}
							} else {
								if (i > minRow) {
									newDistances[i - 1] = distanceMatrix[minCol][i];
								} else {
									newDistances[i] = distanceMatrix[minCol][i];
								}
							}
						}
						else {
							newDistances[i] = 0.0;
						}
					}
					
				}
				distanceMatrix = deleteRows(distanceMatrix, minRow, minCol);
				distanceMatrix = deleteColumns(distanceMatrix, minRow);
				distanceMatrix = insertRows(distanceMatrix, minCol, newDistances);
				distanceMatrix = deleteColumns(distanceMatrix, minCol);
				distanceMatrix = insertColumns(distanceMatrix, minCol, newDistances);
				clusters.remove(minRow);
			}
			clusterSet.removeAll(clustersNotPresentable);
			for(Cluster cluster : clusters) {
				if (!(clusters.size() == 2 && (clusters.get(0).getEntities().size() == 1 || clusters.get(1).getEntities().size() == 1))) {
					if (cluster.getEntities().size() > 1) {
						Cluster c = new Cluster(cluster.getEntities());
						clusterSet.add(c);
					}
				}
			}
		}
		return clusterSet;
	}

}
