//___________________________________
//                                   \
// Digitharia: Input Engine. version 1.0
//___________________________________/


/*

*/
//----------------------------------------------------------------------------//

/**
 * Create a new InputEngine object.
 * @param {object} init An object containing variables to be set or functions to execute
 * @returns An InputEngine Object
 * @constructor
 *
 * Example 1:
 *
 *   var IK = new InputEngine();
 *   IK.RepressDefault = 500;
 *   IK.myfunction = function(parameter){ <Do something here> };
 *   IK.BindKey( KEY_ESCAPE, "Abort('Escaping')" ); // String can also be a function :)
 *
 * Example 2:
 *
 *  var IK = InputEngine({
 *     'RepressDefault': 500,
 *     'myfunction': function(parameter){ <Do something here> },
 *     'BindKey': [ KEY_ESCAPE, "Abort('Escaping')" ],
 *  });
 *
 * note: init (and objects in general) cant hold multiple values of the same (for example: 'BindKey' can only be used once),
 *       to still call the function multiple times, wrap the parameters in an object, like so:
 *       var IK = InputEngine({
 *         'RepressDefault': 500,
 *         'BindKey': {
 *           1: [ KEY_ESCAPE, "Abort('Escaping')" ],
 *           2: [ KEY_SPACE, "Abort('CONFIRM')" ]
 *         }
 *       });
 *       // the keys for this object may be anything except 'length'
 */
function InputEngine(init)
{
	if(this instanceof InputEngine == false)
		return new InputEngine(init);

	this.type = 'default'; // Default type, no real need to change this.
	this.hooked = ''; // The type currently hooked.
	this.RepressKeyDefault = 200; // 200ms = 5 times a second repress

	this.that = this; // Context of the key press functions

	// Run a function else set a value
	for (var action in init){
		if( (typeof this[action] == 'function') && (typeof init[action] == 'object') ){
			if(init[action].length !== undefined)
				this[action].apply(this, init[action]);
			else
				for(var vars in init[action]){
					this[action].apply(this, init[action][vars]);
				}
		} else
			this[action] = init[action];
	}
	return this;
}

/**
 * Inject variables for Keyboard repressing key (repeating while pressed)
 */
InputEngine.prototype.RepressKeyDefault = 200; // 200ms = 5 times a second repress

/**
 * This object holds all registered pressed keys (see addKey() )
 * Each entry holds a nested object with these values: {start:time, ticks:n, next:time}
 */
InputEngine.prototype.PRESSEDKEYS = {};

/**
 * When you use addKey() to define new key actions, the defined functions are held here
 */
InputEngine.prototype.KEYACTION = {};

/**
 * Hold information how many frames the keyboard has been idle
 */
InputEngine.prototype.KBidle = 0; 

/**
 * @param {object} that Context where the ONKEYDOWN, ONKEYUP and ONKEYPRESS functions are being executed.
 * defaults to the InputEngine object itself. Without this function, you need global variables inside your functions.
 * example:
 *  var SUM = { this.a = 0, this.b = 0 };
 *  var IK = InputEngine();
 *  IK.setThis(SUM);
 *  IK.addKey(KEY_A, function(){ this.a++ } ); // or without setThat: IK.addKey(KEY_A, function(){ SUM.a++ } );
 * note: careful with the closure. 
 */
InputEngine.prototype.setThis = function(that){
	this.that = that || this;
}

/**
 * addKey adds 
 * @param {KEY} key A key variable, see keys.txt in the sphere documentation directory for a list.
 * @param {function/string} press Define a function that will run onkeydown
 * @param {function/string} unpress Define a function that will run onkeyup
 * @param {function/string} repress Define a function that will run onkeypress
 * @param {integer} repressdelay Milliseconds between triggering a new repress action. Optional (will use default)
 * note: to loop through all defined keys use something like this: for (var i in IK.KEYACTION) { }
 */
InputEngine.prototype.addKey = function(key, press, unpress, repress, repressdelay){
	var o = {};
	if(press) o.ONKEYDOWN = typeof press=='function' ? press : Function(press);
	if(unpress) o.ONKEYUP = typeof unpress=='function' ? unpress : Function(unpress);
	if(repress) o.ONKEYPRESS = typeof repress=='function' ? repress : Function(repress);
	o.DELAY = typeof repressdelay != 'undefined' ? repressdelay : this.RepressKeyDefault;
	this.KEYACTION[key] = o;
	return this;
};

/**
 * Make BindKey() do the same as addKey()
 */
InputEngine.prototype.BindKey = InputEngine.prototype.addKey;

/**
 * Delete a certain key action defined with addKey(). You dont need to use delKey when redefining a key with addKey().
 * @param {KEY} key A key variable. Use null to delete all keys
 * @returns this
 * note: to delete all keys you can also just do this: IK.KEYACTION = {};
 */
InputEngine.prototype.delKey =function(key){
	if (key === undefined) return this;
	if (key === null) 
		this.KEYACTION = {};
	else
		delete this.KEYACTION[key];
	return this;
};

/**
 * Make UnbindKey() do the same as delKey()
 */
InputEngine.prototype.UnbindKey = InputEngine.prototype.delKey;

