//@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 SortedIntDoubleArrayMap
{
	private int[] _keys;
	private double[] _values;
	public final double NULL;
	
	public SortedIntDoubleArrayMap()
	{
		this(DOUBLE_NULL);
	}
	
	public SortedIntDoubleArrayMap(double nullValue)
	{
		this(nullValue, INT_EMPTY, DOUBLE_EMPTY);
	}
	
	public SortedIntDoubleArrayMap(int[] keys, double[] values)
	{
		this(DOUBLE_NULL, keys, values);
	}
	
	public SortedIntDoubleArrayMap(double nullValue, int[] keys, double[] values)
	{
		NULL = nullValue;
		_keys = keys;
		_values = values;
		sort();
	}
	
	public SortedIntDoubleArrayMap(Map<Integer, Double> t)
	{
		this(DOUBLE_NULL, t);
	}
	
	public SortedIntDoubleArrayMap(double nullValue, Map<Integer, Double> t)
	{
		NULL = nullValue;
		int length = t.size();
		int[] tmpKeys = new int[length];
		double[] tmpValues = new double[length];
		
		Double w;
		int index = 0;
		for (Entry<Integer, Double> 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 = INT_EMPTY;
		_values = DOUBLE_EMPTY;
	}
	
	public boolean containsKey(int key)
	{
		return Arrays.binarySearch(_keys, key) >= 0;
	}
	
	public boolean containsValue(double value)
	{
		for (int i = 0, length = _values.length; i < length; ++i)
			if (_values[i] == value)
				return true;
		return false;
	}
	
	public double get(int key)
	{
		return get(key, NULL);
	}
	
	public double get(int key, double defaultValue)
	{
		int i = Arrays.binarySearch(_keys, key);
		if (i >= 0)
			return _values[i];
		else
			return defaultValue;
	}
	
	public boolean isEmpty()
	{
		return _keys.length == 0;
	}
	
	public int[] keySet()
	{
		return _keys;
	}
	
	public int[] keys()
	{
		return _keys;
	}
	
	public double put(int key, double value)
	{
		int index = Arrays.binarySearch(_keys, key);
		if (index >= 0) {
			double prev = _values[index];
			_values[index] = value;
			return prev;
		} else {
			return insertEntry(-(index + 1), key, value);
		}
	}
	
	public double putIfAbsent(int key, double value)
	{
		int index = Arrays.binarySearch(_keys, key);
		if (index >= 0)
			return _values[index];
		else
			return insertEntry(-(index + 1), key, value);
	}
	
	public void putAll(Map<Integer, Double> t)
	{
		for (Entry<Integer, Double> e : t.entrySet())
			put(e.getKey(), e.getValue());
	}
	
	public double remove(int key)
	{
		int index = Arrays.binarySearch(_keys, key);
		if (index >= 0)
			return deleteEntry(index);
		else
			return NULL;
	}
	
	public int size()
	{
		return _keys.length;
	}
	
	public double[] values()
	{
		return _values;
	}
	
	public SortedIntDoubleArrayMap append(int key, double value)
	{
		double prev = put(key, value);
		if (prev != NULL) throw new RuntimeException("Duplicate key:" + key + " value:" + value + " previus:" + prev);
		return this;
	}
	
	private double deleteEntry(int index)
	{
		final int length = _keys.length - 1;
		if (length < 0)
			return NULL;
		int[] tmpKeys = new int[length];
		double[] tmpValues = new double[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);
		}
		double prev = _values[index];
		_keys = tmpKeys;
		_values = tmpValues;
		return prev;
	}
	
	public int indexOf(int key)
	{
		return Arrays.binarySearch(_keys, key);
	}
	
	private double insertEntry(int index, int key, double value)
	{
		final int length = _keys.length;
		int[] tmpKeys = new int[length + 1];
		double[] tmpValues = new double[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 int keyAt(int index)
	{
		return _keys[index];
	}
	
	public double valueAt(int index)
	{
		return _values[index];
	}
	
	public int getKey(int index)
	{
		return keyAt(index);
	}
	
	public double getValue(int index)
	{
		return valueAt(index);
	}
	
	public double setValue(int index, double value)
	{
		double prev = _values[index];
		_values[index] = value;
		return prev;
	}
	
	public void fill(double value)
	{
		Arrays.fill(_values, value);
	}
	
	public void sort()
	{
		sort(0, _keys.length - 1);
	}
	
	private void sort(int left, int right)
	{
		if (left >= right) return;
		int 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;
			int key = _keys[i]; _keys[i] = _keys[j]; _keys[j] = key;
			double value = _values[i]; _values[i] = _values[j]; _values[j] = value;
			++i;
			--j;
		}
		sort(left,  i - 1);
		sort(j + 1, right);
	}
}
