import { JavaObject, cast_java_lang_Object } from '../../java/lang/JavaObject';
import { KursblockungStatic, cast_de_nrw_schule_svws_core_kursblockung_KursblockungStatic } from '../../core/kursblockung/KursblockungStatic';
import { KursblockungDynDaten, cast_de_nrw_schule_svws_core_kursblockung_KursblockungDynDaten } from '../../core/kursblockung/KursblockungDynDaten';
import { KursblockungAlgorithmusK, cast_de_nrw_schule_svws_core_kursblockung_KursblockungAlgorithmusK } from '../../core/kursblockung/KursblockungAlgorithmusK';
import { KursblockungDynSchueler, cast_de_nrw_schule_svws_core_kursblockung_KursblockungDynSchueler } from '../../core/kursblockung/KursblockungDynSchueler';
import { Logger, cast_de_nrw_schule_svws_logger_Logger } from '../../logger/Logger';
import { System, cast_java_lang_System } from '../../java/lang/System';

export class KursblockungAlgorithmusKMatching extends KursblockungAlgorithmusK {

	private static readonly MAX_RUNDEN_IN_FOLGE_OHNE_VERBESSERUNG : number = 2000;

	private readonly schuelerAlle : Array<KursblockungDynSchueler>;


	/**
	 * Im Konstruktor kann die Klasse die jeweiligen Datenstrukturen aufbauen. Kurse
	 * dürfen in diese Methode noch nicht auf Schienen verteilt werden.
	 * 
	 * @param pLogger Logger für Benutzerhinweise, Warnungen und Fehler.
	 * @param pDynDat Die dynamischen Blockungsdaten.
	 */
	public constructor(pLogger : Logger, pDynDat : KursblockungDynDaten) {
		super(pLogger, pDynDat);
		this.schuelerAlle = this.dynDaten.gibSchuelerArray(false);
	}

	/**
	 * Der Algorithmus entfernt zunächst alle SuS aus ihren Kursen. Anschließend
	 * werden die Kurse zufällig verteilt. Anschließend verändert der Algorithmus
	 * die Lage eines zufälligen Kurses. Falls sich die Bewertung verschlechter,
	 * wird die Veränderung rückgängig gemacht.
	 */
	public berechne(pMaxTimeMillis : number) : void {
		if (this.dynDaten.gibKurseDieFreiSindAnzahl() === 0) {
			return;
		}
		let timeStart : number = System.currentTimeMillis();
		this.dynDaten.aktionSchuelerAusAllenKursenEntfernen();
		this.dynDaten.aktionKurseFreieZufaelligVerteilen();
		this.dynDaten.aktionZustandSpeichernK();
		let countKeineVerbesserung : number = 0;
		while (System.currentTimeMillis() - timeStart < pMaxTimeMillis) {
			if (this.berechneSchritt()) {
				countKeineVerbesserung = 0;
			} else {
				countKeineVerbesserung++;
				if (countKeineVerbesserung === KursblockungAlgorithmusKMatching.MAX_RUNDEN_IN_FOLGE_OHNE_VERBESSERUNG) {
					break;
				}
			}
		}
	}

	/**
	 * Die Lage einiger Kurse wird verändert. Falls sich die Bewertung
	 * verschlechter, wird die Veränderung rückgängig gemacht.
	 */
	private berechneSchritt() : boolean {
		let maxChanges : number = 1;
		while (Math.random() < 0.5) {
			maxChanges++;
		}
		for (let changes : number = 0; changes < maxChanges; changes++){
			this.dynDaten.aktionSchuelerAusAllenKursenEntfernen();
			this.dynDaten.aktionKursFreienEinenZufaelligVerteilen();
			this.verteileSuS();
			if (this.dynDaten.gibBewertungJetztBesserAlsK() > 0) {
				this.dynDaten.aktionZustandSpeichernK();
				return true;
			}
		}
		this.dynDaten.aktionZustandLadenK();
		return false;
	}

	/**
	 * Verteilt die SuS. Multikurse werden zufällig verteilt. Alle anderen Kurse mit
	 * Hilfe eines Matching-Algorithmus.
	 */
	private verteileSuS() : void {
		let perm : Array<number> = KursblockungStatic.gibPermutation(this.schuelerAlle.length);
		for (let p : number = 0; p < perm.length; p++){
			let i : number = perm[p];
			let schueler : KursblockungDynSchueler | null = this.schuelerAlle[i];
			schueler.aktionKurseZufaelligVerteilen(true);
			schueler.aktionKurseMitBipartiteMatchingVerteilen();
		}
	}

	isTranspiledInstanceOf(name : string): boolean {
		return ['de.nrw.schule.svws.core.kursblockung.KursblockungAlgorithmusK', 'de.nrw.schule.svws.core.kursblockung.KursblockungAlgorithmusKMatching'].includes(name);
	}

}

export function cast_de_nrw_schule_svws_core_kursblockung_KursblockungAlgorithmusKMatching(obj : unknown) : KursblockungAlgorithmusKMatching {
	return obj as KursblockungAlgorithmusKMatching;
}
