/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../../utils/Class');
var EventEmitter = require('eventemitter3');
var Events = require('../events');

/**
 * @classdesc
 * A generic Key object which can be passed to the Process functions (and so on)
 * keycode must be an integer
 *
 * @class Key
 * @extends Phaser.Events.EventEmitter
 * @memberof Phaser.Input.Keyboard
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Input.Keyboard.KeyboardPlugin} plugin - The Keyboard Plugin instance that owns this Key object.
 * @param {integer} keyCode - The keycode of this key.
 */
var Key = new Class({

    Extends: EventEmitter,

    initialize:

    function Key (plugin, keyCode)
    {
        EventEmitter.call(this);

        /**
         * The Keyboard Plugin instance that owns this Key object.
         *
         * @name Phaser.Input.Keyboard.Key#plugin
         * @type {Phaser.Input.Keyboard.KeyboardPlugin}
         * @since 3.17.0
         */
        this.plugin = plugin;

        /**
         * The keycode of this key.
         *
         * @name Phaser.Input.Keyboard.Key#keyCode
         * @type {integer}
         * @since 3.0.0
         */
        this.keyCode = keyCode;

        /**
         * The original DOM event.
         *
         * @name Phaser.Input.Keyboard.Key#originalEvent
         * @type {KeyboardEvent}
         * @since 3.0.0
         */
        this.originalEvent = undefined;

        /**
         * Can this Key be processed?
         *
         * @name Phaser.Input.Keyboard.Key#enabled
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.enabled = true;

        /**
         * The "down" state of the key. This will remain `true` for as long as the keyboard thinks this key is held down.
         *
         * @name Phaser.Input.Keyboard.Key#isDown
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.isDown = false;

        /**
         * The "up" state of the key. This will remain `true` for as long as the keyboard thinks this key is up.
         *
         * @name Phaser.Input.Keyboard.Key#isUp
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.isUp = true;

        /**
         * The down state of the ALT key, if pressed at the same time as this key.
         *
         * @name Phaser.Input.Keyboard.Key#altKey
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.altKey = false;

        /**
         * The down state of the CTRL key, if pressed at the same time as this key.
         *
         * @name Phaser.Input.Keyboard.Key#ctrlKey
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.ctrlKey = false;

        /**
         * The down state of the SHIFT key, if pressed at the same time as this key.
         *
         * @name Phaser.Input.Keyboard.Key#shiftKey
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.shiftKey = false;

        /**
         * The down state of the Meta key, if pressed at the same time as this key.
         * On a Mac the Meta Key is the Command key. On Windows keyboards, it's the Windows key.
         *
         * @name Phaser.Input.Keyboard.Key#metaKey
         * @type {boolean}
         * @default false
         * @since 3.16.0
         */
        this.metaKey = false;

        /**
         * The location of the modifier key. 0 for standard (or unknown), 1 for left, 2 for right, 3 for numpad.
         *
         * @name Phaser.Input.Keyboard.Key#location
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.location = 0;

        /**
         * The timestamp when the key was last pressed down.
         *
         * @name Phaser.Input.Keyboard.Key#timeDown
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.timeDown = 0;

        /**
         * The number of milliseconds this key was held down for in the previous down - up sequence.
         * This value isn't updated every game step, only when the Key changes state.
         * To get the current duration use the `getDuration` method.
         *
         * @name Phaser.Input.Keyboard.Key#duration
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.duration = 0;

        /**
         * The timestamp when the key was last released.
         *
         * @name Phaser.Input.Keyboard.Key#timeUp
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.timeUp = 0;

        /**
         * When a key is held down should it continuously fire the `down` event each time it repeats?
         * 
         * By default it will emit the `down` event just once, but if you wish to receive the event
         * for each repeat as well, enable this property.
         *
         * @name Phaser.Input.Keyboard.Key#emitOnRepeat
         * @type {boolean}
         * @default false
         * @since 3.16.0
         */
        this.emitOnRepeat = false;

        /**
         * If a key is held down this holds down the number of times the key has 'repeated'.
         *
         * @name Phaser.Input.Keyboard.Key#repeats
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.repeats = 0;

        /**
         * True if the key has just been pressed (NOTE: requires to be reset, see justDown getter)
         *
         * @name Phaser.Input.Keyboard.Key#_justDown
         * @type {boolean}
         * @private
         * @default false
         * @since 3.0.0
         */
        this._justDown = false;

        /**
         * True if the key has just been pressed (NOTE: requires to be reset, see justDown getter)
         *
         * @name Phaser.Input.Keyboard.Key#_justUp
         * @type {boolean}
         * @private
         * @default false
         * @since 3.0.0
         */
        this._justUp = false;

        /**
         * Internal tick counter.
         *
         * @name Phaser.Input.Keyboard.Key#_tick
         * @type {number}
         * @private
         * @since 3.11.0
         */
        this._tick = -1;
    },

    /**
     * Controls if this Key will continuously emit a `down` event while being held down (true),
     * or emit the event just once, on first press, and then skip future events (false).
     *
     * @method Phaser.Input.Keyboard.Key#setEmitOnRepeat
     * @since 3.16.0
     * 
     * @param {boolean} value - Emit `down` events on repeated key down actions, or just once?
     * 
     * @return {Phaser.Input.Keyboard.Key} This Key instance.
     */
    setEmitOnRepeat: function (value)
    {
        this.emitOnRepeat = value;

        return this;
    },

    /**
     * Processes the Key Down action for this Key.
     * Called automatically by the Keyboard Plugin.
     *
     * @method Phaser.Input.Keyboard.Key#onDown
     * @fires Phaser.Input.Keyboard.Events#DOWN
     * @since 3.16.0
     * 
     * @param {KeyboardEvent} event - The native DOM Keyboard event.
     */
    onDown: function (event)
    {
        this.originalEvent = event;

        if (!this.enabled)
        {
            return;
        }

        this.altKey = event.altKey;
        this.ctrlKey = event.ctrlKey;
        this.shiftKey = event.shiftKey;
        this.metaKey = event.metaKey;
        this.location = event.location;
    
        this.repeats++;

        if (!this.isDown)
        {
            this.isDown = true;
            this.isUp = false;
            this.timeDown = event.timeStamp;
            this.duration = 0;
            this._justDown = true;
            this._justUp = false;

            this.emit(Events.DOWN, this, event);
        }
        else if (this.emitOnRepeat)
        {
            this.emit(Events.DOWN, this, event);
        }
    },

    /**
     * Processes the Key Up action for this Key.
     * Called automatically by the Keyboard Plugin.
     *
     * @method Phaser.Input.Keyboard.Key#onUp
     * @fires Phaser.Input.Keyboard.Events#UP
     * @since 3.16.0
     * 
     * @param {KeyboardEvent} event - The native DOM Keyboard event.
     */
    onUp: function (event)
    {
        this.originalEvent = event;

        if (!this.enabled)
        {
            return;
        }
    
        this.isDown = false;
        this.isUp = true;
        this.timeUp = event.timeStamp;
        this.duration = this.timeUp - this.timeDown;
        this.repeats = 0;
    
        this._justDown = false;
        this._justUp = true;
        this._tick = -1;
        
        this.emit(Events.UP, this, event);
    },

    /**
     * Resets this Key object back to its default un-pressed state.
     *
     * @method Phaser.Input.Keyboard.Key#reset
     * @since 3.6.0
     * 
     * @return {Phaser.Input.Keyboard.Key} This Key instance.
     */
    reset: function ()
    {
        this.preventDefault = true;
        this.enabled = true;
        this.isDown = false;
        this.isUp = true;
        this.altKey = false;
        this.ctrlKey = false;
        this.shiftKey = false;
        this.metaKey = false;
        this.timeDown = 0;
        this.duration = 0;
        this.timeUp = 0;
        this.repeats = 0;
        this._justDown = false;
        this._justUp = false;
        this._tick = -1;

        return this;
    },

    /**
     * Returns the duration, in ms, that the Key has been held down for.
     * 
     * If the key is not currently down it will return zero.
     * 
     * The get the duration the Key was held down for in the previous up-down cycle,
     * use the `Key.duration` property value instead.
     *
     * @method Phaser.Input.Keyboard.Key#getDuration
     * @since 3.17.0
     * 
     * @return {number} The duration, in ms, that the Key has been held down for if currently down.
     */
    getDuration: function ()
    {
        if (this.isDown)
        {
            return (this.plugin.game.loop.time - this.timeDown);
        }
        else
        {
            return 0;
        }
    },

    /**
     * Removes any bound event handlers and removes local references.
     *
     * @method Phaser.Input.Keyboard.Key#destroy
     * @since 3.16.0
     */
    destroy: function ()
    {
        this.removeAllListeners();

        this.originalEvent = null;

        this.plugin = null;
    }

});

module.exports = Key;
