import { JavaObject, cast_java_lang_Object } from '../../java/lang/JavaObject';
import { KursblockungAlgorithmusS, cast_de_nrw_schule_svws_core_kursblockung_KursblockungAlgorithmusS } from '../../core/kursblockung/KursblockungAlgorithmusS';
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 { 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 KursblockungAlgorithmusSZufaellig extends KursblockungAlgorithmusS {

	private static readonly MAX_RUNDEN_IN_FOLGE_OHNE_VERBESSERUNG : number = 50;

	private readonly schuelerArr : Array<KursblockungDynSchueler>;

	private readonly perm : Array<number>;

	private readonly nurMultikurse : boolean;


	/**
	 * Im Konstruktor kann die Klasse die jeweiligen Datenstrukturen aufbauen. Kurse
	 * dürfen in diese Methode noch nicht auf Schienen verteilt werden.
	 * 
	 * @param pLogger        Logger zum Protokollieren von Warnungen und Fehlern.
	 * @param pDynDat        Die dynamischen Blockungsdaten.
	 * @param pNurMultikurse Falls TRUE, dann werden nur die Multikurse der SuS
	 *                       verteilt.
	 */
	public constructor(pLogger : Logger, pDynDat : KursblockungDynDaten, pNurMultikurse : boolean) {
		super(pLogger, pDynDat);
		this.nurMultikurse = pNurMultikurse;
		this.schuelerArr = pDynDat.gibSchuelerArray(this.nurMultikurse);
		this.perm = KursblockungStatic.gibPermutation(this.schuelerArr.length);
	}

	/**
	 * Der Algorithmus verteilt die SuS auf ihre Kurse zufällig. Kommt es während
	 * des Verteilens zur Kollision, so wird der Kurs nicht gewählt.
	 */
	public berechne(pMaxTimeMillis : number) : void {
		let timeStart : number = System.currentTimeMillis();
		this.dynDaten.aktionSchuelerAusAllenKursenEntfernen();
		this.dynDaten.gibStatistik().aktionBewertungSpeichernS();
		let countKeineVerbesserung : number = 0;
		while (System.currentTimeMillis() - timeStart < pMaxTimeMillis) {
			if (this.berechneSchritt()) {
				countKeineVerbesserung = 0;
			} else {
				countKeineVerbesserung++;
				if (countKeineVerbesserung === KursblockungAlgorithmusSZufaellig.MAX_RUNDEN_IN_FOLGE_OHNE_VERBESSERUNG) {
					break;
				}
			}
		}
	}

	/**
	 * Der Algorithmus verteilt die SuS ein weiteres Mal zufällig. Falls die
	 * Verteilung schlechter ist, wird der vorherige Zustand wiederhergestellt.
	 * 
	 * @return TRUE, falls der Zustand sich verbessert hat.
	 */
	private berechneSchritt() : boolean {
		let verbesserung : boolean = false;
		KursblockungStatic.aktionPermutiere(this.perm);
		for (let p : number = 0; p < this.schuelerArr.length; p++){
			let i : number = this.perm[p];
			let schueler : KursblockungDynSchueler = this.schuelerArr[i];
			this.dynDaten.gibStatistik().aktionBewertungSpeichernS();
			schueler.aktionZustandSpeichernS();
			schueler.aktionKurseAlleEntfernen();
			schueler.aktionKurseZufaelligVerteilen(this.nurMultikurse);
			let cmp : number = this.dynDaten.gibStatistik().gibBewertung_NW_KD_JetztS();
			if (cmp < 0) {
				schueler.aktionZustandLadenS();
			}
			if (cmp > 0) {
				verbesserung = true;
			}
		}
		return verbesserung;
	}

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

}

export function cast_de_nrw_schule_svws_core_kursblockung_KursblockungAlgorithmusSZufaellig(obj : unknown) : KursblockungAlgorithmusSZufaellig {
	return obj as KursblockungAlgorithmusSZufaellig;
}
