import { JavaMapEntry, cast_java_util_Map_Entry } from '../../../java/util/JavaMapEntry';
import { NavigableSet, cast_java_util_NavigableSet } from '../../../java/util/NavigableSet';
import { JavaSet, cast_java_util_Set } from '../../../java/util/JavaSet';
import { NavigableMap, cast_java_util_NavigableMap } from '../../../java/util/NavigableMap';
import { AVLMapSubCollection, cast_de_nrw_schule_svws_core_adt_map_AVLMapSubCollection } from '../../../core/adt/map/AVLMapSubCollection';
import { AVLMapIntervall, cast_de_nrw_schule_svws_core_adt_map_AVLMapIntervall } from '../../../core/adt/map/AVLMapIntervall';
import { AVLMapSubEntrySetIterator, cast_de_nrw_schule_svws_core_adt_map_AVLMapSubEntrySetIterator } from '../../../core/adt/map/AVLMapSubEntrySetIterator';
import { AVLMapSubKeySet, cast_de_nrw_schule_svws_core_adt_map_AVLMapSubKeySet } from '../../../core/adt/map/AVLMapSubKeySet';
import { JavaString, cast_java_lang_String } from '../../../java/lang/JavaString';
import { SortedSet, cast_java_util_SortedSet } from '../../../java/util/SortedSet';
import { AVLMap, cast_de_nrw_schule_svws_core_adt_map_AVLMap } from '../../../core/adt/map/AVLMap';
import { Comparator, cast_java_util_Comparator } from '../../../java/util/Comparator';
import { AVLMapNode, cast_de_nrw_schule_svws_core_adt_map_AVLMapNode } from '../../../core/adt/map/AVLMapNode';
import { SortedMap, cast_java_util_SortedMap } from '../../../java/util/SortedMap';
import { JavaIterator, cast_java_util_Iterator } from '../../../java/util/JavaIterator';
import { AVLMapSubKeySetIterator, cast_de_nrw_schule_svws_core_adt_map_AVLMapSubKeySetIterator } from '../../../core/adt/map/AVLMapSubKeySetIterator';
import { Collection, cast_java_util_Collection } from '../../../java/util/Collection';
import { JavaObject, cast_java_lang_Object } from '../../../java/lang/JavaObject';
import { AVLMapSubEntrySet, cast_de_nrw_schule_svws_core_adt_map_AVLMapSubEntrySet } from '../../../core/adt/map/AVLMapSubEntrySet';
import { JavaMap, cast_java_util_Map } from '../../../java/util/JavaMap';
import { Vector, cast_java_util_Vector } from '../../../java/util/Vector';
import { AVLMapSubCollectionIterator, cast_de_nrw_schule_svws_core_adt_map_AVLMapSubCollectionIterator } from '../../../core/adt/map/AVLMapSubCollectionIterator';
import { IllegalArgumentException, cast_java_lang_IllegalArgumentException } from '../../../java/lang/IllegalArgumentException';

export class AVLMapSubMap<K, V> extends JavaObject implements NavigableMap<K, V> {

	private readonly _par : AVLMap<K, V>;

	private readonly _iv : AVLMapIntervall<K>;

	private _asc : boolean = false;


	/**
	 * Erstellt eine neue Sub-Map relativ zur übergebenen {@link AVLMap}.
	 * 
	 * @param parent    Die {@link AVLMap} auf der diese Sup-Map operiert.
	 * @param intervall Das {@link AVLMapIntervall} auf das sich diese Sub-Map bezieht.
	 */
	constructor(parent : AVLMap<K, V>, intervall : AVLMapIntervall<K>, asc : boolean) {
		super();
		this._par = parent;
		this._iv = intervall;
		this._asc = asc;
	}

	public toString() : String {
		let s : String | null = "";
		for (let e of this.entrySet()) 
			s += (s.length === 0 ? "" : ", ") + e;
		return "Entries = [" + s.valueOf() + "], iv = " + this._iv + ", asc = " + this._asc;
	}

