package de.lcag.common;

import java.util.HashMap;
import java.util.TreeMap;

import org.apache.commons.collections4.map.ListOrderedMap;

public class PositionOrderedMap<K, V> extends ListOrderedMap<K, V> {
	private static final long serialVersionUID = 1L;
	private int currentPosition = 0;
	private HashMap<V, Integer> value2Position = new HashMap<>();
	private TreeMap<Integer, K> position2Key = new TreeMap<>(new HashMap<>());

	@Override
	public V put(K pKey, V pValue) {
		if (value2Position.values().contains(currentPosition)) {
			throw new IllegalArgumentException();
		}

		value2Position.put(pValue, currentPosition);
		position2Key.put(currentPosition, pKey);
		currentPosition += 1;

		return super.put(pKey, pValue);
	}

	@Override
	public V put(int pPosition, K pKey, V pValue) {
		if (value2Position.values().contains(pPosition)) {
			throw new IllegalArgumentException();
		}

		value2Position.put(pValue, pPosition);
		position2Key.put(pPosition, pKey);
		currentPosition += 1;

		return super.put(pPosition, pKey, pValue);
	}

	public Integer getPositionOfValue(V pValue) {
		return this.value2Position.get(pValue);
	}

	public Integer getPositionOfKey(K pKey) {
		V value = this.get(pKey);
		return this.value2Position.get(value);
	}

	public int getPositionOfLastElement() {
		K key = this.lastKey();
		V value = this.get(key);

		return value2Position.get(value);
	}

	public K getKeyByPosition(int pPosition) {
		return this.position2Key.get(pPosition);
	}

	public Integer shiftPositionByKey(K pKey) {
		V v = this.get(pKey);

		return shiftPositionByValue(v);
	}

	public Integer shiftPositionByValue(V v) {
		Integer pos = this.value2Position.get(v) + 1;

		return shiftPosition(pos);
	}

	public Integer shiftPosition(int pPosition) {
		K key = this.position2Key.get(pPosition);

		if (key == null) {
			throw new IllegalArgumentException("Position to shift not in use");
		}

		V value = this.get(key);
		int resultNewPosition = pPosition + 1;
		K keyAtNewPos = this.position2Key.get(resultNewPosition);

		if (keyAtNewPos != null) {
			throw new IllegalArgumentException("Target position already in use");
		}

		this.value2Position.put(value, resultNewPosition);
		this.position2Key.remove(pPosition);
		this.position2Key.put(resultNewPosition, key);

		return resultNewPosition;
	}

	public int shiftAllPositions(int pStartPosition) {
		int lastPosition = this.getPositionOfLastElement();
		int resultNoShiftedElements = 0;

		System.out.println(this);
		for (int i = lastPosition; i >= pStartPosition; i--) {
			if (this.position2Key.get(i) == null)
				continue;

			this.shiftPosition(i);
			resultNoShiftedElements += 1;
		}

		return resultNoShiftedElements;
	}

	@Override
	public V remove(int pIndex) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public V remove(Object pObject) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public boolean remove(Object pKey, Object pValue) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public V replace(Object pKey, Object pValue) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public boolean replace(Object pKey, Object pOldValue, Object pNewValue) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();

		this.forEach((K key, V value) -> {
			Integer v2p = this.value2Position.get(value);
			K p2k = this.position2Key.get(v2p);

			if (v2p == null)
				v2p = -1;

			sb.append(String.format("Index %d: Key %10s: value=%10s, v2p=%d, p2k=%s\n", this.indexOf(key),
					key.toString(), value.toString(), v2p, p2k == null ? "(null)" : p2k.toString()));
		});

		String result = sb.toString();
		return result;
	}
}
