/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.core.btries; import gplx.*; import gplx.core.*;
public class Btrie_slim_itm {
	private Btrie_slim_itm[] ary = Btrie_slim_itm.Ary_empty;
	public Btrie_slim_itm(byte key_byte, Object val, boolean case_any) {this.key_byte = key_byte; this.val = val; this.case_any = case_any;}
	public byte Key_byte() {return key_byte;} private byte key_byte;
	public Object Val() {return val;} public void Val_set(Object val) {this.val = val;} private Object val;
	public boolean Case_any() {return case_any;} private boolean case_any;
	public boolean Ary_is_empty() {return ary == Btrie_slim_itm.Ary_empty;}
	public void Clear() {
		val = null;
		for (int i = 0; i < ary_len; i++)
			ary[i].Clear();
		ary = Btrie_slim_itm.Ary_empty;
		ary_len = ary_max = 0;
	}
	public Btrie_slim_itm Ary_find(byte b) {
		int find_val = (case_any && (b > 64 && b < 91) ? b + 32 : b) & 0xff;// PATCH.JAVA:need to convert to unsigned byte
		int key_val = 0;
		switch (ary_len) {
			case 0: return null;
			case 1:
				Btrie_slim_itm rv = ary[0];
				key_val = rv.Key_byte() & 0xff;// PATCH.JAVA:need to convert to unsigned byte;
				key_val = (case_any && (key_val > 64 && key_val < 91) ? key_val + 32 : key_val);
				return key_val == find_val ? rv : null;
			default:
				int adj = 1;
				int prv_pos = 0;
				int prv_len = ary_len;
				int cur_len = 0;
				int cur_idx = 0;
				Btrie_slim_itm itm = null;
				while (true) {
					cur_len = prv_len / 2;
					if (prv_len % 2 == 1) ++cur_len;
					cur_idx = prv_pos + (cur_len * adj);
					if		(cur_idx < 0)			cur_idx = 0;
					else if (cur_idx >= ary_len)	cur_idx = ary_len - 1;
					itm = ary[cur_idx];
					key_val = itm.Key_byte() & 0xff;	// PATCH.JAVA:need to convert to unsigned byte;
					key_val = (case_any && (key_val > 64 && key_val < 91) ? key_val + 32 : key_val);
					if		(find_val <	 key_val)	adj = -1;
					else if (find_val >	 key_val)	adj =  1;
					else  /*(find_val == cur_val)*/ return itm;
					if (cur_len == 1) {
						cur_idx += adj;
						if (cur_idx < 0 || cur_idx >= ary_len) return null;
						itm = ary[cur_idx];
						return (itm.Key_byte() & 0xff) == find_val ? itm : null;	// PATCH.JAVA:need to convert to unsigned byte;
					}
					prv_len = cur_len;
					prv_pos = cur_idx;
				}
		}
	}
	public Btrie_slim_itm Ary_add(byte b, Object val) {
		int new_len = ary_len + 1;
		if (new_len > ary_max) {
			ary_max += 4;
			ary = (Btrie_slim_itm[])Array_.Resize(ary, ary_max);
		}
		Btrie_slim_itm rv = new Btrie_slim_itm(b, val, case_any);
		ary[ary_len] = rv;
		ary_len = new_len;
		ByteHashItm_sorter._.Sort(ary, ary_len);
		return rv;
	}
	public void Ary_del(byte b) {
		boolean found = false;
		for (int i = 0; i < ary_len; i++) {
			if (found) {
				if (i < ary_len - 1)
					ary[i] = ary[i + 1];
			}
			else {
				if (b == ary[i].Key_byte()) found = true;
			}
		}
		if (found) --ary_len;
	}
	public static final Btrie_slim_itm[] Ary_empty = new Btrie_slim_itm[0]; int ary_len = 0, ary_max = 0;
}
class ByteHashItm_sorter {// quicksort
	Btrie_slim_itm[] ary; int ary_len;
	public void Sort(Btrie_slim_itm[] ary, int ary_len) {
		if (ary == null || ary_len < 2) return;
		this.ary = ary;
		this.ary_len = ary_len;
		Sort_recurse(0, ary_len - 1);
	}
	private void Sort_recurse(int lo, int hi) {
		int i = lo, j = hi;			
		int mid = ary[lo + (hi-lo)/2].Key_byte()& 0xFF;				// get mid itm
		while (i <= j) {											// divide into two lists
			while ((ary[i].Key_byte() & 0xFF) < mid)				// if lhs.cur < mid, then get next from lhs
				i++;				
			while ((ary[j].Key_byte() & 0xFF) > mid)				// if rhs.cur > mid, then get next from rhs
				j--;

			// lhs.cur > mid && rhs.cur < mid; switch lhs.cur and rhs.cur; increase i and j
			if (i <= j) {
				Btrie_slim_itm tmp = ary[i];
				ary[i] = ary[j];
				ary[j] = tmp;
				i++;
				j--;
			}
		}
		if (lo < j) Sort_recurse(lo, j);
		if (i < hi) Sort_recurse(i, hi);
	}
	public static final ByteHashItm_sorter _ = new ByteHashItm_sorter(); ByteHashItm_sorter() {}
}
