/*

Copyright 2008-2011 Clipperz Srl

This file is part of Clipperz's Javascript Crypto Library.
Javascript Crypto Library provides web developers with an extensive
and efficient set of cryptographic functions. The library aims to
obtain maximum execution speed while preserving modularity and
reusability.
For further information about its features and functionalities please
refer to http://www.clipperz.com

* Javascript Crypto Library 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.

* Javascript Crypto Library 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 Javascript Crypto Library.  If not, see
  <http://www.gnu.org/licenses/>.

*/

if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }

//=============================================================================

Clipperz.ByteArray_abstract = function(args) {
	return this;
}

Clipperz.ByteArray_abstract.prototype = MochiKit.Base.update(null, {

	//-------------------------------------------------------------------------

	'toString': function() {
		return "Clipperz.ByteArray_abstract";
	},
	
	//-------------------------------------------------------------------------

	'equals': function(aValue) {
		return (this.compare(aValue) == 0);
	},
	
	//-------------------------------------------------------------------------

	'compare': function(aValue) {
		var result;
		var i;
		
		result = MochiKit.Base.compare(this.length(), aValue.length());
		i = this.length();
		
		while ((result == 0) && (i>0)) {
			i--;
			result = MochiKit.Base.compare(this.byteAtIndex(i), aValue.byteAtIndex(i));
		}
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'clone': function() {
		throw Clipperz.Base.exception.AbstractMethod;
	},

	//-------------------------------------------------------------------------

	'newInstance': function() {
		throw Clipperz.Base.exception.AbstractMethod;
	},
	
	//-------------------------------------------------------------------------

	'reset': function() {
		throw Clipperz.Base.exception.AbstractMethod;
	},
	
	//-------------------------------------------------------------------------
	
	'length': function() {
		throw Clipperz.Base.exception.AbstractMethod;
	},
	
	//-------------------------------------------------------------------------

	'checkByteValue': function(aValue) {
//Clipperz.log("aValue", aValue.toString(16));
//Clipperz.log("(aValue & 0xff)", (aValue & 0xff).toString(16));

		if ((aValue & 0xff) != aValue) {
			MochiKit.Logging.logError("Clipperz.ByteArray.appendByte: the provided value (0x" + aValue.toString(16) + ") is not a byte value.");
			throw Clipperz.ByteArray.exception.InvalidValue;
		}
	},
	
	//-------------------------------------------------------------------------

	'xorMergeWithBlock': function(aBlock, anAllignment, paddingMode) {
		var result;
		var a, b;
		var aLength;
		var bLength;			
		var i, c;
		
		if (this.length() > aBlock.length()) {
			a = this;
			b = aBlock;
		} else {
			a = aBlock;
			b = this;
		}

		aLength = a.length();
		bLength = b.length();

		if (aLength != bLength) {
			if (paddingMode == 'truncate') {
				if (anAllignment == 'left') {
					a = a.split(0, bLength);
				} else {
					a = a.split(aLength - bLength);
				}
			} else {
				var ii, cc;
				var padding;
				
//				padding = new Clipperz.ByteArray();
				padding = this.newInstance();
				cc = aLength - bLength;
				for (ii=0; ii<cc; ii++) {
					padding.appendByte(0);
				}
				
				if (anAllignment == 'left') {
					b = b.appendBlock(padding);
				} else {
					b = padding.appendBlock(b);
				}
			}
		}


//		result = new Clipperz.ByteArray();
		result = this.newInstance();
		c = a.length();
		for (i=0; i<c; i++) {
			result.appendByte(a.byteAtIndex(i) ^ b.byteAtIndex(i));
		}

		return result;
	},

	//-------------------------------------------------------------------------
/*
	'shiftLeft': function(aNumberOfBitsToShift) {
		var result;

		result = this.clone();	//	???????????
		
		return result;
	},
*/	
	//-------------------------------------------------------------------------

	'appendBlock': function(aBlock) {
		throw Clipperz.Base.exception.AbstractMethod;
	},

	//-------------------------------------------------------------------------

	'appendByte': function(aValue) {
		throw Clipperz.Base.exception.AbstractMethod;
	},

	'appendBytes': function(args) {
		var	values;
		var	i,c;

		if (args.constructor == Array) {
			values = args;
		} else {
			values = arguments;
		}

		c = values.length;
		for (i=0; i<c; i++) {
			this.appendByte(values[i]);
		}
		
		return this;
	},
	
	//-------------------------------------------------------------------------

	'appendWord': function(aValue, isLittleEndian) {
		var result;
		var processAsLittleEndian;
		
		processAsLittleEndian = isLittleEndian === true ? true : false;
		
		if (processAsLittleEndian) {
			result = this.appendBytes(	(aValue) & 0xff, (aValue >> 8) & 0xff, (aValue >> 16) & 0xff, (aValue >> 24) & 0xff	);	//	little endian
		} else {
			result = this.appendBytes(	(aValue >> 24) & 0xff, (aValue >> 16) & 0xff, (aValue >> 8) & 0xff, (aValue) & 0xff	);	//	big endian - DEFAULT
		}
		
		return result;
	},

	'appendWords': function(args) {
		var	values;
		var	i,c;

		if (args.constructor == Array) {
			values = args;
		} else {
			values = arguments;
		}

		c = values.length;
		for (i=0; i<c; i++) {
			this.appendWord(values[i], false);
		}
		
		return this;
	},

	//-------------------------------------------------------------------------

	'appendBigEndianWords': function(args) {
		var	values;
		var	i,c;

		if (args.constructor == Array) {
			values = args;
		} else {
			values = arguments;
		}

		c = values.length;
		for (i=0; i<c; i++) {
			this.appendWord(values[i], true);
		}
		
		return this;
	},

	//-------------------------------------------------------------------------

	'appendBinaryString': function (aBinaryString) {
		var i,c;
		
		c = aBinaryString.length;
		for (i=0; i<c; i++) {
			this.appendByte(aBinaryString.charCodeAt(i));
		};

		return this;
	},

	//-------------------------------------------------------------------------

	'byteAtIndex': function(anIndex) {
		throw Clipperz.Base.exception.AbstractMethod;
	},
	
	'setByteAtIndex': function(aValue, anIndex) {
		throw Clipperz.Base.exception.AbstractMethod;
	},

	//-------------------------------------------------------------------------

	'bitAtIndex': function(aBitPosition) {
		var result;
		var	bytePosition;
		var bitPositionInSelectedByte;
		var selectedByte;
		var selectedByteMask;
		
		bytePosition = this.length() - Math.ceil((aBitPosition + 1)/ 8);
		bitPositionInSelectedByte = aBitPosition % 8;
		selectedByte = this.byteAtIndex(bytePosition);

		if (bitPositionInSelectedByte > 0) {
			selectedByteMask = (1 << bitPositionInSelectedByte);
		} else {
			selectedByteMask = 1;
		}
		result = selectedByte & selectedByteMask ? 1 : 0;
//console.log("aBitPosition: " + aBitPosition + ", length: " + this.length() + ", bytePosition: " + bytePosition + ", bitPositionInSelectedByte: " + bitPositionInSelectedByte + ", selectedByteMask: " + selectedByteMask);
		
		return result;
	},

	//-------------------------------------------------------------------------

	'bitBlockAtIndexWithSize': function(aBitPosition, aSize) {
		var result;
		var bitValue;
		var i,c;
		
		result = 0;
		c = aSize;
		for (i=0; i<c; i++) {
			bitValue = this.bitAtIndex(aBitPosition + i);
			result = result | bitValue << i;
		}
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'asString': function() {
		var	result;
		var	length;
		var	i;

//var startTime = new Date();

//#		result = "";
		result = [];
		
		i = 0;
		length = this.length();
		
		while (i < length) {
			var	currentCharacter;
			var	currentByte;
			var	unicode;
			
			currentByte = this.byteAtIndex(i);
			
			if ((currentByte & 0x80) == 0x00 ) {		//	0xxxxxxx
				unicode = currentByte;
				currentCharacter = String.fromCharCode(unicode);
			} else if ((currentByte & 0xe0) == 0xc0 ) {	//	110xxxxx 10xxxxxx
				unicode = (currentByte & 0x1f) << 6;
				i++; currentByte = this.byteAtIndex(i);
				unicode = unicode | (currentByte & 0x3f);

				currentCharacter = String.fromCharCode(unicode);
			} else if ((currentByte & 0xf0) == 0xe0 ) {	//	1110xxxx 10xxxxxx 10xxxxxx
				unicode = (currentByte & 0x0f) << (6+6);
				i++; currentByte = this.byteAtIndex(i);
				unicode = unicode | ((currentByte & 0x3f) << 6);
				i++; currentByte = this.byteAtIndex(i);
				unicode = unicode | (currentByte & 0x3f);
				
				currentCharacter = String.fromCharCode(unicode);
			} else {									//	11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
				unicode = (currentByte & 0x07) << (6+6+6);
				i++; currentByte = this.byteAtIndex(i);
				unicode = unicode | ((currentByte & 0x3f) << (6+6));
				i++; currentByte = this.byteAtIndex(i);
				unicode = unicode | ((currentByte & 0x3f) << 6);
				i++; currentByte = this.byteAtIndex(i);
				unicode = unicode | (currentByte & 0x3f);
				
				currentCharacter = String.fromCharCode(unicode);
			}
			
//			result += currentCharacter;
			result.push(currentCharacter);
			i++;
		}

//MochiKit.Logging.logDebug("[" + (new Date() - startTime) + "] ByteArray.asString");

//		return result;
		return result.join("");
	},

	//-------------------------------------------------------------------------

	'toHexString': function() {
		throw Clipperz.Base.exception.AbstractMethod;
	},
	
	//-------------------------------------------------------------------------
	
	'base64map': "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
	'base64mapIndex': "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(''),
//	'base64mapInvertedIndex': {
//		'A':  0, 'B':  1, 'C':  2, 'D':  3, 'E':  4, 'F':  5, 'G':  6, 'H':  7, 'I':  8, 'J':  9,
//		'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19,
//		'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25, 'a': 26, 'b': 27, 'c': 28, 'd': 29,
//		'e': 30, 'f': 31, 'g': 32, 'h': 33, 'i': 34, 'j': 35, 'k': 36, 'l': 37, 'm': 38, 'n': 39,
//		'o': 40, 'p': 41, 'q': 42, 'r': 43, 's': 44, 't': 45, 'u': 46, 'v': 47, 'w': 48, 'x': 49,
//		'y': 50, 'z': 51, '0': 52, '1': 53, '2': 54, '3': 55, '4': 56, '5': 57, '6': 58, '7': 59,
//		'8': 60, '9': 61, '+': 62, '/': 63,
//		"=": -1},

	//-------------------------------------------------------------------------
	
	'appendBase64String': function(aValue) {
		var i;
		var length;

		length = aValue.length;
		
		if ((length % 4) != 0) {
			MochiKit.Logging.logError("the value passed to the 'ByteArray.setBase64Value' is not correct");
			throw Clipperz.ByteArray.exception.InvalidValue;
		}

		i = 0;
		while (i<length) {
			var value1, value2, value3, value4;
			var byte1, byte2, byte3;
			
			value1 = this.base64map.indexOf(aValue.charAt(i));
			value2 = this.base64map.indexOf(aValue.charAt(i+1));
			value3 = this.base64map.indexOf(aValue.charAt(i+2));
			value4 = this.base64map.indexOf(aValue.charAt(i+3));

//			value1 = this.base64mapInvertedIndex[aValue.charAt(i)];
//			value2 = this.base64mapInvertedIndex[aValue.charAt(i+1)];
//			value3 = this.base64mapInvertedIndex[aValue.charAt(i+2)];
//			value4 = this.base64mapInvertedIndex[aValue.charAt(i+3)];

			byte1 = (value1 << 2) | ((value2 & 0x30) >> 4);
			if (value3 != -1) {
				byte2 = ((value2 & 0x0f) << 4) | ((value3 & 0x3c) >> 2);

				if (value4 != -1) {
					byte3 = ((value3 & 0x03) << 6) | (value4);
				} else {
					byte3 = null;
				}
			} else {
				byte2 = null;
				byte3 = null;
			}

			this.appendByte(byte1);
			this.appendByte(byte2);
			this.appendByte(byte3);

			i += 4;
		}
		
		return this;
	},

	//-------------------------------------------------------------------------
	
	'toBase64String': function() {
		var result;
		var length;
		var i;
		var byte1, byte2, byte3;
		var char1, char2, char3, char4;
		
		i = 0;
		length = this.length();
		result = new Array(Math.ceil(length/3));
		
		while (i < length) {
			byte1 = this.byteAtIndex(i);
			if ((i+2) < length) {
				byte2 = this.byteAtIndex(i+1);
				byte3 = this.byteAtIndex(i+2);
			} else if ((i+2) == length) {
				byte2 = this.byteAtIndex(i+1);
				byte3 = null;
			} else {
				byte2 = null;
				byte3 = null;
			}
			
			char1 = this.base64mapIndex[byte1 >> 2];
			if (byte2 != null) {
				char2 = this.base64mapIndex[((byte1 & 0x03) << 4) | ((byte2 & 0xf0) >> 4)];
				if (byte3 != null) {
					char3 = this.base64mapIndex[((byte2 & 0x0f) << 2) | ((byte3 & 0xc0) >> 6)];
					char4 = this.base64mapIndex[(byte3 & 0x3f)];
				} else {
					char3 = this.base64mapIndex[(byte2 & 0x0f) << 2];
					char4 = "=";
				}
			} else {
				char2 = this.base64mapIndex[(byte1 & 0x03) << 4];
				char3 = "=";
				char4 = "=";
			}

			result.push(char1 + char2 + char3 + char4);
			
			i += 3;
		}

		return result.join("");
	},

	//-------------------------------------------------------------------------

	'base32map': "0123456789abcdefghjkmnpqrstvwxyz",
	'base32mapIndex': "0123456789abcdefghjkmnpqrstvwxyz".split(''),

	//-------------------------------------------------------------------------
	
	'appendBase32String': function(aValue) {
		var value;
		var i;
		var length;
		var value1, value2, value3, value4, value5, value6, value7, value8;
		var byte1, byte2, byte3, byte4, byte5;

		value = aValue.toLowerCase();
		value = value.replace(/[\s\-]/g, '');
		value = value.replace(/[0o]/g, '0');
		value = value.replace(/[1il]/g, '1');

		length = value.length;
		
		if ((length % 8) != 0) {
			MochiKit.Logging.logError("the value passed to the 'ByteArray.setBase32Value' is not correct");
			throw Clipperz.ByteArray.exception.InvalidValue;
		}

		i = 0;
		while (i<length) {
			value1 = this.base32map.indexOf(value.charAt(i));
			value2 = this.base32map.indexOf(value.charAt(i+1));
			value3 = this.base32map.indexOf(value.charAt(i+2));
			value4 = this.base32map.indexOf(value.charAt(i+3));
			value5 = this.base32map.indexOf(value.charAt(i+4));
			value6 = this.base32map.indexOf(value.charAt(i+5));
			value7 = this.base32map.indexOf(value.charAt(i+6));
			value8 = this.base32map.indexOf(value.charAt(i+7));

			byte1 = byte2 = byte3 = byte4 = byte5 = null;
			
			byte1 = (value1 << 3) | ((value2 & 0x1c) >> 2);
			if (value3 != -1) {
				byte2 = ((value2 & 0x03) << 6) | (value3 << 1) | ((value4 & 0x10) >> 4);
				if (value5 != -1) {
					byte3 = ((value4 & 0x0f) << 4) | ((value5 & 0x1e) >> 1);
					if (value6 != -1) {
						byte4 = ((value5 & 0x01) << 7) | (value6 << 2) | ((value7 & 0x18) >> 3);
						if (value8 != -1) {
							byte5 = ((value7 & 0x07) << 5) | (value8);
						}
					}
				}
			}

			this.appendByte(byte1);
			this.appendByte(byte2);
			this.appendByte(byte3);
			this.appendByte(byte4);
			this.appendByte(byte5);

			i += 8;
		}

		return this;
	},

	//-------------------------------------------------------------------------

	'toBase32String': function() {
		var result;
		var length;
		var i;
		var byte1, byte2, byte3, byte4, byte5;
		var char1, char2, char3, char4, char5, char6, char7, char8;
		
		i = 0;
		length = this.length();
		result = new Array(Math.ceil(length/5));
		
		while (i < length) {
			byte1 = this.byteAtIndex(i);
			
			if ((i+4) < length) {
				byte2 = this.byteAtIndex(i+1);
				byte3 = this.byteAtIndex(i+2);
				byte4 = this.byteAtIndex(i+3);
				byte5 = this.byteAtIndex(i+4);
			} else if ((i+4) == length) {
				byte2 = this.byteAtIndex(i+1);
				byte3 = this.byteAtIndex(i+2);
				byte4 = this.byteAtIndex(i+3);
				byte5 = null;
			} else if ((i+3) == length) {
				byte2 = this.byteAtIndex(i+1);
				byte3 = this.byteAtIndex(i+2);
				byte4 = null;
				byte5 = null;
			} else if ((i+2) == length) {
				byte2 = this.byteAtIndex(i+1);
				byte3 = null;
				byte4 = null;
				byte5 = null;
			} else {
				byte2 = null;
				byte3 = null;
				byte4 = null;
				byte5 = null;
			}

			
			char1 = this.base32mapIndex[byte1 >> 3];
			char2 = char3 = char4 = char5 = char6 = char7 = char8 = "=";
			
			if (byte2 != null) {
				char2 = this.base32mapIndex[((byte1 & 0x07) << 2) | ((byte2 & 0xc0) >> 6)];
				char3 = this.base32mapIndex[((byte2 & 0x3e) >> 1)];
				if (byte3 != null) {
					char4 = this.base32mapIndex[((byte2 & 0x01) << 4) | ((byte3 & 0xf0) >> 4)];
					if (byte4 != null) {
						char5 = this.base32mapIndex[((byte3 & 0x0f) << 1) | ((byte4 & 0x80) >> 7)];
						char6 = this.base32mapIndex[(byte4 & 0x7c) >> 2];
						if (byte5 != null) {
							char7 = this.base32mapIndex[((byte4 & 0x03) << 3) | ((byte5 & 0xe0) >> 5)];
							char8 = this.base32mapIndex[(byte5 & 0x1f)];
						} else {
							char7 = this.base32mapIndex[(byte4 & 0x03) << 3];
						}
					} else {
						char5 = this.base32mapIndex[(byte3 & 0x0f) << 1];
					}

				} else {
					char4 = this.base32mapIndex[(byte2 & 0x01) << 4];
				}
			} else {
				char2 = this.base32mapIndex[(byte1 & 0x07) << 2];
			}

			result.push(char1 + char2 + char3 + char4 + char5 + char6 + char7 + char8);
			i += 5;
		}

		return result.join("");
	},
	
	//-------------------------------------------------------------------------

	'toBinaryString': function () {
		var	i, c;
		var result;

		result = '';

		c = this.length();
		for (i=0; i<c; i++) {
			result += String.fromCharCode(this.byteAtIndex(i));
		}
		
		return result;
	},


	//-------------------------------------------------------------------------

	'split': function(aStartingIndex, anEndingIndex) {
		throw Clipperz.Base.exception.AbstractMethod;
	},

	//-------------------------------------------------------------------------

	'increment': function() {
		var i;
		var done;
		
		done = false;
		i = this.length() - 1;
		
		while ((i>=0) && (done == false)) {
			var currentByteValue;
			
			currentByteValue = this.byteAtIndex(i);
			
			if (currentByteValue == 0xff) {
				this.setByteAtIndex(0, i);
				if (i>= 0) {
					i --;
				} else {
					done = true;
				}
			} else {
				this.setByteAtIndex(currentByteValue + 1, i);
				done = true;
			}
		}
	},
	
	//-------------------------------------------------------------------------

	'arrayValues': function() {
		throw Clipperz.Base.exception.AbstractMethod;
	},
	
	//-------------------------------------------------------------------------
	__syntaxFix__: "syntax fix"
	
});

//=============================================================================
//
//	Clipperz.ByteArray_hex
//
//=============================================================================
/*
Clipperz.ByteArray_hex = function (args) {
	this._value = "";

	if (typeof(args) != 'undefined') {
		if (args.constructor == Array) {
			this.appendBytes(args);
		} else if (args.constructor == String) {
			if (args.indexOf("0x") == 0) {
				var	value;
			
				value = args.substring(2).toLowerCase();
				if (/[0123456789abcdef]* /.test(value)) {	 the space in the regexp shoud be removed if the code is activate
					if ((value.length % 2) == 0) {
						this._value = value;
					} else {
						this._value = "0" + value;
					}
				} else {
MochiKit.Logging.logError("Clipperz.ByteArray should be inizialized with an hex string.");
					throw Clipperz.ByteArray.exception.InvalidValue;
				}
			} else {
				var	value;
				var	i,c;

				c = args.length;
				value = new Array(c);
				for (i=0; i<c; i++) {
					value.push(Clipperz.ByteArray.unicodeToUtf8HexString(args.charCodeAt(i)));
				}

				this._value = value.join("");
			}
		} else {
			this.appendBytes(MochiKit.Base.extend(null, arguments));
		}
	}
	return this;
}

Clipperz.ByteArray_hex.prototype = MochiKit.Base.update(new Clipperz.ByteArray_abstract(), {

	//-------------------------------------------------------------------------

	'toString': function() {
		return "Clipperz.ByteArray_hex";
	},
	
	//-------------------------------------------------------------------------

	'clone': function() {
		var result;
		
		result = this.newInstance();
		result._value = this._value;
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'newInstance': function() {
		return new Clipperz.ByteArray_hex();
	},
	
	//-------------------------------------------------------------------------

	'reset': function() {
		this._value = "";
	},
	
	//-------------------------------------------------------------------------
	
	'length': function() {
		return (this._value.length / 2);
	},
	
	//-------------------------------------------------------------------------

	'appendBlock': function(aBlock) {
		this._value = this._value += aBlock.toHexString().substring(2);

		return this;
	},

	//-------------------------------------------------------------------------

	'appendByte': function(aValue) {
		if (aValue != null) {
			this.checkByteValue(aValue);
			this._value += Clipperz.ByteArray.byteToHex(aValue);
		}
		
		return this;
	},

	//-------------------------------------------------------------------------

	'byteAtIndex': function(anIndex) {
		return parseInt(this._value.substr(anIndex*2, 2), 16);
	},
	
	'setByteAtIndex': function(aValue, anIndex) {
		var	missingBytes;
		
		this.checkByteValue(aValue);

		missingBytes = anIndex - this.length();
		
		if (missingBytes < 0) {
			var	currentValue;
			var	firstCutIndex;
			var secondCutIndex;

			firstCutIndex = anIndex * 2;
			secondCutIndex = firstCutIndex + 2;
			currentValue = this._value;
			this._value =	currentValue.substring(0, firstCutIndex) +
							Clipperz.ByteArray.byteToHex(aValue) +
							currentValue.substring(secondCutIndex);
		} else if (missingBytes == 0) {
			this.appendByte(aValue);
		} else {
			var i,c;
			
			c = missingBytes;
			for (i=0; i<c; i++) {
				this.appendByte(0);
			}
			
			this.appendByte(aValue);
		}
	},

	//-------------------------------------------------------------------------

	'toHexString': function() {
		return "0x" + this._value;
	},
	
	//-------------------------------------------------------------------------

	'split': function(aStartingIndex, anEndingIndex) {
		var result;
		var	startingIndex;
		var endingIndex;

		result = this.newInstance();

		startingIndex = aStartingIndex * 2;
		if (typeof(anEndingIndex) != 'undefined') {
			endingIndex = anEndingIndex * 2;
			result._value = this._value.substring(startingIndex, endingIndex);
		} else {
			result._value = this._value.substring(startingIndex);
		}
		
		return result;
	},

	//-------------------------------------------------------------------------

	'arrayValues': function() {
		var result;
		var i,c;
		
		c = this.length();

		result = new Array(c);
		for (i=0; i<c; i++) {
			result[i] = this.byteAtIndex(i);
		}
		
		return result;
	},
	
	//-------------------------------------------------------------------------
	__syntaxFix__: "syntax fix"
});
*/

//=============================================================================
//
//	Clipperz.ByteArray_array
//
//=============================================================================

Clipperz.ByteArray_array = function (args) {
	if (typeof(args) != 'undefined') {
		if (args.constructor == Array) {
			this._value = args.slice(0);
		} else if (args.constructor == String) {
			var result;
			var	value;
			var i, c;
			
			if (args.indexOf("0x") == 0) {
			
				value = args.substring(2).toLowerCase();
				if (/[0123456789abcdef]*/.test(value)) {
					if ((value.length % 2) != 0) {
						value = "0" + value;
					}
				} else {
MochiKit.Logging.logError("Clipperz.ByteArray should be inizialized with an hex string.");
					throw Clipperz.ByteArray.exception.InvalidValue;
				}

				c = value.length / 2
				result = new Array(c);
				for (i=0; i<c; i++) {
					result[i] = parseInt(value.substr(i*2, 2), 16);
				}

			} else {
				var unicode;
				result = [];
				c = args.length;
				for (i=0; i<c; i++) {
//					Clipperz.ByteArray.pushUtf8BytesOfUnicodeChar(result, args.charCodeAt(i));

					unicode = args.charCodeAt(i);
					if (unicode <= 0x7f) {										//	0x00000000 - 0x0000007f -> 0xxxxxxx
						result.push(unicode);
				//	} else if ((unicode >= 0x80) && (unicode <= 0x7ff)) {		//	0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
					} else if (unicode <= 0x7ff) {		//	0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
						result.push((unicode >> 6) | 0xc0);
						result.push((unicode & 0x3F) | 0x80);
				//	} else if ((unicode >= 0x0800) && (unicode <= 0xffff)) {	//	0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
					} else if (unicode <= 0xffff) {	//	0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
						result.push((unicode >> 12) | 0xe0);
						result.push(((unicode >> 6) & 0x3f) | 0x80);
						result.push((unicode & 0x3f) | 0x80);
					} else {													//	0x00010000 - 0x001fffff -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
						result.push((unicode >> 18) | 0xf0);
						result.push(((unicode >> 12) & 0x3f) | 0x80);
						result.push(((unicode >> 6) & 0x3f) | 0x80);
						result.push((unicode & 0x3f) | 0x80);
					}
				}
			}
			
		
			this._value = result;
		} else {
			this._value = [];
			this.appendBytes(MochiKit.Base.extend(null, arguments));
		}
	} else {
		this._value = [];
	}
	
	return this;
}

Clipperz.ByteArray_array.prototype = MochiKit.Base.update(new Clipperz.ByteArray_abstract(), {

	//-------------------------------------------------------------------------

	'toString': function() {
		return "Clipperz.ByteArray_array";
	},
	
	//-------------------------------------------------------------------------

	'clone': function() {
		var result;
		
		result = this.newInstance();
		result.appendBytes(this._value);
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'newInstance': function() {
		return new Clipperz.ByteArray_array();
	},
	
	//-------------------------------------------------------------------------

	'reset': function() {
		this._value = [];
	},
	
	//-------------------------------------------------------------------------
	
	'length': function() {
		return (this._value.length);
	},
	
	//-------------------------------------------------------------------------

	'appendBlock': function(aBlock) {
		MochiKit.Base.extend(this._value, aBlock._value);
		
		return this;
	},

	//-------------------------------------------------------------------------

	'appendByte': function(aValue) {
		if (aValue != null) {
			this.checkByteValue(aValue);
			this._value.push(aValue);
		}
		
		return this;
	},

	//-------------------------------------------------------------------------

	'byteAtIndex': function(anIndex) {
		return this._value[anIndex];
	},
	
	'setByteAtIndex': function(aValue, anIndex) {
		var	missingBytes;
		
		this.checkByteValue(aValue);

		missingBytes = anIndex - this.length();
		
		if (missingBytes < 0) {
			this._value[anIndex] = aValue;
		} else if (missingBytes == 0) {
			this._value.push(aValue);
		} else {
			var i,c;
			
			c = missingBytes;
			for (i=0; i<c; i++) {
				this._value.push(0);
			}
			
			this._value.push(aValue);
		}
	},

	//-------------------------------------------------------------------------

	'toHexString': function() {
		var result;
		var i, c;
		
		result = "0x";
		c = this.length();
		for (i=0; i<c; i++) {
			result += Clipperz.ByteArray.byteToHex(this._value[i]);
		}
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'split': function(aStartingIndex, anEndingIndex) {
		var result;
		
		result = this.newInstance();
		result._value = this._value.slice(aStartingIndex, anEndingIndex ? anEndingIndex : this.length());
		
		return result;
	},

	//-------------------------------------------------------------------------

	'arrayValues': function() {
		return this._value.slice(0);
	},
	
	//-------------------------------------------------------------------------
	__syntaxFix__: "syntax fix"
});





//=============================================================================
//
//	Clipperz.ByteArray_string
//
//=============================================================================
/*
Clipperz.ByteArray_string = function (args) {
	this._value = "";

	if (typeof(args) != 'undefined') {
		if (args.constructor == Array) {
			this.appendBytes(args);
		} else if (args.constructor == String) {
			var result;
			var	value;
			var i, c;
			
			if (args.indexOf("0x") == 0) {
			
				value = args.substring(2).toLowerCase();
				if (/[0123456789abcdef]* /.test(value)) { the space in the regexp shoud be removed if the code is activated
					if ((value.length % 2) != 0) {
						value = "0" + value;
					}
				} else {
MochiKit.Logging.logError("Clipperz.ByteArray should be inizialized with an hex string.");
					throw Clipperz.ByteArray.exception.InvalidValue;
				}
			} else {
				value = "";
				c = args.length;
				for (i=0; i<c; i++) {
					value += Clipperz.ByteArray.unicodeToUtf8HexString(args.charCodeAt(i));
				}
			}
			
			c = value.length / 2
			for (i=0; i<c; i++) {
				this.appendByte(parseInt(value.substr(i*2, 2), 16));
			}
		} else {
			this.appendBytes(MochiKit.Base.extend(null, arguments));
		}
	}
	
	return this;
}

Clipperz.ByteArray_string.prototype = MochiKit.Base.update(new Clipperz.ByteArray_abstract(), {

	//-------------------------------------------------------------------------

	'toString': function() {
		return "Clipperz.ByteArray_string";
	},
	
	//-------------------------------------------------------------------------

	'clone': function() {
		var result;
		
		result = this.newInstance();
		result._value = this._value;
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'newInstance': function() {
		return new Clipperz.ByteArray_string();
	},
	
	//-------------------------------------------------------------------------

	'reset': function() {
		this._value = "";
	},
	
	//-------------------------------------------------------------------------
	
	'length': function() {
		return (this._value.length);
	},
	
	//-------------------------------------------------------------------------

	'appendBlock': function(aBlock) {
		this._value += aBlock._value;
		
		return this;
	},

	//-------------------------------------------------------------------------

	'appendByte': function(aValue) {
		if (aValue != null) {
			this.checkByteValue(aValue);
			this._value += String.fromCharCode(aValue);
		}
		
		return this;
	},

	//-------------------------------------------------------------------------

	'byteAtIndex': function(anIndex) {
		return this._value.charCodeAt(anIndex);
	},
	
	'setByteAtIndex': function(aValue, anIndex) {
		var	missingBytes;
		
		this.checkByteValue(aValue);

		missingBytes = anIndex - this.length();
		
		if (missingBytes < 0) {
			this._value = this._value.substring(0, anIndex) + String.fromCharCode(aValue) + this._value.substring(anIndex + 1);
		} else if (missingBytes == 0) {
			this.appendByte(aValue);
		} else {
			var i,c;
			
			c = missingBytes;
			for (i=0; i<c; i++) {
				this.appendByte(0);
			}
			
			this.appendByte(aValue);
		}
	},

	//-------------------------------------------------------------------------

	'toHexString': function() {
		var result;
		var i, c;
		
		result = "0x";
		c = this.length();
		for (i=0; i<c; i++) {
			result += Clipperz.ByteArray.byteToHex(this.byteAtIndex(i));
		}
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'split': function(aStartingIndex, anEndingIndex) {
		var result;
		result = this.newInstance();
		result._value = this._value.substring(aStartingIndex, anEndingIndex ? anEndingIndex : this.length());
		
		return result;
	},

	//-------------------------------------------------------------------------

	'arrayValues': function() {
		var result;
		var i,c;
		
		c = this.length();

		result = new Array(c);
		for (i=0; i<c; i++) {
			result[i] = this.byteAtIndex(i);
		}
		
		return result;
	},
	
	//-------------------------------------------------------------------------
	__syntaxFix__: "syntax fix"
});
*/

//=============================================================================
//
//	Clipperz.ByteArray
//
//=============================================================================

Clipperz.ByteArray = Clipperz.ByteArray_array;
//Clipperz.ByteArray = Clipperz.ByteArray_string;
//Clipperz.ByteArray = Clipperz.ByteArray_hex;

//#############################################################################

Clipperz.ByteArray.byteToHex = function(aByte) {
	return ((aByte < 16) ? "0" : "") + aByte.toString(16);
}


Clipperz.ByteArray.unicodeToUtf8HexString = function(aUnicode) {
	var result;
	var	self;
	
	self = Clipperz.ByteArray;
	
	if (aUnicode <= 0x7f) {										//	0x00000000 - 0x0000007f -> 0xxxxxxx
		result = self.byteToHex(aUnicode);
//	} else if ((aUnicode >= 0x80) && (aUnicode <= 0x7ff)) {		//	0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
	} else if (aUnicode <= 0x7ff) {		//	0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
		result = self.byteToHex((aUnicode >> 6) | 0xc0);
		result += self.byteToHex((aUnicode & 0x3F) | 0x80);
//	} else if ((aUnicode >= 0x0800) && (aUnicode <= 0xffff)) {	//	0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
	} else if (aUnicode <= 0xffff) {	//	0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
		result = self.byteToHex((aUnicode >> 12) | 0xe0);
		result += self.byteToHex(((aUnicode >> 6) & 0x3f) | 0x80);
		result += self.byteToHex((aUnicode & 0x3f) | 0x80);
	} else {													//	0x00010000 - 0x001fffff -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
		result = self.byteToHex((aUnicode >> 18) | 0xf0);
		result += self.byteToHex(((aUnicode >> 12) & 0x3f) | 0x80);
		result += self.byteToHex(((aUnicode >> 6) & 0x3f) | 0x80);
		result += self.byteToHex((aUnicode & 0x3f) | 0x80);
	}

	return result;
}

Clipperz.ByteArray.pushUtf8BytesOfUnicodeChar = function(anArray, aUnicode) {
	var	self;
	
	self = Clipperz.ByteArray;
	
	if (aUnicode <= 0x7f) {										//	0x00000000 - 0x0000007f -> 0xxxxxxx
		anArray.push(aUnicode);
//	} else if ((aUnicode >= 0x80) && (aUnicode <= 0x7ff)) {		//	0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
	} else if (aUnicode <= 0x7ff) {		//	0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
		anArray.push((aUnicode >> 6) | 0xc0);
		anArray.push((aUnicode & 0x3F) | 0x80);
//	} else if ((aUnicode >= 0x0800) && (aUnicode <= 0xffff)) {	//	0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
	} else if (aUnicode <= 0xffff) {	//	0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
		anArray.push((aUnicode >> 12) | 0xe0);
		anArray.push(((aUnicode >> 6) & 0x3f) | 0x80);
		anArray.push((aUnicode & 0x3f) | 0x80);
	} else {													//	0x00010000 - 0x001fffff -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
		anArray.push((aUnicode >> 18) | 0xf0);
		anArray.push(((aUnicode >> 12) & 0x3f) | 0x80);
		anArray.push(((aUnicode >> 6) & 0x3f) | 0x80);
		anArray.push((aUnicode & 0x3f) | 0x80);
	}
}

Clipperz.ByteArray.prefixMatchingBits = function (aValue, bValue) {
		var	result;
		var i,c;
		
		result = 0;

		c = Math.min(aValue.length(), bValue.length());
		i = 0;
		while (i<c && (aValue.byteAtIndex(i) == bValue.byteAtIndex(i))) {
			result += 8;
			i++;
		}

		if (i<c) {
			var	xorValue;
			
			xorValue = (aValue.byteAtIndex(i) ^ bValue.byteAtIndex(i));
			
			if (xorValue >= 128) {
				result += 0;
			} else if (xorValue >= 64) {
				result += 1;
			} else if (xorValue >= 32) {
				result += 2;
			} else if (xorValue >= 16) {
				result += 3;
			} else if (xorValue >= 8) {
				result += 4;
			} else if (xorValue >= 4) {
				result += 5;
			} else if (xorValue >= 2) {
				result += 6;
			} else if (xorValue >= 1) {
				result += 7;
			}
		}

		return result;
};

Clipperz.ByteArray.exception = {
	InvalidValue: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.InvalidValue")
};

//#############################################################################

Clipperz.ByteArrayIterator = function(args) {
	args = args || {};

	this._byteArray = args.byteArray;
	this._blockSize = args.blockSize;
	this._finalPadding = args.finalPadding || false;
	
	this._currentPosition = 0;
	
	return this;
}

Clipperz.ByteArrayIterator.prototype = MochiKit.Base.update(null, {

	//-------------------------------------------------------------------------

	'toString': function() {
		return "Clipperz.ByteArrayIterator";
	},

	//-------------------------------------------------------------------------

	'blockSize': function() {
		var result;
		
		result = this._blockSize;
		
		return result;
	},

	//-------------------------------------------------------------------------

	'currentPosition': function() {
		var result;
		
		result = this._currentPosition;
		
		return result;
	},

	//-------------------------------------------------------------------------

	'byteArray': function() {
		var result;
		
		result = this._byteArray;
		
		return result;
	},
	
	//-------------------------------------------------------------------------

	'finalPadding': function() {
		var result;
		
		result = this._finalPadding;
		
		return result;
	},
	
	//-------------------------------------------------------------------------
		
	'nextBlock': function() {
		var result;
		var currentPosition;
		var	byteArrayLength;
		
		currentPosition = this._currentPosition;
		byteArrayLength = this.byteArray().length();
		
		if (currentPosition < byteArrayLength) {
			var i,c;

			c = this.blockSize();
			result = new Array(c);
			for (i=0; i<c; i++) {
				if (currentPosition < byteArrayLength) {
					result[i] = this.byteArray().byteAtIndex(currentPosition);
					currentPosition++;
				} else if (this.finalPadding() == true) {
					result[i] = 0;
				}
			}
			
			this._currentPosition = currentPosition;
		} else {
			result = null;
		}
		
		return result;
	},	

	//-------------------------------------------------------------------------

	'nextBlockArray': function() {
		var result;
		var nextBlock;

		nextBlock = this.nextBlock();
		
		if (nextBlock != null) {
			result = new Clipperz.ByteArray(nextBlock);
		} else {
			result = null;
		}
		
		return result;
	},
	
	//-----------------------------------------------------------------------------
	__syntaxFix__: "syntax fix"
	
});
