package game.map;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * Diese Klasse teilt Burgen in Königreiche auf
 */
public class Clustering {

    private Random random;
    private final List<Castle> allCastles;
    private final int kingdomCount;

    /**
     * Ein neues Clustering-Objekt erzeugen.
     * @param castles Die Liste von Burgen, die aufgeteilt werden sollen
     * @param kingdomCount Die Anzahl von Königreichen die generiert werden sollen
     */
    public Clustering(List<Castle> castles, int kingdomCount) {
        if (kingdomCount < 2)
            throw new IllegalArgumentException("Ungültige Anzahl an Königreichen");

        this.random = new Random();
        this.kingdomCount = kingdomCount;
        this.allCastles = Collections.unmodifiableList(castles);
    }

    /**
     * Gibt eine Liste von Königreichen zurück.
     * Jedes Königreich sollte dabei einen Index im Bereich 0-5 bekommen, damit die Burg richtig angezeigt werden kann.
     * Siehe auch {@link Kingdom#getType()}
     */
    public List<Kingdom> getPointsClusters() {
    	List<Kingdom> kingdoms = new ArrayList<>();
    	
        // Erzeuge Königreiche mit zufälligen Zentren
    	for(int i = 0; i < kingdomCount; i++) {
    		kingdoms.add(new Kingdom(i, allCastles.get(random.nextInt(allCastles.size()))));
    	}
    	
    	boolean changed;
    	Kingdom lastKingdom;
    	
    	// Wiederhole, solange sich die Zuordnungen der Burgen zu den Königreichen ändern
    	do {
    		changed = false;
    		
    		// Ordne jede Burg dem nächstgelegenen Königreich zu
    		for(Castle c : allCastles) {
    			lastKingdom = c.getKingdom();
    			c.setKingdom(this.getNearestKingdom(c, kingdoms));
    			if(c.getKingdom() != lastKingdom) changed = true; // Eine Zuordnung hat sich geändert
    		}
    		
    		// Setze die Zentren der Königreiche neu
    		for(Kingdom k : kingdoms) {
    			double meanX=0, meanY=0, sumX=0, sumY=0;
    			double numCastles = k.getCastles().size();
    			for(Castle c : k.getCastles()) {
    				sumX += c.getLocationOnMap().getX();
    				sumY += c.getLocationOnMap().getY();
    			}
    			meanX = sumX/numCastles;
    			meanY = sumY/numCastles;
    			Point p = new Point();
    			p.setLocation(meanX, meanY);
    			k.setCenter(getNearestCastle(p, allCastles));
    		}
    		
    	}while(changed);

    	return kingdoms;
    }
    
    /**
     * Gibt die dem übergebenen Punkt nächstgelegene Burg zurück
     * @param point der Punkt auf der Karte
     * @param castles die Liste aller Burgen
     * @return die nächstgelegene Burg
     */
    protected Castle getNearestCastle(Point point, List<Castle> castles) {
    	Castle nearest = castles.get(0);
    	for(Castle c : castles) {
    		if(c.distance(point) < nearest.distance(point)) {
    			nearest = c;
    		}
    	}
    	return nearest;
    }
    
    /**
     * Gibt das zur übergebenen Burg nächstgelegene Königreich zurück
     * @param castle die Burg
     * @param kingdoms die Liste aller Königreiche
     * @return das nächstgelegene Königreich
     */
    protected Kingdom getNearestKingdom(Castle castle, List<Kingdom> kingdoms) {
    	Kingdom nearest = kingdoms.get(0);
    	double dist = castle.distance(nearest.getCenter());
    	for(Kingdom k : kingdoms) {
    		if(castle.distance(k.getCenter()) < dist) {
    			nearest = k;
    			dist = castle.distance(nearest.getCenter());
    		}
    	}
    	return nearest;
    }
}
