//@formatter:off
/*
 * Auther: tukune
 * 
 * DON'T CODE FORMAT!
 * DON'T CONVERT TAB <---> SPACE
 * DON'T STRIP TRAILING BLANKS FROM END OF LINE
 */
package jp.sf.l2j.arrayMaps;

import static jp.sf.l2j.arrayMaps.SortedArrayMap.*;

import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;

/*
 * @auther JOJO
 */
public class SortedLongByteArrayMap
{
	private long[] _keys;
	private byte[] _values;
	public final byte NULL;
	
	public SortedLongByteArrayMap()
	{
		this(BYTE_NULL);
	}
	
	public SortedLongByteArrayMap(byte nullValue)
	{
		this(nullValue, LONG_EMPTY, BYTE_EMPTY);
	}
	
	public SortedLongByteArrayMap(long[] keys, byte[] values)
	{
		this(BYTE_NULL, keys, values);
	}
	
	public SortedLongByteArrayMap(byte nullValue, long[] keys, byte[] values)
	{
		NULL = nullValue;
		_keys = keys;
		_values = values;
		sort();
	}
	
	public SortedLongByteArrayMap(Map<Long, Byte> t)
	{
		this(BYTE_NULL, t);
	}
	
	public SortedLongByteArrayMap(byte nullValue, Map<Long, Byte> t)
	{
		NULL = nullValue;
		int length = t.size();
		long[] tmpKeys = new long[length];
		byte[] tmpValues = new byte[length];
		
		Byte w;
		int index = 0;
		for (Entry<Long, Byte> e : t.entrySet())
		{
			tmpKeys[index] = e.getKey();
			tmpValues[index] = (w = e.getValue()) == null ? NULL : w;
			++index;
		}
		_keys = tmpKeys;
		_values = tmpValues;
		sort();
	}
	
	public void clear()
	{
		_keys = LONG_EMPTY;
		_values = BYTE_EMPTY;
	}
	
	public boolean containsKey(long key)
	{
		return Arrays.binarySearch(_keys, key) >= 0;
	}
	
	public boolean containsValue(byte value)
	{
		for (int i = 0, length = _values.length; i < length; ++i)
			if (_values[i] == value)
				return true;
		return false;
	}
	
	public byte get(long key)
	{
		return get(key, NULL);
	}
	
	public byte get(long key, byte defaultValue)
	{
		int i = Arrays.binarySearch(_keys, key);
		if (i >= 0)
			return _values[i];
		else
			return defaultValue;
	}
	
	public boolean isEmpty()
	{
		return _keys.length == 0;
	}
	
	public long[] keySet()
	{
		return _keys;
	}
	
	public long[] keys()
	{
		return _keys;
	}
	
	public byte put(long key, byte value)
	{
		int index = Arrays.binarySearch(_keys, key);
		if (index >= 0) {
			byte prev = _values[index];
			_values[index] = value;
			return prev;
		} else {
			return insertEntry(-(index + 1), key, value);
		}
	}
	
	public byte putIfAbsent(long key, byte value)
	{
		int index = Arrays.binarySearch(_keys, key);
		if (index >= 0)
			return _values[index];
		else
			return insertEntry(-(index + 1), key, value);
	}
	
	public void putAll(Map<Long, Byte> t)
	{
		for (Entry<Long, Byte> e : t.entrySet())
			put(e.getKey(), e.getValue());
	}
	
	public byte remove(long key)
	{
		int index = Arrays.binarySearch(_keys, key);
		if (index >= 0)
			return deleteEntry(index);
		else
			return NULL;
	}
	
	public int size()
	{
		return _keys.length;
	}
	
	public byte[] values()
	{
		return _values;
	}
	
	public SortedLongByteArrayMap append(long key, byte value)
	{
		byte prev = put(key, value);
		if (prev != NULL) throw new RuntimeException("Duplicate key:" + key + " value:" + value + " previus:" + prev);
		return this;
	}
	
	private byte deleteEntry(int index)
	{
		final int length = _keys.length - 1;
		if (length < 0)
			return NULL;
		long[] tmpKeys = new long[length];
		byte[] tmpValues = new byte[length];
		if (index > 0) {
			System.arraycopy(_keys, 0, tmpKeys, 0, index);
			System.arraycopy(_values, 0, tmpValues, 0, index);
		}
		int n;
		if ((n = length - index) > 0) {
			System.arraycopy(_keys, index + 1, tmpKeys, index, n);
			System.arraycopy(_values, index + 1, tmpValues, index, n);
		}
		byte prev = _values[index];
		_keys = tmpKeys;
		_values = tmpValues;
		return prev;
	}
	
	public int indexOf(long key)
	{
		return Arrays.binarySearch(_keys, key);
	}
	
	private byte insertEntry(int index, long key, byte value)
	{
		final int length = _keys.length;
		long[] tmpKeys = new long[length + 1];
		byte[] tmpValues = new byte[length + 1];
		if (index > 0) {
			System.arraycopy(_keys, 0, tmpKeys, 0, index);
			System.arraycopy(_values, 0, tmpValues, 0, index);
		}
		tmpKeys[index] = key;
		tmpValues[index] = value;
		int n;
		if ((n = length - index) > 0) {
			System.arraycopy(_keys, index, tmpKeys, index + 1, n);
			System.arraycopy(_values, index, tmpValues, index + 1, n);
		}
		_keys = tmpKeys;
		_values = tmpValues;
		return NULL;
	}
	
	public long keyAt(int index)
	{
		return _keys[index];
	}
	
	public byte valueAt(int index)
	{
		return _values[index];
	}
	
	public long getKey(int index)
	{
		return keyAt(index);
	}
	
	public byte getValue(int index)
	{
		return valueAt(index);
	}
	
	public byte setValue(int index, byte value)
	{
		byte prev = _values[index];
		_values[index] = value;
		return prev;
	}
	
	public void fill(byte value)
	{
		Arrays.fill(_values, value);
	}
	
	public void sort()
	{
		sort(0, _keys.length - 1);
	}
	
	private void sort(int left, int right)
	{
		if (left >= right) return;
		long pivot = _keys[(left + right) / 2];
		int i = left;
		int j = right;
		for (;;) {
			while (_keys[i] < pivot) ++i;
			while (pivot < _keys[j]) --j;
			if (i >= j) break;
			long key = _keys[i]; _keys[i] = _keys[j]; _keys[j] = key;
			byte value = _values[i]; _values[i] = _values[j]; _values[j] = value;
			++i;
			--j;
		}
		sort(left,  i - 1);
		sort(j + 1, right);
	}
}