	public equals(o : unknown) : boolean {
		if (o as unknown === this as unknown) 
			return true;
		if (((o instanceof JavaObject) && (o.isTranspiledInstanceOf('java.util.Map'))) === false) 
			return false;
		let mapO : JavaMap<unknown, unknown> | null = cast_java_util_Map(o);
		if (mapO.size() !== this.size()) 
			return false;
		for (let e of this.entrySet()) 
			if (JavaObject.equalsTranspiler(e.getValue(), (mapO.get(e.getKey()))) === false) 
				return false;
		return true;
	}

	public hashCode() : number {
		let h : number = 0;
		for (let entry of this.entrySet()) 
			h += JavaObject.getTranspilerHashCode(entry);
		return h;
	}

	public comparator() : Comparator<K> {
		return this._par.bcGetComparator(this._iv);
	}

	public firstKey() : K {
		return this._asc ? this._par.bcGetFirstKeyOrException(this._iv) : this._par.bcGetLastKeyOrException(this._iv);
	}

	public lastKey() : K {
		return this._asc ? this._par.bcGetLastKeyOrException(this._iv) : this._par.bcGetFirstKeyOrException(this._iv);
	}

	public keySet() : JavaSet<K> {
		return new AVLMapSubKeySet<K, V>(this);
	}

	public values() : Collection<V> {
		return new AVLMapSubCollection(this);
	}

	public entrySet() : JavaSet<JavaMapEntry<K, V>> {
		return new AVLMapSubEntrySet(this);
	}

	public size() : number {
		return this._par.bcGetSize(this._iv);
	}

	public isEmpty() : boolean {
		return this._par.bcIsEmpty(this._iv);
	}

	public containsKey(key : unknown) : boolean {
		return this._par.bcContainsKey(key, this._iv);
	}

	public containsValue(value : unknown) : boolean {
		return this._par.bcContainsValue(value, this._iv);
	}

	public get(key : unknown) : V | null {
		return this._par.bcGetValueOfKeyOrNull(key, this._iv);
	}

	public put(key : K, value : V) : V | null {
		return this._par.bcAddEntryReturnOldValueOrNull(key, value, this._iv);
	}

	public remove(key : unknown) : V | null {
		return this._par.bcRemoveKeyReturnOldValueOrNull(key, this._iv);
	}

	public putAll(map : JavaMap<K, V>) : void {
		this._par.bcAddAllEntriesOfMap(map, this._iv);
	}

	public clear() : void {
		let iter : JavaIterator<JavaMapEntry<K | null, V | null> | null> | null = this.bcGetSubEntrySetIterator();
		while (iter.hasNext()) {
			iter.next();
			iter.remove();
		}
	}

