/*
 * Created on May 16, 2005
 */
package kr.util;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeMap;




public class Util {

	public static float percentDiff(float a, float b) {
		return a/(a+b);
	}

	public static int findNonAlpha(String s, int start) {
		for(;start < s.length(); start++)
		{
			int ch = s.charAt(start);
			if(ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
				continue;
			break;
		}
		
		return start;
	}

	public static <T extends Object> T getFirstOrNull(List<T> list) {
		if(list.isEmpty())
			return null;
		
		return list.get(0);
	}

	public static <T extends Object> T getFirstOrNull(Collection<T> c) {
		if(c.isEmpty())
			return null;
		
		return c.iterator().next();
	}

	public static double square(double d) {
		return d*d;
	}

	public static double percentDiff(double a, double b) {
		return a/(a+b);
	}

	public static <B, A> SortedMap<B, A> toReversedSortedMap(Map<A, B> im) 
	{
		SortedMap<B,A> m = new TreeMap<B, A>();
		
		for(A key : im.keySet())
		{
			m.put(im.get(key), key);
		}
		
		return m;
	}

	public static <B, A> Map<B,A> reverseMap(Map<A, B> im) {
		Map<B,A> m = new HashMap<B, A>();
		
		for(A key : im.keySet())
		{
			m.put(im.get(key), key);
		}
		
		return m;
	}

	public static <A extends Comparable> List<A> getSortedList(Collection<A> name) {
		// TODO Auto-generated method stub
		ArrayList<A> l = new ArrayList<A>();
		l.addAll(name);
		
		Collections.sort(l);
		return l;
	}

	public static <A> List<A> reverse(List<A> sortedList) {
		ArrayList<A> l = new ArrayList<A>(sortedList.size());
		for(int i = sortedList.size()-1; i >= 0; i--)
		{
			l.add(sortedList.get(i));
		}
		return l;
	}

	public static String arrayToString(Object[] a) {
		return arrayToString("[ ",a,", "," ]");
	}

	public static String arrayToString(String header, Object[] a, String sep, String footer) {
		StringBuffer s = new StringBuffer(header);
		for(int i = 0; i < a.length; i++)
		{
			s.append(a[i]);
			if(i != a.length -1)
				s.append(sep);
		}
		s.append(footer);
		return s.toString();
	}

	public static String listToString(String header, List a, String sep, String footer) {
		return arrayToString(header, a.toArray(), sep, footer);
	}

	public static String trimChars(String sql, String charsToTrim, String replaceWith) 
	{
		StringBuffer out = new StringBuffer();
		
		for(int i = 0; i < sql.length();i ++)
		{
			char c = sql.charAt(i);
			
			if(charsToTrim.indexOf(c) == -1)
			{
				out.append(c);
			}
			else
				out.append(replaceWith);
		}
		
		return out.toString();
	}
	
	public static int removeHighestBit(int map)
	{
		//PERF is there a better way to do this???
		int val = 1;
		while(true)
		{
			if(val > map)
			{
				map -= (val>>1);
				return map;
			}
			
			val = val << 1;
		}
	}

	/**
	 * Iterates through all bit combination for a given map of bits. When
	 * finished, returns -1
	 */
	public static int iterateThroughBitCombinations(int map, int currVal)
	{
		int i = 1;

		while(true)
		{
			//find next lowest bit in map
			for(; i < map; i = (i<<1))
			{
				if((map & i) != 0)
					break;
			}
			
			if(i > map)
			{
				//there are no more bits to iterate through
				return -1;
			}
			
			//if the current value has that bit on
			if((currVal & i) != 0)
			{
				//set it to zero and carry on to the next bit
				currVal = currVal - i;
				i = (i << 1);
			}
			else
			{
				return currVal | i;
			}
		}		
	}
	
	public static String toString(Object[] array) {
		StringBuffer sb = new StringBuffer();
		for(int i = 0; i < array.length; i++)
		{
			sb.append(array[i]);
			if(i < array.length - 1)
				sb.append(", ");
		}

		return sb.toString();
	}

	public static int countBits(long i) {
		int count = 0;
		while(i != 0)
		{
			count += (i & 1);
			i = i >> 1;
		}
		
		return count;
	}
	
	public static int [] getRandomNonRepeatingInts(int total, int max) {
		
		//if there are so many that choosing the same random number twice is large
		if(total > max / 2)
		{	
			//create an array of all of them and shuffle it
			int [] out = new int[max];
			
			for(int i = 0; i < max; i++)
			{
				out[i] = i;
			}
			
			//we're not returning the ones after total, so don't calculate them
			for(int i = 0; i < total; i++)
			{
				int v = (int)(Math.random() * (max-i))+i;
				int t;
				t = out[v];
				out[v] = out[i];
				out[i] = t;
			}
			
			//return only the first ones that are the same as the number specified to return
			int [] out2 = new int[total];
			
			System.arraycopy(out, 0, out2, 0, out2.length);
			
			return out2;
		}
		
		//if the number is small, keep guessing random numbers
		//until they don't overlap
		int [] out = new int[total];
		
		for(int i = 0; i < total; i++)
		{
			out[i] = (int)(Math.random() * max);
			
			//check if it overlaps
			for(int j = 0; j < i; j++)
			{
				if(out[j] == out[i])
				{
					i--;
					break;
				}
			}
		}
		
		return out;
	}

	public static <T> void swap(List<T> l, int i1, int i2) {
		T t = l.get(i1);
		l.set(i1,l.get(i2));
		l.set(i2, t);
	}

	/**
	 * Returns the string split into tokens given the seperator 
	 */
	public static String[] split(String string, String sep) {
		StringTokenizer st = new StringTokenizer(string, sep);
		List<String> l = new ArrayList<String>();

		while( st.hasMoreTokens())
		{
			l.add(st.nextToken());
		}
		
		return l.toArray(new String[0]);
	}

	public static String[] addArrays(String[] a, String[] b) {
		
		String [] newArray = new String[a.length + b.length];
		
		System.arraycopy(a, 0, newArray, 0, a.length);
		System.arraycopy(b, 0, newArray, a.length, b.length);
		
		return newArray;
	}

	public static Object join(String sep, Object ... args) 
	{
		StringBuffer sb = new StringBuffer();
		
		for(int i = 0; i < args.length; i++)
		{
			sb.append(args[i]);
			if(i != args.length -1) sb.append(sep);
		}
		
		return sb.toString();
	}

	public static String toHex(byte[] message) {
		return toHex(message, 0, message.length);
	}

	public static String toHex(byte[] dat, int start, int end) {
		StringBuffer sb = new StringBuffer();
		for(int i = start; i < end; i++)
		{
			sb.append(String.format("0x%02x ",dat[i]));
		}
		
		return sb.deleteCharAt(sb.length()-1).toString();
	}

	public static boolean containsToken(byte[] message, int start, byte token) {
		for(int i = start; i < message.length; i++)
		{
			if(message[i] == token)
				return true;
		}
		return false;
	}

	public static String removeChars(String text, char... c) {
		StringBuffer out = new StringBuffer();
		
		for(int i = 0; i < text.length(); i++)
		{
			char x = text.charAt(i);
			boolean removeFlag = false;
			for(char y : c)
			{
				if(x == y)
				{
					removeFlag = true;
					break;
				}
			}
			
			if(!removeFlag)
				out.append(x);
		}
		
		return out.toString();
	}
	
	public static String removeCharsNotIn(String text, String chars) {
		StringBuffer out = new StringBuffer();
		
		for(int i = 0; i < text.length(); i++)
		{
			char x = text.charAt(i);
			if(chars.indexOf(x) != -1)
				out.append(x);
		}
		
		return out.toString();
	}


	/**
	 * Returns the index of the data point that is near the value. If
	 * beforeIfNotPresent is set, and no data point exists at that value
	 * then the data point immediately prior will be selected (-1 if before
	 * the beginning of the list). Otherwise the opposite is true
	 */
	public static <T> T get(SortedSet<T> data, T comparator, boolean beforeIfNotPresent)
	{
		if(beforeIfNotPresent && !data.contains(comparator))
		{
			SortedSet<T> s = data.headSet(comparator);
			if(s.isEmpty())
				return null;
			return s.last();
		}

		SortedSet<T> s = data.tailSet(comparator);
		if(s.isEmpty())
			return null;
		return s.first();
	}

	/**
	 * Returns the index of the item at the given value. If
	 * beforeIfNotPresent is set, and no item exists at that value
	 * then the item immediately prior will be selected (-1 if before
	 * the beginning of the list). Otherwise the opposite is true.
	 * However, if there are no elements in the list, -1 is returned regardless.
	 */
	public static <T extends Comparable> int getIndexFromSortedList(List<T> l, T comparator, boolean beforeIfNotPresent)
	{
		if(l.size() == 0)
			return -1;
		int r = Collections.binarySearch((List)l, (Object)comparator);
		if(r >= 0) return r;
		
		return beforeIfNotPresent ? -r-2 : -r-1;
	}

	public static void main(String []argv)
	{
		/*int map = 1 + 2 + 8;
		
		for(int i = 0; i != -1; i = Util.iterateThroughBitCombinations(map, i))
		{
			System.out.println("i is "+i);
		}
		
		int i = 64;
		
		System.out.println("Util.countBits("+i+") is "+Util.countBits(i));
		
		int [] random1 = Util.getRandomNonRepeatingInts(19999,20000);
		int [] random2 = Util.getRandomNonRepeatingInts(9,20);
		
		System.out.println("random1: ");
		
		for(int j : random1)
		{
			System.out.print(j+" ");
		}

		System.out.println("\nrandom2: ");
		
		for(int j : random2)
		{
			System.out.print(j+" ");
		}*/
		
		byte [] dat = parseHex("0x34 0xFF 0xF8");
		System.out.println(toHex(dat));

	}

	public static byte[] parseHex(String data) {
		int i = 0;
		data = data.toLowerCase();
		
		ByteBuffer bb = new ByteBuffer();
		
		while(i +2< data.length())
		{
			if(data.substring(i, i+2).equals("0x"))
			{
				byte b = 0;
				for(int j = i+2; j <= i+3; j++)
				{
					char c = data.charAt(j);
					if(c >= 'a' && c <='f')
						b |= c - 'a' + 10;
					else if(c >= '0' && c <='9')
						b |= c - '0';
					else return null;
					if(j == i+2)
						b = (byte)(b << 4);
				}
				
				bb.append(b);
				i+=4;
			}
			else if(data.charAt(i) == ' ')
				i++;
				
		}
		return bb.toByteArray();
	}

}
