/*
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 * 
 * This file is part of MjSip (http://www.mjsip.org)
 * 
 * MjSip is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * MjSip 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MjSip; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package org.zoolu.tools;

/**
 * Mangle collects some static methods for mangling binary-data structures
 */
public class Mangle {

	/** Compares two arrays of bytes */
	public static boolean compare(byte[] a, byte[] b) {
		if (a.length != b.length)
			return false;
		for (int i = 0; i < a.length; i++)
			if (a[i] != b[i])
				return false;
		return true;
	}

	/** Initalizes a byte array with value <i>value</i> */
	public static byte[] initBytes(byte[] b, int value) {
		for (int i = 0; i < b.length; i++)
			b[i] = (byte) value;
		return b;
	}

	/** Gets the unsigned representatin of a byte (into a short) */
	public static short uByte(byte b) {
		return (short) (((short) b + 256) % 256);
	}

	/** Gets the unsigned representatin of a 32-bit word (into a long) */
	public static long uWord(int n) {
		long wmask = 0x10000;
		wmask *= wmask;
		return (long) (((long) n + wmask) % wmask);
	}

	/** Rotates w left n bits. */
	private static int rotateLeft(int w, int n) {
		return (w << n) | (w >>> (32 - n));
	}

	/** Rotates w right n bits. */
	private static int rotateRight(int w, int n) {
		return (w >>> n) | (w << (32 - n));
	}

	/** Rotates an array of int (words), shifting 1 word left. */
	private static int[] rotateLeft(int[] w) {
		int len = w.length;
		int w1 = w[len - 1];
		for (int i = len - 1; i > 1; i--)
			w[i] = w[i - 1];
		w[0] = w1;
		return w;
	}

	/** Rotates an array of int (words), shifting 1 word right. */
	private static int[] rotateRight(int[] w) {
		int len = w.length;
		int w0 = w[0];
		for (int i = 1; i < len; i++)
			w[i - 1] = w[i];
		w[len - 1] = w0;
		return w;
	}

	/** Rotates an array of bytes, shifting 1 byte left. */
	private static byte[] rotateLeft(byte[] b) {
		int len = b.length;
		byte b1 = b[len - 1];
		for (int i = len - 1; i > 1; i--)
			b[i] = b[i - 1];
		b[0] = b1;
		return b;
	}

	/** Rotates an array of bytes, shifting 1 byte right. */
	private static byte[] rotateRight(byte[] b) {
		int len = b.length;
		byte b0 = b[0];
		for (int i = 1; i < len; i++)
			b[i - 1] = b[i];
		b[len - 1] = b0;
		return b;
	}

	/** Returns a copy of an array of bytes <i>b</i> */
	public static byte[] clone(byte[] b) {
		return getBytes(b, 0, b.length);
	}

	/**
	 * Returns a <i>len</i>-byte array from array <i>b</i> with offset
	 * <i>offset</i>
	 */
	public static byte[] getBytes(byte[] b, int offset, int len) {
		byte[] bb = new byte[len];
		for (int k = 0; k < len; k++)
			bb[k] = b[offset + k];
		return bb;
	}

	/** Returns a 2-byte array from array <i>b</i> with offset <i>offset</i> */
	public static byte[] twoBytes(byte[] b, int offset) {
		return getBytes(b, offset, 2);
	}

	/** Returns a 4-byte array from array <i>b</i> with offset <i>offset</i> */
	public static byte[] fourBytes(byte[] b, int offset) {
		return getBytes(b, offset, 4);
	}

	/**
	 * Copies all bytes of array <i>src</i> into array <i>dst</i> with offset
	 * <i>offset</i>
	 */
	public static void copyBytes(byte[] src, byte[] dst, int offset) {
		for (int k = 0; k < src.length; k++)
			dst[offset + k] = src[k];
	}

	/**
	 * Copies the first <i>len</i> bytes of array <i>src</i> into array <i>dst</i>
	 * with offset <i>offset</i>
	 */
	public static void copyBytes(byte[] src, byte[] dst, int offset, int len) {
		for (int k = 0; k < len; k++)
			dst[offset + k] = src[k];
	}

	/**
	 * Copies the first 2 bytes of array <i>src</i> into array <i>dst</i> with
	 * offset <i>offset</i>
	 */
	public static void copyTwoBytes(byte[] src, byte[] dst, int offset) {
		copyBytes(src, dst, offset, 2);
	}

	/**
	 * Copies a the first 4 bytes of array <i>src</i> into array <i>dst</i>
	 * with offset <i>index</i>
	 */
	public static void copyFourBytes(byte[] src, byte[] dst, int offset) {
		copyBytes(src, dst, offset, 4);
	}

	/**
	 * Transforms the first <i>len</i> bytes of an array into a string of hex
	 * values
	 */
	public static String bytesToHexString(byte[] b, int len) {
		String s = new String();
		for (int i = 0; i < len; i++) {
			s += Integer.toHexString((((b[i] + 256) % 256) / 16) % 16);
			s += Integer.toHexString(((b[i] + 256) % 256) % 16);
		}
		return s;
	}

