/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
	"underscore"
	, "toolbox"
]
, function (_, Toolbox) {
	"use strict";

	var
	Types					= function(code) {
		this.code			= code;
		this.length			= null;
		this.unpack			= null;
		this.typeMap		= typeMap;

		this.getCode		= function() {
			return this.code;
		};

		this.getByteLength	= function() {
			return this.length;
		};

		if (_.isNumber(this.code)) {
			if (!_.isUndefined(typeMap[this.code])) {
				_.extend(this, typeMap[this.code]);
			} else {

			}
		}

		return this;
	}

	, TypesPrototype	= {
		getInt8:			function (chunk) {
			return new DataView(chunk.slice(0,typeMap[1].length)).getInt8(0, false);
		}

		, getUint8:			function (chunk) {
			return new DataView(chunk.slice(0,typeMap[2].length)).getUint8(0, false);
		}

		, getInt16:			function (chunk) {
			return new DataView(chunk.slice(0,typeMap[3].length)).getInt16(0, false);
		}

		, getUint16:		function (chunk) {
			return new DataView(chunk.slice(0,typeMap[4].length)).getUint16(0, false);
		}

		, getInt32:			function (chunk) {
			return new DataView(chunk.slice(0,typeMap[5].length)).getInt32(0, false);
		}

		, getUint32:		function (chunk) {
			return new DataView(chunk.slice(0,typeMap[6].length)).getUint32(0, false);
		}

		, getFloat64:		function (chunk) {
			return new DataView(chunk.slice(0,typeMap[7].length)).getFloat64(0, false);
		}

		, getString:		function(chunk, length) {
			if (!_.isNumber(length) || length > chunk.byteLength) {
				length	= chunk.byteLength;
			}

			return String.fromCharCode.apply(null, new Uint8Array(chunk.slice(0, length)));
		}

		// TODO: This might be optimizable
		, getUTFString:		function (chunk, length) {
			var str = '';
			if (!_.isNumber(length) || length > chunk.byteLength) {
				length	= chunk.byteLength;
			}

			for (var i = 0; i < length; i++) {
				var slice	= chunk.slice(i, i+1);
				var num		= new DataView(slice).getUint8(0, false);

				str += num <= 0x7F ?
					num === 0x25 ? "%25" :
					String.fromCharCode(num) :
					"%" + num.toString(16);
			}

			return decodeURIComponent(str);
		}

		, getDate:			function (chunk) {
			return new Date(TypesPrototype.getUint32(chunk) * 1000);
		}

		, getVersion:		function (chunk) {
			return TypesPrototype.getInt8(chunk.slice(0,1)) + "." + TypesPrototype.getInt8(chunk.slice(1,2));
		}

		, getModel:			function (chunk) {
			return chunk;
		}

		, getUnknown:		function (chunk) {
			// Unimplemented
			throw "ERROR_UNIMPLEMENTED";

//			switch (chunk.byteLength) {
//				// We can tring a string
//				default:
//
//				break;
//			}

		}

		, getType:			function (chunk) {
			return TypesPrototype.getString(chunk.slice(0, 4)).toLowerCase();
		}
	}

	, typeMap	= {
		1:	{
			length:		1
			, unpack:	TypesPrototype.getInt8
		}

		, 2:	{
			length:		1
			, unpack:	TypesPrototype.getUint8
		}

		, 3:	{
			length:		2
			, unpack:	TypesPrototype.getInt16
		}

		, 4:	{
			length:		2
			, unpack:	TypesPrototype.getUint16
		}

		, 5:	{
			length:		4
			, unpack:	TypesPrototype.getInt32
		}

		, 6:	{
			length:		4
			, unpack:	TypesPrototype.getUint32
		}

		, 7:	{
			length:		8
			, unpack:	TypesPrototype.getFloat64
		}

		// There is no unsigned 64bit integer in JS.
		, 8:	{
			length:		8
			, unpack:	TypesPrototype.getFloat64
		}

		, 9:	{
			length:		null
			, unpack:	TypesPrototype.getUTFString
		}

		, 10:	{
			length:		4
			, unpack:	TypesPrototype.getDate
		}

		, 11:	{
			length:		2
			, unpack:	TypesPrototype.getVersion
		}

		, 12:	{
			length:		null
			, unpack:	TypesPrototype.getModel
		}
	};

	Types.prototype		= TypesPrototype;

	return Types;
});
