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

	var that

	, Types		= Toolbox.Base.extend({
		constructor:	function (code) {
			this.code		= code
			, this.length	= null
			;

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

				}
			}

			return this;
		}

		, code:		null
		, length:	null
		, unpack:	null

		, getCode:			function() {
			return this.code;
		}

		, getByteLength:	function() {
			return this.length;
		}

	}
	, {


		typeMap:	{
			1:	{
				length:		1
				, unpack:	function (chunk) {
					Types.getInt8(chunk);
				}
			}

			, 2:	{
				length:		1
				, unpack:	function (chunk) {
					Types.getUint8(chunk);
				}
			}

			, 3:	{
				length:		2
				, unpack:	function (chunk) {
					Types.getInt16(chunk);
				}
			}

			, 4:	{
				length:		2
				, unpack:	function (chunk) {
					Types.getUint16(chunk);
				}
			}

			, 5:	{
				length:		4
				, unpack:	function (chunk) {
					Types.getInt32(chunk);
				}
			}

			, 6:	{
				length:		4
				, unpack:	function (chunk) {
					Types.getUint32(chunk);
				}
			}

			, 7:	{
				length:		8
				, unpack:	function (chunk) {
					Types.getFloat64(chunk);
				}
			}

			// There is no unsigned 64bit integer in JS.
			, 8:	{
				length:		8
				, unpack:	function (chunk) {
					Types.getFloat64(chunk);
				}
			}

			, 9:	{
				length:		null
				, unpack:	function (chunk, length) {
					Types.getUTFString(chunk, length);
				}
			}

			, 10:	{
				length:		4
				, unpack:	function (chunk) {
					Types.getDate(chunk);
				}
			}

			, 11:	{
				length:		2
				, unpack:	function (chunk) {
					Types.getVersion(chunk);
				}
			}

			, 12:	{
				length:		null
				, unpack:	function (chunk, length) {
					Types.getModel(chunk, length);
				}
			}
		}

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

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

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

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

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

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

		, getFloat64:			function (chunk) {
			return new DataView(chunk.slice(0,Types.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(Types.getUint32(chunk));
		}

		, getVersion:			function (chunk) {
			return Types.getInt8(chunk.slice(0,1)) + "." + Types.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 Types.getString(chunk.slice(0, 4)).toLowerCase();
		}

	});

	return Types;
});