	/** Transforms a byte array into a string of hex values */
	public static String bytesToHexString(byte[] b) {
		return bytesToHexString(b, b.length);
	}

	/**
	 * Transforms a string of hex values into an array of bytes of max length
	 * <i>len</i>. The string may include ':' chars. If <i>len</i> is set to
	 * -1, all string is converted.
	 */
	public static byte[] hexStringToBytes(String str, int len) { // if the
		// string is
		// of the
		// form
		// xx:yy:zz:ww..,
		// remove
		// all ':'
		// first
		if (str.indexOf(":") >= 0) {
			String aux = "";
			char c;
			for (int i = 0; i < str.length(); i++)
				if ((c = str.charAt(i)) != ':')
					aux += c;
			str = aux;
		}
		// if len=-1, set the len value
		if (len < 0)
			len = str.length() / 2;
		byte[] b = new byte[len];
		for (int i = 0; i < len; i++) {
			if (len < str.length() / 2)
				b[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2),
						16);
			else
				b[i] = 0;
		}
		return b;
	}

	/**
	 * Transforms a string of hex values into an array of bytes. The string may
	 * include ':' chars.
	 */
	public static byte[] hexStringToBytes(String str) {
		return hexStringToBytes(str, -1);
	}

	/** Transforms a four-bytes array into a dotted four-decimals string */
	public static String bytesToAddress(byte[] b) {
		return Integer.toString(uByte(b[0])) + "."
				+ Integer.toString(uByte(b[1])) + "."
				+ Integer.toString(uByte(b[2])) + "."
				+ Integer.toString(uByte(b[3]));
	}

	/** Transforms a dotted four-decimals string into a four-bytes array */
	public static byte[] addressToBytes(String addr) {
		int begin = 0, end;
		byte[] b = new byte[4];
		for (int i = 0; i < 4; i++) {
			String num;
			if (i < 3) {
				end = addr.indexOf('.', begin);
				b[i] = (byte) Integer.parseInt(addr.substring(begin, end));
				begin = end + 1;
			} else
				b[3] = (byte) Integer.parseInt(addr.substring(begin));
		}
		return b;
	}

	/** Transforms a 4-bytes array into a 32-bit int */
	public static long bytesToInt(byte[] b) {
		return ((((((long) uByte(b[0]) << 8) + uByte(b[1])) << 8) + uByte(b[2])) << 8)
				+ uByte(b[3]);
	}

	/** Transforms a 32-bit int into a 4-bytes array */
	public static byte[] intToBytes(long n) {
		byte[] b = new byte[4];
		b[0] = (byte) (n >> 24);
		b[1] = (byte) ((n >> 16) % 256);
		b[2] = (byte) ((n >> 8) % 256);
		b[3] = (byte) (n % 256);
		return b;
	}

	/**
	 * Transforms a 4-bytes array into a 32-bit word (with the more
	 * significative byte at left)
	 */
	public static long bytesToWord(byte[] b, int offset) {
		return ((((((long) uByte(b[offset + 3]) << 8) + uByte(b[offset + 2])) << 8) + uByte(b[offset + 1])) << 8)
				+ uByte(b[offset + 0]);
	}

	/**
	 * Transforms a 4-bytes array into a 32-bit word (with the more
	 * significative byte at left)
	 */
	public static long bytesToWord(byte[] b) {
		return ((((((long) uByte(b[3]) << 8) + uByte(b[2])) << 8) + uByte(b[1])) << 8)
				+ uByte(b[0]);
	}

	/**
	 * Transforms a 32-bit word (with the more significative byte at left) into
	 * a 4-bytes array
	 */
	public static byte[] wordToBytes(long n) {
		byte[] b = new byte[4];
		b[3] = (byte) (n >> 24);
		b[2] = (byte) ((n >> 16) % 256);
		b[1] = (byte) ((n >> 8) % 256);
		b[0] = (byte) (n % 256);
		return b;
	}

	private static void print(String str) {
		System.out.println(str);
	}

	// *************************** MAIN ****************************

	private static void decode(byte buffer[], int[] out) {
		int offset = 0;
		int len = 64;
		for (int i = 0; offset < len; i++, offset += 4) {
			out[i] = ((int) (buffer[offset] & 0xff))
					| (((int) (buffer[offset + 1] & 0xff)) << 8)
					| (((int) (buffer[offset + 2] & 0xff)) << 16)
					| (((int) buffer[offset + 3]) << 24);
		}
	}

	public static void main(String[] args) {
		byte[] buff = new byte[64];
		for (int i = 0; i < 64; i++)
			buff[i] = (byte) i;

		int[] x = new int[16];
		for (int i = 0; i < 16; i++)
			x[i] = (int) bytesToWord(buff, (i * 4));

		for (int i = 0; i < 16; i++)
			print("x[" + i + "]: " + bytesToHexString(wordToBytes(x[i])));
		decode(buff, x);
		for (int i = 0; i < 16; i++)
			print("x[" + i + "]: " + bytesToHexString(wordToBytes(x[i])));
	}
}
