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

var Angle = require('../math/angle/Between');
var Class = require('../utils/Class');
var Distance = require('../math/distance/DistanceBetween');
var FuzzyEqual = require('../math/fuzzy/Equal');
var SmoothStepInterpolation = require('../math/interpolation/SmoothStepInterpolation');
var Vector2 = require('../math/Vector2');

/**
 * @classdesc
 * A Pointer object encapsulates both mouse and touch input within Phaser.
 *
 * By default, Phaser will create 2 pointers for your game to use. If you require more, i.e. for a multi-touch
 * game, then use the `InputPlugin.addPointer` method to do so, rather than instantiating this class directly,
 * otherwise it won't be managed by the input system.
 *
 * You can reference the current active pointer via `InputPlugin.activePointer`. You can also use the properties
 * `InputPlugin.pointer1` through to `pointer10`, for each pointer you have enabled in your game.
 *
 * The properties of this object are set by the Input Plugin during processing. This object is then sent in all
 * input related events that the Input Plugin emits, so you can reference properties from it directly in your
 * callbacks.
 *
 * @class Pointer
 * @memberof Phaser.Input
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Input.InputManager} manager - A reference to the Input Manager.
 * @param {integer} id - The internal ID of this Pointer.
 */
var Pointer = new Class({

    initialize:

    function Pointer (manager, id)
    {
        /**
         * A reference to the Input Manager.
         *
         * @name Phaser.Input.Pointer#manager
         * @type {Phaser.Input.InputManager}
         * @since 3.0.0
         */
        this.manager = manager;

        /**
         * The internal ID of this Pointer.
         *
         * @name Phaser.Input.Pointer#id
         * @type {integer}
         * @readonly
         * @since 3.0.0
         */
        this.id = id;

        /**
         * The most recent native DOM Event this Pointer has processed.
         *
         * @name Phaser.Input.Pointer#event
         * @type {(TouchEvent|MouseEvent)}
         * @since 3.0.0
         */
        this.event;

        /**
         * The DOM element the Pointer was pressed down on, taken from the DOM event.
         * In a default set-up this will be the Canvas that Phaser is rendering to, or the Window element.
         *
         * @name Phaser.Input.Pointer#downElement
         * @type {any}
         * @readonly
         * @since 3.16.0
         */
        this.downElement;

        /**
         * The DOM element the Pointer was released on, taken from the DOM event.
         * In a default set-up this will be the Canvas that Phaser is rendering to, or the Window element.
         *
         * @name Phaser.Input.Pointer#upElement
         * @type {any}
         * @readonly
         * @since 3.16.0
         */
        this.upElement;

        /**
         * The camera the Pointer interacted with during its last update.
         *
         * A Pointer can only ever interact with one camera at once, which will be the top-most camera
         * in the list should multiple cameras be positioned on-top of each other.
         *
         * @name Phaser.Input.Pointer#camera
         * @type {Phaser.Cameras.Scene2D.Camera}
         * @default null
         * @since 3.0.0
         */
        this.camera = null;

        /**
         * A read-only property that indicates which button was pressed, or released, on the pointer
         * during the most recent event. It is only set during `up` and `down` events.
         *
         * On Touch devices the value is always 0.
         *
         * Users may change the configuration of buttons on their pointing device so that if an event's button property
         * is zero, it may not have been caused by the button that is physically left–most on the pointing device;
         * however, it should behave as if the left button was clicked in the standard button layout.
         *
         * @name Phaser.Input.Pointer#button
         * @type {integer}
         * @readonly
         * @default 0
         * @since 3.18.0
         */
        this.button = 0;

        /**
         * 0: No button or un-initialized
         * 1: Left button
         * 2: Right button
         * 4: Wheel button or middle button
         * 8: 4th button (typically the "Browser Back" button)
         * 16: 5th button (typically the "Browser Forward" button)
         *
         * For a mouse configured for left-handed use, the button actions are reversed.
         * In this case, the values are read from right to left.
         *
         * @name Phaser.Input.Pointer#buttons
         * @type {integer}
         * @default 0
         * @since 3.0.0
         */
        this.buttons = 0;

        /**
         * The position of the Pointer in screen space.
         *
         * @name Phaser.Input.Pointer#position
         * @type {Phaser.Math.Vector2}
         * @readonly
         * @since 3.0.0
         */
        this.position = new Vector2();

        /**
         * The previous position of the Pointer in screen space.
         *
         * The old x and y values are stored in here during the InputManager.transformPointer call.
         *
         * Use the properties `velocity`, `angle` and `distance` to create your own gesture recognition.
         *
         * @name Phaser.Input.Pointer#prevPosition
         * @type {Phaser.Math.Vector2}
         * @readonly
         * @since 3.11.0
         */
        this.prevPosition = new Vector2();

        /**
         * An internal vector used for calculations of the pointer speed and angle.
         *
         * @name Phaser.Input.Pointer#midPoint
         * @type {Phaser.Math.Vector2}
         * @private
         * @since 3.16.0
         */
        this.midPoint = new Vector2(-1, -1);

        /**
         * The current velocity of the Pointer, based on its current and previous positions.
         *
         * This value is smoothed out each frame, according to the `motionFactor` property.
         *
         * This property is updated whenever the Pointer moves, regardless of any button states. In other words,
         * it changes based on movement alone - a button doesn't have to be pressed first.
         *
         * @name Phaser.Input.Pointer#velocity
         * @type {Phaser.Math.Vector2}
         * @readonly
         * @since 3.16.0
         */
        this.velocity = new Vector2();

        /**
         * The current angle the Pointer is moving, in radians, based on its previous and current position.
         *
         * The angle is based on the old position facing to the current position.
         *
         * This property is updated whenever the Pointer moves, regardless of any button states. In other words,
         * it changes based on movement alone - a button doesn't have to be pressed first.
         *
         * @name Phaser.Input.Pointer#angle
         * @type {number}
         * @readonly
         * @since 3.16.0
         */
        this.angle = 0;

        /**
         * The distance the Pointer has moved, based on its previous and current position.
         *
         * This value is smoothed out each frame, according to the `motionFactor` property.
         *
         * This property is updated whenever the Pointer moves, regardless of any button states. In other words,
         * it changes based on movement alone - a button doesn't have to be pressed first.
         *
         * If you need the total distance travelled since the primary buttons was pressed down,
         * then use the `Pointer.getDistance` method.
         *
         * @name Phaser.Input.Pointer#distance
         * @type {number}
         * @readonly
         * @since 3.16.0
         */
        this.distance = 0;

        /**
         * The smoothing factor to apply to the Pointer position.
         *
         * Due to their nature, pointer positions are inherently noisy. While this is fine for lots of games, if you need cleaner positions
         * then you can set this value to apply an automatic smoothing to the positions as they are recorded.
         *
         * The default value of zero means 'no smoothing'.
         * Set to a small value, such as 0.2, to apply an average level of smoothing between positions. You can do this by changing this
         * value directly, or by setting the `input.smoothFactor` property in the Game Config.
         *
         * Positions are only smoothed when the pointer moves. If the primary button on this Pointer enters an Up or Down state, then the position
         * is always precise, and not smoothed.
         *
         * @name Phaser.Input.Pointer#smoothFactor
         * @type {number}
         * @default 0
         * @since 3.16.0
         */
        this.smoothFactor = 0;

        /**
         * The factor applied to the motion smoothing each frame.
         *
         * This value is passed to the Smooth Step Interpolation that is used to calculate the velocity,
         * angle and distance of the Pointer. It's applied every frame, until the midPoint reaches the current
         * position of the Pointer. 0.2 provides a good average but can be increased if you need a
         * quicker update and are working in a high performance environment. Never set this value to
         * zero.
         *
         * @name Phaser.Input.Pointer#motionFactor
         * @type {number}
         * @default 0.2
         * @since 3.16.0
         */
        this.motionFactor = 0.2;

        /**
         * The x position of this Pointer, translated into the coordinate space of the most recent Camera it interacted with.
         *
         * If you wish to use this value _outside_ of an input event handler then you should update it first by calling
         * the `Pointer.updateWorldPoint` method.
         *
         * @name Phaser.Input.Pointer#worldX
         * @type {number}
         * @default 0
         * @since 3.10.0
         */
        this.worldX = 0;

        /**
         * The y position of this Pointer, translated into the coordinate space of the most recent Camera it interacted with.
         *
         * If you wish to use this value _outside_ of an input event handler then you should update it first by calling
         * the `Pointer.updateWorldPoint` method.
         *
         * @name Phaser.Input.Pointer#worldY
         * @type {number}
         * @default 0
         * @since 3.10.0
         */
        this.worldY = 0;

        /**
         * Time when this Pointer was most recently moved (regardless of the state of its buttons, if any)
         *
         * @name Phaser.Input.Pointer#moveTime
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.moveTime = 0;

        /**
         * X coordinate of the Pointer when Button 1 (left button), or Touch, was pressed, used for dragging objects.
         *
         * @name Phaser.Input.Pointer#downX
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.downX = 0;

        /**
         * Y coordinate of the Pointer when Button 1 (left button), or Touch, was pressed, used for dragging objects.
         *
         * @name Phaser.Input.Pointer#downY
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.downY = 0;

        /**
         * The Event timestamp when the first button, or Touch input, was pressed. Used for dragging objects.
         *
         * @name Phaser.Input.Pointer#downTime
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.downTime = 0;

        /**
         * X coordinate of the Pointer when Button 1 (left button), or Touch, was released, used for dragging objects.
         *
         * @name Phaser.Input.Pointer#upX
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.upX = 0;

        /**
         * Y coordinate of the Pointer when Button 1 (left button), or Touch, was released, used for dragging objects.
         *
         * @name Phaser.Input.Pointer#upY
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.upY = 0;

        /**
         * The Event timestamp when the final button, or Touch input, was released. Used for dragging objects.
         *
         * @name Phaser.Input.Pointer#upTime
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.upTime = 0;

        /**
         * Is the primary button down? (usually button 0, the left mouse button)
         *
         * @name Phaser.Input.Pointer#primaryDown
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.primaryDown = false;

        /**
         * Is _any_ button on this pointer considered as being down?
         *
         * @name Phaser.Input.Pointer#isDown
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.isDown = false;

        /**
         * Did the previous input event come from a Touch input (true) or Mouse? (false)
         *
         * @name Phaser.Input.Pointer#wasTouch
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.wasTouch = false;

        /**
         * Did this Pointer get canceled by a touchcancel event?
         *
         * Note: "canceled" is the American-English spelling of "cancelled". Please don't submit PRs correcting it!
         *
         * @name Phaser.Input.Pointer#wasCanceled
         * @type {boolean}
         * @default false
         * @since 3.15.0
         */
        this.wasCanceled = false;

        /**
         * If the mouse is locked, the horizontal relative movement of the Pointer in pixels since last frame.
         *
         * @name Phaser.Input.Pointer#movementX
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.movementX = 0;

        /**
         * If the mouse is locked, the vertical relative movement of the Pointer in pixels since last frame.
         *
         * @name Phaser.Input.Pointer#movementY
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.movementY = 0;

        /**
         * The identifier property of the Pointer as set by the DOM event when this Pointer is started.
         *
         * @name Phaser.Input.Pointer#identifier
         * @type {number}
         * @since 3.10.0
         */
        this.identifier = 0;

        /**
         * The pointerId property of the Pointer as set by the DOM event when this Pointer is started.
         * The browser can and will recycle this value.
         *
         * @name Phaser.Input.Pointer#pointerId
         * @type {number}
         * @since 3.10.0
         */
        this.pointerId = null;

        /**
         * An active Pointer is one that is currently pressed down on the display.
         * A Mouse is always considered as active.
         *
         * @name Phaser.Input.Pointer#active
         * @type {boolean}
         * @since 3.10.0
         */
        this.active = (id === 0) ? true : false;

        /**
         * Is this pointer Pointer Locked?
         *
         * Only a mouse pointer can be locked and it only becomes locked when requested via
         * the browsers Pointer Lock API.
         *
         * You can request this by calling the `this.input.mouse.requestPointerLock()` method from
         * a `pointerdown` or `pointerup` event handler.
         *
         * @name Phaser.Input.Pointer#locked
         * @readonly
         * @type {boolean}
         * @since 3.19.0
         */
        this.locked = false;

        /**
         * The horizontal scroll amount that occurred due to the user moving a mouse wheel or similar input device.
         *
         * @name Phaser.Input.Pointer#deltaX
         * @type {number}
         * @default 0
         * @since 3.18.0
         */
        this.deltaX = 0;

        /**
         * The vertical scroll amount that occurred due to the user moving a mouse wheel or similar input device.
         * This value will typically be less than 0 if the user scrolls up and greater than zero if scrolling down.
         *
         * @name Phaser.Input.Pointer#deltaY
         * @type {number}
         * @default 0
         * @since 3.18.0
         */
        this.deltaY = 0;

        /**
         * The z-axis scroll amount that occurred due to the user moving a mouse wheel or similar input device.
         *
         * @name Phaser.Input.Pointer#deltaZ
         * @type {number}
         * @default 0
         * @since 3.18.0
         */
        this.deltaZ = 0;
    },

    /**
     * Takes a Camera and updates this Pointer's `worldX` and `worldY` values so they are
     * the result of a translation through the given Camera.
     *
     * Note that the values will be automatically replaced the moment the Pointer is
     * updated by an input event, such as a mouse move, so should be used immediately.
     *
     * @method Phaser.Input.Pointer#updateWorldPoint
     * @since 3.19.0
     *
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera which is being tested against.
     *
     * @return {this} This Pointer object.
     */
    updateWorldPoint: function (camera)
    {
        //  Stores the world point inside of tempPoint
        var temp = camera.getWorldPoint(this.x, this.y);

        this.worldX = temp.x;
        this.worldY = temp.y;

        return this;
    },

    /**
     * Takes a Camera and returns a Vector2 containing the translated position of this Pointer
     * within that Camera. This can be used to convert this Pointers position into camera space.
     *
     * @method Phaser.Input.Pointer#positionToCamera
     * @since 3.0.0
     *
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to use for the translation.
     * @param {(Phaser.Math.Vector2|object)} [output] - A Vector2-like object in which to store the translated position.
     *
     * @return {(Phaser.Math.Vector2|object)} A Vector2 containing the translated coordinates of this Pointer, based on the given camera.
     */
    positionToCamera: function (camera, output)
    {
        return camera.getWorldPoint(this.x, this.y, output);
    },

    /**
     * Calculates the motion of this Pointer, including its velocity and angle of movement.
     * This method is called automatically each frame by the Input Manager.
     *
     * @method Phaser.Input.Pointer#updateMotion
     * @private
     * @since 3.16.0
     */
    updateMotion: function ()
    {
        var cx = this.position.x;
        var cy = this.position.y;

        var mx = this.midPoint.x;
        var my = this.midPoint.y;

        if (cx === mx && cy === my)
        {
            //  Nothing to do here
            return;
        }

        //  Moving towards our goal ...
        var vx = SmoothStepInterpolation(this.motionFactor, mx, cx);
        var vy = SmoothStepInterpolation(this.motionFactor, my, cy);

        if (FuzzyEqual(vx, cx, 0.1))
        {
            vx = cx;
        }

        if (FuzzyEqual(vy, cy, 0.1))
        {
            vy = cy;
        }

        this.midPoint.set(vx, vy);

        var dx = cx - vx;
        var dy = cy - vy;

        this.velocity.set(dx, dy);

        this.angle = Angle(vx, vy, cx, cy);

        this.distance = Math.sqrt(dx * dx + dy * dy);
    },

    /**
     * Internal method to handle a Mouse Up Event.
     *
     * @method Phaser.Input.Pointer#up
     * @private
     * @since 3.0.0
     *
     * @param {MouseEvent} event - The Mouse Event to process.
     */
    up: function (event)
    {
        if ('buttons' in event)
        {
            this.buttons = event.buttons;
        }

        this.event = event;

        this.button = event.button;

        this.upElement = event.target;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, event.pageX, event.pageY, false);

        //  0: Main button pressed, usually the left button or the un-initialized state
        if (event.button === 0)
        {
            this.primaryDown = false;
            this.upX = this.x;
            this.upY = this.y;
        }

        if (this.buttons === 0)
        {
            //  No more buttons are still down
            this.isDown = false;

            this.upTime = event.timeStamp;

            this.wasTouch = false;
        }
    },

    /**
     * Internal method to handle a Mouse Down Event.
     *
     * @method Phaser.Input.Pointer#down
     * @private
     * @since 3.0.0
     *
     * @param {MouseEvent} event - The Mouse Event to process.
     */
    down: function (event)
    {
        if ('buttons' in event)
        {
            this.buttons = event.buttons;
        }

        this.event = event;

        this.button = event.button;

        this.downElement = event.target;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, event.pageX, event.pageY, false);

        //  0: Main button pressed, usually the left button or the un-initialized state
        if (event.button === 0)
        {
            this.primaryDown = true;
            this.downX = this.x;
            this.downY = this.y;
        }

        if (!this.isDown)
        {
            this.isDown = true;

            this.downTime = event.timeStamp;
        }

        this.wasTouch = false;
    },

    /**
     * Internal method to handle a Mouse Move Event.
     *
     * @method Phaser.Input.Pointer#move
     * @private
     * @since 3.0.0
     *
     * @param {MouseEvent} event - The Mouse Event to process.
     */
    move: function (event)
    {
        if ('buttons' in event)
        {
            this.buttons = event.buttons;
        }

        this.event = event;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, event.pageX, event.pageY, true);

        if (this.locked)
        {
            //  Multiple DOM events may occur within one frame, but only one Phaser event will fire
            this.movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
            this.movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
        }

        this.moveTime = event.timeStamp;

        this.wasTouch = false;
    },

    /**
     * Internal method to handle a Mouse Wheel Event.
     *
     * @method Phaser.Input.Pointer#wheel
     * @private
     * @since 3.18.0
     *
     * @param {WheelEvent} event - The Wheel Event to process.
     */
    wheel: function (event)
    {
        if ('buttons' in event)
        {
            this.buttons = event.buttons;
        }

        this.event = event;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, event.pageX, event.pageY, false);

        this.deltaX = event.deltaX;
        this.deltaY = event.deltaY;
        this.deltaZ = event.deltaZ;

        this.wasTouch = false;
    },

    /**
     * Internal method to handle a Touch Start Event.
     *
     * @method Phaser.Input.Pointer#touchstart
     * @private
     * @since 3.0.0
     *
     * @param {Touch} touch - The Changed Touch from the Touch Event.
     * @param {TouchEvent} event - The full Touch Event.
     */
    touchstart: function (touch, event)
    {
        if (touch['pointerId'])
        {
            this.pointerId = touch.pointerId;
        }

        this.identifier = touch.identifier;
        this.target = touch.target;
        this.active = true;

        this.buttons = 1;

        this.event = event;

        this.downElement = touch.target;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, touch.pageX, touch.pageY, false);

        this.primaryDown = true;
        this.downX = this.x;
        this.downY = this.y;
        this.downTime = event.timeStamp;

        this.isDown = true;

        this.wasTouch = true;
        this.wasCanceled = false;

        this.updateMotion();
    },

    /**
     * Internal method to handle a Touch Move Event.
     *
     * @method Phaser.Input.Pointer#touchmove
     * @private
     * @since 3.0.0
     *
     * @param {Touch} touch - The Changed Touch from the Touch Event.
     * @param {TouchEvent} event - The full Touch Event.
     */
    touchmove: function (touch, event)
    {
        this.event = event;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, touch.pageX, touch.pageY, true);

        this.moveTime = event.timeStamp;

        this.wasTouch = true;

        this.updateMotion();
    },

    /**
     * Internal method to handle a Touch End Event.
     *
     * @method Phaser.Input.Pointer#touchend
     * @private
     * @since 3.0.0
     *
     * @param {Touch} touch - The Changed Touch from the Touch Event.
     * @param {TouchEvent} event - The full Touch Event.
     */
    touchend: function (touch, event)
    {
        this.buttons = 0;

        this.event = event;

        this.upElement = touch.target;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, touch.pageX, touch.pageY, false);

        this.primaryDown = false;
        this.upX = this.x;
        this.upY = this.y;
        this.upTime = event.timeStamp;

        this.isDown = false;

        this.wasTouch = true;
        this.wasCanceled = false;

        this.active = false;

        this.updateMotion();
    },

    /**
     * Internal method to handle a Touch Cancel Event.
     *
     * @method Phaser.Input.Pointer#touchcancel
     * @private
     * @since 3.15.0
     *
     * @param {Touch} touch - The Changed Touch from the Touch Event.
     * @param {TouchEvent} event - The full Touch Event.
     */
    touchcancel: function (touch, event)
    {
        this.buttons = 0;

        this.event = event;

        this.upElement = touch.target;

        //  Sets the local x/y properties
        this.manager.transformPointer(this, touch.pageX, touch.pageY, false);

        this.primaryDown = false;
        this.upX = this.x;
        this.upY = this.y;
        this.upTime = event.timeStamp;

        this.isDown = false;

        this.wasTouch = true;
        this.wasCanceled = true;

        this.active = false;
    },

    /**
     * Checks to see if any buttons are being held down on this Pointer.
     *
     * @method Phaser.Input.Pointer#noButtonDown
     * @since 3.0.0
     *
     * @return {boolean} `true` if no buttons are being held down.
     */
    noButtonDown: function ()
    {
        return (this.buttons === 0);
    },

    /**
     * Checks to see if the left button is being held down on this Pointer.
     *
     * @method Phaser.Input.Pointer#leftButtonDown
     * @since 3.0.0
     *
     * @return {boolean} `true` if the left button is being held down.
     */
    leftButtonDown: function ()
    {
        return (this.buttons & 1) ? true : false;
    },

    /**
     * Checks to see if the right button is being held down on this Pointer.
     *
     * @method Phaser.Input.Pointer#rightButtonDown
     * @since 3.0.0
     *
     * @return {boolean} `true` if the right button is being held down.
     */
    rightButtonDown: function ()
    {
        return (this.buttons & 2) ? true : false;
    },

    /**
     * Checks to see if the middle button is being held down on this Pointer.
     *
     * @method Phaser.Input.Pointer#middleButtonDown
     * @since 3.0.0
     *
     * @return {boolean} `true` if the middle button is being held down.
     */
    middleButtonDown: function ()
    {
        return (this.buttons & 4) ? true : false;
    },

    /**
     * Checks to see if the back button is being held down on this Pointer.
     *
     * @method Phaser.Input.Pointer#backButtonDown
     * @since 3.0.0
     *
     * @return {boolean} `true` if the back button is being held down.
     */
    backButtonDown: function ()
    {
        return (this.buttons & 8) ? true : false;
    },

    /**
     * Checks to see if the forward button is being held down on this Pointer.
     *
     * @method Phaser.Input.Pointer#forwardButtonDown
     * @since 3.0.0
     *
     * @return {boolean} `true` if the forward button is being held down.
     */
    forwardButtonDown: function ()
    {
        return (this.buttons & 16) ? true : false;
    },

    /**
     * Checks to see if the left button was just released on this Pointer.
     *
     * @method Phaser.Input.Pointer#leftButtonReleased
     * @since 3.18.0
     *
     * @return {boolean} `true` if the left button was just released.
     */
    leftButtonReleased: function ()
    {
        return (this.button === 0 && !this.isDown);
    },

    /**
     * Checks to see if the right button was just released on this Pointer.
     *
     * @method Phaser.Input.Pointer#rightButtonReleased
     * @since 3.18.0
     *
     * @return {boolean} `true` if the right button was just released.
     */
    rightButtonReleased: function ()
    {
        return (this.button === 2 && !this.isDown);
    },

    /**
     * Checks to see if the middle button was just released on this Pointer.
     *
     * @method Phaser.Input.Pointer#middleButtonReleased
     * @since 3.18.0
     *
     * @return {boolean} `true` if the middle button was just released.
     */
    middleButtonReleased: function ()
    {
        return (this.button === 1 && !this.isDown);
    },

    /**
     * Checks to see if the back button was just released on this Pointer.
     *
     * @method Phaser.Input.Pointer#backButtonReleased
     * @since 3.18.0
     *
     * @return {boolean} `true` if the back button was just released.
     */
    backButtonReleased: function ()
    {
        return (this.button === 3 && !this.isDown);
    },

    /**
     * Checks to see if the forward button was just released on this Pointer.
     *
     * @method Phaser.Input.Pointer#forwardButtonReleased
     * @since 3.18.0
     *
     * @return {boolean} `true` if the forward button was just released.
     */
    forwardButtonReleased: function ()
    {
        return (this.button === 4 && !this.isDown);
    },

    /**
     * If the Pointer has a button pressed down at the time this method is called, it will return the
     * distance between the Pointer's `downX` and `downY` values and the current position.
     *
     * If no button is held down, it will return the last recorded distance, based on where
     * the Pointer was when the button was released.
     *
     * If you wish to get the distance being travelled currently, based on the velocity of the Pointer,
     * then see the `Pointer.distance` property.
     *
     * @method Phaser.Input.Pointer#getDistance
     * @since 3.13.0
     *
     * @return {number} The distance the Pointer moved.
     */
    getDistance: function ()
    {
        if (this.isDown)
        {
            return Distance(this.downX, this.downY, this.x, this.y);
        }
        else
        {
            return Distance(this.downX, this.downY, this.upX, this.upY);
        }
    },

    /**
     * If the Pointer has a button pressed down at the time this method is called, it will return the
     * horizontal distance between the Pointer's `downX` and `downY` values and the current position.
     *
     * If no button is held down, it will return the last recorded horizontal distance, based on where
     * the Pointer was when the button was released.
     *
     * @method Phaser.Input.Pointer#getDistanceX
     * @since 3.16.0
     *
     * @return {number} The horizontal distance the Pointer moved.
     */
    getDistanceX: function ()
    {
        if (this.isDown)
        {
            return Math.abs(this.downX - this.x);
        }
        else
        {
            return Math.abs(this.downX - this.upX);
        }
    },

    /**
     * If the Pointer has a button pressed down at the time this method is called, it will return the
     * vertical distance between the Pointer's `downX` and `downY` values and the current position.
     *
     * If no button is held down, it will return the last recorded vertical distance, based on where
     * the Pointer was when the button was released.
     *
     * @method Phaser.Input.Pointer#getDistanceY
     * @since 3.16.0
     *
     * @return {number} The vertical distance the Pointer moved.
     */
    getDistanceY: function ()
    {
        if (this.isDown)
        {
            return Math.abs(this.downY - this.y);
        }
        else
        {
            return Math.abs(this.downY - this.upY);
        }
    },

    /**
     * If the Pointer has a button pressed down at the time this method is called, it will return the
     * duration since the button was pressed down.
     *
     * If no button is held down, it will return the last recorded duration, based on the time
     * the last button on the Pointer was released.
     *
     * @method Phaser.Input.Pointer#getDuration
     * @since 3.16.0
     *
     * @return {number} The duration the Pointer was held down for in milliseconds.
     */
    getDuration: function ()
    {
        if (this.isDown)
        {
            return (this.manager.time - this.downTime);
        }
        else
        {
            return (this.upTime - this.downTime);
        }
    },

    /**
     * If the Pointer has a button pressed down at the time this method is called, it will return the
     * angle between the Pointer's `downX` and `downY` values and the current position.
     *
     * If no button is held down, it will return the last recorded angle, based on where
     * the Pointer was when the button was released.
     *
     * The angle is based on the old position facing to the current position.
     *
     * If you wish to get the current angle, based on the velocity of the Pointer, then
     * see the `Pointer.angle` property.
     *
     * @method Phaser.Input.Pointer#getAngle
     * @since 3.16.0
     *
     * @return {number} The angle between the Pointer's coordinates in radians.
     */
    getAngle: function ()
    {
        if (this.isDown)
        {
            return Angle(this.downX, this.downY, this.x, this.y);
        }
        else
        {
            return Angle(this.downX, this.downY, this.upX, this.upY);
        }
    },

    /**
     * Takes the previous and current Pointer positions and then generates an array of interpolated values between
     * the two. The array will be populated up to the size of the `steps` argument.
     *
     * ```javaScript
     * var points = pointer.getInterpolatedPosition(4);
     *
     * // points[0] = { x: 0, y: 0 }
     * // points[1] = { x: 2, y: 1 }
     * // points[2] = { x: 3, y: 2 }
     * // points[3] = { x: 6, y: 3 }
     * ```
     *
     * Use this if you need to get smoothed values between the previous and current pointer positions. DOM pointer
     * events can often fire faster than the main browser loop, and this will help you avoid janky movement
     * especially if you have an object following a Pointer.
     *
     * Note that if you provide an output array it will only be populated up to the number of steps provided.
     * It will not clear any previous data that may have existed beyond the range of the steps count.
     *
     * Internally it uses the Smooth Step interpolation calculation.
     *
     * @method Phaser.Input.Pointer#getInterpolatedPosition
     * @since 3.11.0
     *
     * @param {integer} [steps=10] - The number of interpolation steps to use.
     * @param {array} [out] - An array to store the results in. If not provided a new one will be created.
     *
     * @return {array} An array of interpolated values.
     */
    getInterpolatedPosition: function (steps, out)
    {
        if (steps === undefined) { steps = 10; }
        if (out === undefined) { out = []; }

        var prevX = this.prevPosition.x;
        var prevY = this.prevPosition.y;

        var curX = this.position.x;
        var curY = this.position.y;

        for (var i = 0; i < steps; i++)
        {
            var t = (1 / steps) * i;

            out[i] = { x: SmoothStepInterpolation(t, prevX, curX), y: SmoothStepInterpolation(t, prevY, curY) };
        }

        return out;
    },

    /**
     * Destroys this Pointer instance and resets its external references.
     *
     * @method Phaser.Input.Pointer#destroy
     * @since 3.0.0
     */
    destroy: function ()
    {
        this.camera = null;
        this.manager = null;
        this.position = null;
    },

    /**
     * The x position of this Pointer.
     * The value is in screen space.
     * See `worldX` to get a camera converted position.
     *
     * @name Phaser.Input.Pointer#x
     * @type {number}
     * @since 3.0.0
     */
    x: {

        get: function ()
        {
            return this.position.x;
        },

        set: function (value)
        {
            this.position.x = value;
        }

    },

    /**
     * The y position of this Pointer.
     * The value is in screen space.
     * See `worldY` to get a camera converted position.
     *
     * @name Phaser.Input.Pointer#y
     * @type {number}
     * @since 3.0.0
     */
    y: {

        get: function ()
        {
            return this.position.y;
        },

        set: function (value)
        {
            this.position.y = value;
        }

    },

    /**
     * Time when this Pointer was most recently updated by a DOM Event.
     * This comes directly from the `event.timeStamp` property.
     * If no event has yet taken place, it will return zero.
     *
     * @name Phaser.Input.Pointer#time
     * @type {number}
     * @readonly
     * @since 3.16.0
     */
    time: {

        get: function ()
        {
            return (this.event) ? this.event.timeStamp : 0;
        }

    }

});

module.exports = Pointer;