/**
 * Read all pressed keys and handles accordingly. Extra checks have been added for correct key detection.
 * @returns >0 if no keys are pressed 0 if keys were pressed/unpressed. And -1 if someone else just read (and emptied) the key buffer.
 * @type void
 * Now, why is it so much code to read a key input?
 * Well, FlipScreen() or UpdateMapEngine() fill the keyboard buffer. 
 * When we use GetKey(), one key is removed from the buffer, while IsAnyKeyPressed() stays true. Thats what we do in the first part.
 * So, it could be that somewhere else empties the buffer, and we receive an empty keyboard buffer. In this case we wait for the next frame 
 * It could also be that our buffer is old, and the key is not pressed anymore (it happened to me), this situation only occurs during one frame while unpressing.
 * In the second part we check our keys with the reliable IsKeyPressed()
 * In the third part we check if a key has been released
 * Then we empty the keyboard buffer
 * If a key was found, return 0, else return the idle frames.
 * 
 */
InputEngine.prototype.handleKeyboardInput = function() {
	var KEY;
/*	if (IsAnyKeyPressed()){
		if(!AreKeysLeft()) return -1;
		while( AreKeysLeft() ){
			KEY = GetKey();
			if(!IsKeyPressed(KEY)) continue;
			if(!this.PRESSEDKEYS[KEY] && this.KEYACTION[KEY])
				this._press(KEY);
		}
	}
*/	KEY = -1;
	for(KEY in this.KEYACTION){
		if(IsKeyPressed(parseInt(KEY)))
			this._press(KEY);
	}
	for(KEY in this.PRESSEDKEYS){
		if(IsKeyPressed(parseInt(KEY))) continue;
		this._unpress(KEY);
	}
//	while(AreKeysLeft()) { GetKey(); }
	if(KEY == -1)
		return ++this.idle;
	else
		return this.idle=0;
}

InputEngine.prototype.clearKeyboardBuffer = function() {
	while(AreKeysLeft()) { GetKey(); }
}

/**
 * Internal function: Register a keyboard KEY as being pressed and act accordingly
 */
InputEngine.prototype._press = function(KEY){
	var now = GetTime();
	if(this.PRESSEDKEYS[KEY]){
		if(this.PRESSEDKEYS[KEY].next>now) return;
		++this.PRESSEDKEYS[KEY].tick;
		this.PRESSEDKEYS[KEY].next = now + this.KEYACTION[KEY].DELAY;
		if(this.KEYACTION[KEY].ONKEYPRESS)
			this.KEYACTION[KEY].ONKEYPRESS.apply(this.that, [this.PRESSEDKEYS[KEY].tick]);
	}else{
		this.PRESSEDKEYS[KEY] = { start: now, tick: 0, next: (now + this.KEYACTION[KEY].DELAY) };
		if(this.KEYACTION[KEY].ONKEYDOWN)
			this.KEYACTION[KEY].ONKEYDOWN.apply(this.that, [this.PRESSEDKEYS[KEY].tick]);
	}
}

/**
 * Internal function: Register a keyboard KEY as being unpressed and act accordingly
 */
InputEngine.prototype._unpress = function(KEY){
	if(this.KEYACTION[KEY].ONKEYUP)
		this.KEYACTION[KEY].ONKEYUP.apply(this.that, [this.PRESSEDKEYS[KEY].tick]);
	delete this.PRESSEDKEYS[KEY];
}

/**
 * Trigger the key action functions directly
 * note: You can also run them directly: IK.KEYACTION[KEY_A].ONKEYDOWN(); but loose 'this' functionality inside your function.
 */
InputEngine.prototype.ONKEYDOWN = function(KEY){ if(this.KEYACTION[KEY].ONKEYDOWN) this.KEYACTION[KEY].ONKEYDOWN.apply(this.that, [0]); }
InputEngine.prototype.ONKEYUP = function(KEY, tick){ if(this.KEYACTION[KEY].ONKEYUP) this.KEYACTION[KEY].ONKEYUP.apply(this.that, [tick]); }
InputEngine.prototype.ONKEYPRESS = function(KEY, tick){ if(this.KEYACTION[KEY].ONKEYPRESS) this.KEYACTION[KEY].ONKEYPRESS.apply(this.that, [tick]); }

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

/**
 * Mocking functions, emulate keypresses through MockKeyPress() instead of getting them from the keyboard.
 */
InputEngine.prototype.MockKey = {_:0};

/*
 * Make a Mock key press
 * @param {key} KEY A key to press
 */
InputEngine.prototype.MockKeyPress= function(KEY, ms) {
	if(!this.MockKey[KEY])
		this.MockKey['_']++ ;
	var now=GetTime();
	this.MockKey[KEY] = {start:now, tick:0, mocknext:now+ms, next: (now + this.KEYACTION[KEY]?this.KEYACTION[KEY].DELAY:this.RepressKeyDefault)}
};

/*
 * Make a Mock key unpress, or, if you give no parameter, it will unpress all keys (Digitharia will act on them on the next frame)
 * @param {key} KEY A key to unpress
 */
InputEngine.prototype.MockKeyUnpress= function(KEY) {
	if(KEY)
		return this.MockKey[KEY].mocknext = 0; 
	for(var KEY in this.MockKey){
		this.MockKey[KEY].mocknext = 0; 
	};
}

/*
 * Clears all the Mock registered keypresses. 
 */
InputEngine.prototype.MockKeyClear= function() {
	this.MockKey = {_:0};
};

InputEngine.prototype.MockKeyboardInput = function() {
	if(this.MockKey['_']){
		var KEY,now;
		for(var KEY in this.MockKey){
			if(KEY == '_') continue;
			now = GetTime();
			if(this.MockKey[KEY].mocknext<now){
				delete this.MockKey[KEY];
				--this.MockKey['_'];
			}
			if(!this.KEYACTION[KEY]) continue;
			this._press(KEY);
		}
	}
	for(var KEY in this.PRESSEDKEYS){
		if(this.MockKey[KEY]) continue;
		this._unpress(KEY);
	}
}