	public lowerEntry(key : K) : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcGetLowerEntryOrNull(key, this._iv) : this._par.bcGetHigherEntryOrNull(key, this._iv);
	}

	public lowerKey(key : K) : K | null {
		return this._asc ? this._par.bcGetLowerKeyOrNull(key, this._iv) : this._par.bcGetHigherKeyOrNull(key, this._iv);
	}

	public floorEntry(key : K) : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcGetFloorEntryOrNull(key, this._iv) : this._par.bcGetCeilingEntryOrNull(key, this._iv);
	}

	public floorKey(key : K) : K | null {
		return this._asc ? this._par.bcGetFloorKeyOrNull(key, this._iv) : this._par.bcGetCeilingKeyOrNull(key, this._iv);
	}

	public ceilingEntry(key : K) : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcGetCeilingEntryOrNull(key, this._iv) : this._par.bcGetFloorEntryOrNull(key, this._iv);
	}

	public ceilingKey(key : K) : K | null {
		return this._asc ? this._par.bcGetCeilingKeyOrNull(key, this._iv) : this._par.bcGetFloorKeyOrNull(key, this._iv);
	}

	public higherEntry(key : K) : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcGetHigherEntryOrNull(key, this._iv) : this._par.bcGetLowerEntryOrNull(key, this._iv);
	}

	public higherKey(key : K) : K | null {
		return this._asc ? this._par.bcGetHigherKeyOrNull(key, this._iv) : this._par.bcGetLowerKeyOrNull(key, this._iv);
	}

	public firstEntry() : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcGetFirstEntryOrNull(this._iv) : this._par.bcGetLastEntryOrNull(this._iv);
	}

	public lastEntry() : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcGetLastEntryOrNull(this._iv) : this._par.bcGetFirstEntryOrNull(this._iv);
	}

	public pollFirstEntry() : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcPollFirstEntryOrNull(this._iv) : this._par.bcPollLastEntryOrNull(this._iv);
	}

	public pollLastEntry() : JavaMapEntry<K, V> | null {
		return this._asc ? this._par.bcPollLastEntryOrNull(this._iv) : this._par.bcPollFirstEntryOrNull(this._iv);
	}

	public descendingMap() : NavigableMap<K, V> {
		return new AVLMapSubMap<K, V>(this._par, this._iv, !this._asc);
	}

	public navigableKeySet() : NavigableSet<K> {
		return new AVLMapSubKeySet(this);
	}

	public descendingKeySet() : NavigableSet<K> {
		return new AVLMapSubKeySet(new AVLMapSubMap<K, V>(this._par, this._iv, !this._asc));
	}

	public subMap(fromKey : K, fromInclusive : boolean, toKey : K, toInclusive : boolean) : NavigableMap<K, V>;

	public subMap(fromKey : K, toKey : K) : SortedMap<K, V>;

	/**
	 * Implementation for method overloads of 'subMap'
	 */
	public subMap(__param0 : K, __param1 : K | boolean, __param2? : K, __param3? : boolean) : NavigableMap<K, V> | SortedMap<K, V> {
		if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && typeof __param1 === "boolean") && ((typeof __param2 !== "undefined") && (typeof __param2 !== "undefined")) && ((typeof __param3 !== "undefined") && typeof __param3 === "boolean")) {
			let fromKey : K = __param0 as unknown as K;
			let fromInclusive : boolean = __param1 as boolean;
			let toKey : K = __param2 as unknown as K;
			let toInclusive : boolean = __param3 as boolean;
			return this._createMap(fromKey, fromInclusive, toKey, toInclusive, this._asc);
		} else if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && (typeof __param1 !== "undefined")) && (typeof __param2 === "undefined") && (typeof __param3 === "undefined")) {
			let fromKey : K = __param0 as unknown as K;
			let toKey : K = __param1 as unknown as K;
			return this._createMap(fromKey, true, toKey, false, this._asc);
		} else throw new Error('invalid method overload');
	}

	public headMap(toKey : K, inclusive : boolean) : NavigableMap<K, V>;

	public headMap(toKey : K) : SortedMap<K, V>;

	/**
	 * Implementation for method overloads of 'headMap'
	 */
	public headMap(__param0 : K, __param1? : boolean) : NavigableMap<K, V> | SortedMap<K, V> {
		if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && typeof __param1 === "boolean")) {
			let toKey : K = __param0 as unknown as K;
			let inclusive : boolean = __param1 as boolean;
			return this._createMap(this._iv.from, this._iv.fromInc, toKey, inclusive, this._asc);
		} else if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && (typeof __param1 === "undefined")) {
			let toKey : K = __param0 as unknown as K;
			return this._createMap(this._iv.from, this._iv.fromInc, toKey, false, this._asc);
		} else throw new Error('invalid method overload');
	}

	public tailMap(fromKey : K, inclusive : boolean) : NavigableMap<K, V>;

	public tailMap(fromKey : K) : SortedMap<K, V>;

	/**
	 * Implementation for method overloads of 'tailMap'
	 */
	public tailMap(__param0 : K, __param1? : boolean) : NavigableMap<K, V> | SortedMap<K, V> {
		if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && typeof __param1 === "boolean")) {
			let fromKey : K = __param0 as unknown as K;
			let inclusive : boolean = __param1 as boolean;
			return this._createMap(fromKey, inclusive, this._iv.to, this._iv.toInc, this._asc);
		} else if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && (typeof __param1 === "undefined")) {
			let fromKey : K = __param0 as unknown as K;
			return this._createMap(fromKey, true, this._iv.to, this._iv.toInc, this._asc);
		} else throw new Error('invalid method overload');
	}

	bcAddKey(e : K) : boolean {
		return this._par.bcAddKey(e, this._iv);
	}

	bcAddAllEntries(c : Collection<JavaMapEntry<K, V>>) : boolean {
		return this._par.bcAddAllEntries(c, this._iv);
	}

	bcAddEntryReturnBool(e : JavaMapEntry<K, V>) : boolean {
		return this._par.bcAddEntryReturnBool(e, this._iv);
	}

	bcContainsAllKeys(c : Collection<unknown>) : boolean {
		return this._par.bcContainsAllKeys(c, this._iv);
	}

	bcContainsAllEntries(c : Collection<unknown>) : boolean {
		return this._par.bcContainsAllEntries(c, this._iv);
	}

	bcContainsEntry(o : unknown) : boolean {
		return this._par.bcContainsEntry(o, this._iv);
	}

	bcRemoveKeyReturnBool(o : unknown) : boolean {
		return this._par.bcRemoveKeyReturnBool(o, this._iv);
	}

	bcRemoveEntry(o : unknown) : boolean {
		return this._par.bcRemoveEntry(o, this._iv);
	}

	bcRemoveAllKeys(c : Collection<unknown>) : boolean {
		return this._par.bcRemoveAllKeys(c, this._iv);
	}

	bcRemoveAllEntries(c : Collection<unknown>) : boolean {
		return this._par.bcRemoveAllEntries(c, this._iv);
	}

	bcRetainAllKeys(c : Collection<unknown>) : boolean {
		let mapRetain : AVLMap<K, K> = new AVLMap();
		for (let obj of c) {
			let key : K = obj as unknown as K;
			mapRetain.put(key, key);
		}
		let changed : boolean = false;
		let iterOfKeys : JavaIterator<K | null> | null = this.bcGetSubKeySetIterator();
		while (iterOfKeys.hasNext()) {
			let key : K | null = iterOfKeys.next();
			if (mapRetain.containsKey(key) === false) {
				iterOfKeys.remove();
				changed = true;
			}
		}
		return changed;
	}

	bcRetainAllEntries(c : Collection<unknown>) : boolean {
		let mapSave : AVLMap<K, V> = new AVLMap();
		let setSave : JavaSet<JavaMapEntry<K, V>> = mapSave.entrySet();
		for (let o of c) 
			if (this.bcContainsEntry(o)) 
				setSave.add(cast_java_util_Map_Entry(o));
		let changed : boolean = false;
		let iterOfEntries : JavaIterator<JavaMapEntry<K | null, V | null> | null> | null = this.bcGetSubEntrySetIterator();
		while (iterOfEntries.hasNext()) 
			if (setSave.contains(iterOfEntries.next()) === false) {
				iterOfEntries.remove();
				changed = true;
			}
		return changed;
	}

	firstEntryAsNode() : AVLMapNode<K, V> | null {
		return this._asc ? this._par.bcGetFirstEntryOrNull(this._iv) : this._par.bcGetLastEntryOrNull(this._iv);
	}

	nextEntryOrNull(node : AVLMapNode<K, V>) : AVLMapNode<K, V> | null {
		return this._asc ? this._par.bcGetNextEntryOrNull(node, this._iv) : this._par.bcGetPrevEntryOrNull(node, this._iv);
	}

	bcAddAllKeys(c : Collection<K>) : boolean {
		return this._par.bcAddAllKeys(c, this._iv);
	}

	bcGetLowerKeyOrNull(e : K) : K | null {
		return this._asc ? this._par.bcGetLowerKeyOrNull(e, this._iv) : this._par.bcGetHigherKeyOrNull(e, this._iv);
	}

	bcGetFloorKeyOrNull(e : K) : K | null {
		return this._asc ? this._par.bcGetFloorKeyOrNull(e, this._iv) : this._par.bcGetCeilingKeyOrNull(e, this._iv);
	}

	bcGetCeilingKeyOrNull(e : K) : K | null {
		return this._asc ? this._par.bcGetCeilingKeyOrNull(e, this._iv) : this._par.bcGetFloorKeyOrNull(e, this._iv);
	}

	bcGetHigherKeyOrNull(e : K) : K | null {
		return this._asc ? this._par.bcGetHigherKeyOrNull(e, this._iv) : this._par.bcGetLowerKeyOrNull(e, this._iv);
	}

	bcPollFirstKeyOrNull() : K | null {
		return this._asc ? this._par.bcPollFirstKeyOrNull(this._iv) : this._par.bcPollLastKeyOrNull(this._iv);
	}

	bcPollLastKeyOrNull() : K | null {
		return this._asc ? this._par.bcPollLastKeyOrNull(this._iv) : this._par.bcPollFirstKeyOrNull(this._iv);
	}

	bcGetArrayListOfKeys() : Vector<K | null> {
		let v : Vector<K | null> | null = new Vector();
		let iter : JavaIterator<K | null> | null = this.navigableKeySet().iterator();
		while (iter.hasNext()) 
			v.add(iter.next());
		return v;
	}

	bcGetArrayListOfValues() : Vector<V | null> {
		let v : Vector<V | null> | null = new Vector();
		let iter : JavaIterator<V | null> | null = this.values().iterator();
		while (iter.hasNext()) 
			v.add(iter.next());
		return v;
	}

	bcGetArrayListOfEntries() : Vector<JavaMapEntry<K | null, V | null> | null> {
		let v : Vector<JavaMapEntry<K | null, V | null> | null> | null = new Vector();
		let iter : JavaIterator<JavaMapEntry<K | null, V | null> | null> | null = this.entrySet().iterator();
		while (iter.hasNext()) 
			v.add(iter.next());
		return v;
	}

	bcGetSubKeySetIterator() : JavaIterator<K> {
		return new AVLMapSubKeySetIterator(this);
	}

	bcGetSubKeySetDescending() : NavigableSet<K> {
		return new AVLMapSubKeySet(new AVLMapSubMap(this._par, this._iv, !this._asc));
	}

	bcGetSubKeySetDescendingIterator() : JavaIterator<K> {
		return new AVLMapSubKeySetIterator(new AVLMapSubMap(this._par, this._iv, !this._asc));
	}

	public bcGetSubKeySet(fromElement : K, fromInclusive : boolean, toElement : K, toInclusive : boolean) : NavigableSet<K>;

	public bcGetSubKeySet(fromElement : K, toElement : K) : SortedSet<K>;

	/**
	 * Implementation for method overloads of 'bcGetSubKeySet'
	 */
	public bcGetSubKeySet(__param0 : K, __param1 : K | boolean, __param2? : K, __param3? : boolean) : NavigableSet<K> | SortedSet<K> {
		if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && typeof __param1 === "boolean") && ((typeof __param2 !== "undefined") && (typeof __param2 !== "undefined")) && ((typeof __param3 !== "undefined") && typeof __param3 === "boolean")) {
			let fromElement : K = __param0 as unknown as K;
			let fromInclusive : boolean = __param1 as boolean;
			let toElement : K = __param2 as unknown as K;
			let toInclusive : boolean = __param3 as boolean;
			return this._createSet(fromElement, fromInclusive, toElement, toInclusive, this._asc);
		} else if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && (typeof __param1 !== "undefined")) && (typeof __param2 === "undefined") && (typeof __param3 === "undefined")) {
			let fromElement : K = __param0 as unknown as K;
			let toElement : K = __param1 as unknown as K;
			return this._createSet(fromElement, true, toElement, false, this._asc);
		} else throw new Error('invalid method overload');
	}

	public bcGetSubKeyHeadSet(toElement : K, inclusive : boolean) : NavigableSet<K>;

	public bcGetSubKeyHeadSet(toElement : K) : SortedSet<K>;

	/**
	 * Implementation for method overloads of 'bcGetSubKeyHeadSet'
	 */
	public bcGetSubKeyHeadSet(__param0 : K, __param1? : boolean) : NavigableSet<K> | SortedSet<K> {
		if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && typeof __param1 === "boolean")) {
			let toElement : K = __param0 as unknown as K;
			let inclusive : boolean = __param1 as boolean;
			return this._createSet(this._iv.from, this._iv.fromInc, toElement, inclusive, this._asc);
		} else if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && (typeof __param1 === "undefined")) {
			let toElement : K = __param0 as unknown as K;
			return this._createSet(this._iv.from, this._iv.fromInc, toElement, false, this._asc);
		} else throw new Error('invalid method overload');
	}

	public bcGetSubKeyTailSet(fromElement : K, inclusive : boolean) : NavigableSet<K>;

	public bcGetSubKeyTailSet(fromElement : K) : SortedSet<K>;

	/**
	 * Implementation for method overloads of 'bcGetSubKeyTailSet'
	 */
	public bcGetSubKeyTailSet(__param0 : K, __param1? : boolean) : NavigableSet<K> | SortedSet<K> {
		if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && ((typeof __param1 !== "undefined") && typeof __param1 === "boolean")) {
			let fromElement : K = __param0 as unknown as K;
			let inclusive : boolean = __param1 as boolean;
			return this._createSet(fromElement, inclusive, this._iv.to, this._iv.toInc, this._asc);
		} else if (((typeof __param0 !== "undefined") && (typeof __param0 !== "undefined")) && (typeof __param1 === "undefined")) {
			let fromElement : K = __param0 as unknown as K;
			return this._createSet(fromElement, true, this._iv.to, this._iv.toInc, this._asc);
		} else throw new Error('invalid method overload');
	}

	bcGetSubCollectionIterator() : JavaIterator<V> {
		return new AVLMapSubCollectionIterator(this);
	}

	bcContainsAllValues(c : Collection<unknown>) : boolean {
		return this._par.bcContainsAllValues(c, this._iv);
	}

	bcGetSubEntrySetIterator() : JavaIterator<JavaMapEntry<K, V>> {
		return new AVLMapSubEntrySetIterator(this);
	}

	private _createMap(from : K, fromInc : boolean, to : K, toInc : boolean, asc : boolean) : AVLMapSubMap<K, V> {
		if (this._par.bcCheckOutOfIntervall(from, fromInc, this._iv)) 
			throw new IllegalArgumentException("FROM-KEY " + from + "/" + fromInc + " nicht in " + this._iv)
		if (this._par.bcCheckOutOfIntervall(to, toInc, this._iv)) 
			throw new IllegalArgumentException("TO-KEY " + to + "/" + toInc + " nicht in " + this._iv)
		return new AVLMapSubMap<K, V>(this._par, new AVLMapIntervall<K>(from, fromInc, to, toInc), asc);
	}

	private _createSet(from : K, fromInc : boolean, to : K, toInc : boolean, asc : boolean) : AVLMapSubKeySet<K, V> {
		return new AVLMapSubKeySet<K, V>(this._createMap(from, fromInc, to, toInc, asc));
	}

	isTranspiledInstanceOf(name : string): boolean {
		return ['java.util.Map', 'java.util.NavigableMap', 'de.nrw.schule.svws.core.adt.map.AVLMapSubMap', 'java.util.SortedMap'].includes(name);
	}

}

export function cast_de_nrw_schule_svws_core_adt_map_AVLMapSubMap<K, V>(obj : unknown) : AVLMapSubMap<K, V> {
	return obj as AVLMapSubMap<K, V>;
}
