using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace ECOT.Engine
{
    public delegate void DisconnectedEventHandler(object sender, EventArgs e);
    public delegate void ConnectedEventHandler(object sender, EventArgs e);

    /// <summary>
    /// Helper for reading input from keyboard and gamepad. This class tracks both
    /// the current and previous state of both input devices, and implements query
    /// properties for high level input actions such as "move up through the menu"
    /// or "pause the game".
    /// </summary>
    public class InputState
    {
        private enum ThumbStick
        {
            Left,
            Right
        }

        public bool InverseControl
        {
            get { return this.inverseControl; }
            set { this.inverseControl = value; }
        }

        public bool InverseControlCamera
        {
            get { return this.inverseControlCamera; }
            set { this.inverseControlCamera = value; }
        }

        private int vibrationDuration = 0;

        #region Membres
        /// <summary>
        /// Current and last keyboard states
        /// </summary>
        private KeyboardState currentKeyboardState;
        private KeyboardState lastKeyboardState;
        /// <summary>
        /// Current and last gamepad state
        /// </summary>
        private GamePadState currentGamePadState;
        private GamePadState lastGamePadState;

#if !XBOX
        private MouseState currentMouseState;
        private MouseState lastMouseState;
#endif

        private PlayerIndex currentPlayerIndex;
        private bool active = true;
        private bool[] activeControllers = new bool[4];
        private double lastInput;

        public event DisconnectedEventHandler OnDisconnection;
        public event ConnectedEventHandler OnConnection;

        private bool mousePressed = false;
        private bool inverseControl = false;
        private bool inverseControlCamera = false;

        /// <summary>
        /// Virtual mouse for Xbox Controller with position and speed
        /// </summary>
        private Vector2 position;
        private int mouseSpeed = 25;

        #endregion

        private Vector2 internalPosition;

        #region Propriétés

        /// <summary>
        /// Properties of inputs
        /// </summary>
        #region Input Properties
#if DEBUG
        public bool Debug
        {
            get
            {
                return IsNewKeyPress(Keys.F1);
            }
        }
#endif

        public PlayerIndex CurrentPlayerIndex
        {
            get { return this.currentPlayerIndex; }
            set { this.currentPlayerIndex = value; }
        }

        public bool IsActivated(int index)
        {
            return this.activeControllers[index];
        }

        public bool MouseFirstPressed
        {
            get
            {
                return (
#if !XBOX
                    this.lastMouseState.LeftButton == ButtonState.Released &&
                    this.currentMouseState.LeftButton == ButtonState.Pressed && this.propagateClick);
#else
                    this.currentGamePadState.IsButtonDown(Buttons.A) &&
                    this.lastGamePadState.IsButtonUp(Buttons.A) && this.propagateClick);
#endif
            }
        }

        public bool MouseFirstReleased
        {
            get
            {
                return (
#if !XBOX
                    this.lastMouseState.LeftButton == ButtonState.Pressed &&
                    this.currentMouseState.LeftButton == ButtonState.Released);
#else
                    this.currentGamePadState.IsButtonUp(Buttons.A) &&
                    this.lastGamePadState.IsButtonDown(Buttons.A));
#endif
            }
        }

        public bool MousePressed
        {
            get
            {
#if !XBOX
                return (this.currentMouseState.LeftButton == ButtonState.Pressed || IsNewKeyPress(Keys.Q));
#else
                return this.currentGamePadState.IsButtonDown(Buttons.A);
#endif
            }
        }

        public bool MousePressed2
        {
            get { return this.mousePressed; }
            set { this.mousePressed = value; }
        }

        public bool MouseReleased
        {
            get
            {
#if !XBOX
                return this.currentMouseState.LeftButton == ButtonState.Released;
#else
                return this.currentGamePadState.IsButtonUp(Buttons.A);
#endif
            }
        }

        public int MouseSpeed
        {
            get { return this.mouseSpeed; }
            set { this.mouseSpeed = value; }
        }

        public bool MouseFirstRPressed
        {
            get
            {
                return (
#if !XBOX
                    this.lastMouseState.RightButton == ButtonState.Released &&
                    this.currentMouseState.RightButton == ButtonState.Pressed);
#else
                    this.currentGamePadState.IsButtonDown(Buttons.B) &&
                    this.lastGamePadState.IsButtonUp(Buttons.B));
#endif
            }
        }

        public bool MouseFirstRReleased
        {
            get
            {
                return (
#if !XBOX
                    this.lastMouseState.RightButton == ButtonState.Pressed &&
                    this.currentMouseState.RightButton == ButtonState.Released);
#else
                    this.currentGamePadState.IsButtonUp(Buttons.B) &&
                    this.lastGamePadState.IsButtonDown(Buttons.B));
#endif
            }
        }

        public int X
        {
            get { return (int)this.position.X; }
            set { this.position.X = value;}
            //            get
            //            {
            //#if !XBOX
            //                return this.currentMouseState.X;
            //#else
            //                // return (int)this.currentGamePadState.ThumbSticks.Left.X;
            //                return (int)this.position.X;
            //#endif
            //            }
        }

        public int Y
        {
            get { return (int)this.position.Y; }
            set { this.position.Y = value; }

            //            get
            //            {
            //#if !XBOX
            //                return this.currentMouseState.Y;
            //#else
            //                // return (int)this.currentGamePadState.ThumbSticks.Right.X;
            //                return (int)this.position.Y;
            //#endif
            //}
        }

        public Vector2 InternalPosition
        {
            get { return this.internalPosition; }
            set { this.internalPosition = value; }
        }

        public Vector2 Position
        {
            get { return this.position; }
            set { this.position = value; }

            //            get
            //            {
            //#if !XBOX
            //                return new Vector2(this.currentMouseState.X, this.currentMouseState.Y);
            //#else
            //                // return new Vector2(this.currentGamePadState.ThumbSticks.Left.X, this.currentGamePadState.ThumbSticks.Right.X);
            //                return this.position;
            //#endif
            //            }
        }

        public Point MousePosition
        {
            get { return new Point((int)this.position.X, (int)this.position.Y); }
            set { this.position = new Vector2(value.X, value.Y); }

            //            get
            //            {
            //#if !XBOX
            //                return new Point(this.currentMouseState.X, this.currentMouseState.Y);
            //#else
            //                //return new Point((int)this.currentGamePadState.ThumbSticks.Left.X, (int)this.currentGamePadState.ThumbSticks.Right.X);
            //                return new Point((int)this.position.X, (int)this.position.Y);
            //#endif
            //            }
        }

        public Vector2 ThumbStickLeft
        {
            get { return currentGamePadState.ThumbSticks.Left; }
        }

        public Vector2 ThumbStickRight
        {
            get { return currentGamePadState.ThumbSticks.Right; }
        }

        public Vector2 LastThumbStickRight
        {
            get { return lastGamePadState.ThumbSticks.Right; }
        }

        public float LeftTrigger
        {
            get { return currentGamePadState.Triggers.Left; }
        }

        public float RightTrigger
        {
            get { return currentGamePadState.Triggers.Right; }
        }

        public float MoveSpeed
        {
            get
            {
#if XBOX
                return 70.0f;
#endif
                return 100.0f;
            }

        }

        #endregion

        #region Game Actions
        /// <summary>
        /// Checks for a "menu up" input action (on either keyboard or gamepad).
        /// </summary>
        public bool MenuUp
        {
            get
            {
                return IsNewKeyPress(Keys.Up, Buttons.DPadUp);

            }
        }
        /// <summary>
        /// Checks for a "menu left" input action (on either keyboard or gamepad).
        /// </summary>
        public bool MenuLeft
        {
            get
            {
                return IsNewKeyPress(Keys.Left, Buttons.DPadLeft);

            }
        }
        /// <summary>
        /// Checks for a "menu right" input action (on either keyboard or gamepad).
        /// </summary>
        public bool MenuRight
        {
            get
            {
                return IsNewKeyPress(Keys.Right, Buttons.DPadRight);

            }
        }
        /// <summary>
        /// Checks for a "menu down" input action (on either keyboard or gamepad).
        /// </summary>
        public bool MenuDown
        {
            get
            {
                return IsNewKeyPress(Keys.Down, Buttons.DPadDown);
            }
        }
        /// <summary>
        /// Checks for a "menu select" input action (on either keyboard or gamepad).
        /// </summary>
        public bool MenuSelect
        {
            get
            {
                return IsNewKeyPress(Keys.Space, Buttons.A) ||
                       IsNewKeyPress(Keys.Enter, Buttons.Start);
            }
        }
        /// <summary>
        /// Checks for a "menu cancel" input action (on either keyboard or gamepad).
        /// </summary>
        public bool MenuCancel
        {
            get
            {
                return IsNewKeyPress(Keys.Escape, Buttons.B) ||
                       IsNewKeyPress(Keys.Back, Buttons.Back);
            }
        }
        /// <summary>
        /// Checks for a "pause the game" input action (on either keyboard or gamepad).
        /// </summary>
        public bool PauseGame
        {
            get
            {
                return IsNewKeyPress(Keys.Escape, Buttons.B) ||
                       IsNewKeyPress(Keys.Back, Buttons.Start);
            }
        }
        /// <summary>
        /// Checks for a "center view on the closiest city" input action
        /// </summary>
//        public bool ViewClosiestCity
//        {
//            get
//            {
//#if XBOX
//                if (!this.viewCityEnabled)
//                    return false;
//                else
//                // TO CHANGE
//                    return true;
//#endif
//                return IsNewKeyPress(Keys.A);
//            }
//        }
        /// <summary>
        /// Checks for a "center view on the next city" input action
        /// </summary>
        public bool ViewNextCity
        {
            get
            {
#if XBOX
                if (!this.viewCityEnabled)
                    return false;
                else
                    return IsButtonFirstPress(Buttons.RightShoulder);
#endif
                return IsNewKeyPress(Keys.G);
            }
        }
        /// <summary>
        /// Checks for a "center view on the previous city" input action
        /// </summary>
        public bool ViewPreviousCity
        {
            get
            {
#if XBOX
                if (!this.viewCityEnabled)
                    return false;
                else
                    return IsButtonFirstPress(Buttons.LeftShoulder);
#endif
                return IsNewKeyPress(Keys.H);
            }
        }
        /// <summary>
        /// Checks for a "change the axis of the cycle of cities" input action
        /// </summary>
        public bool ChangeSortAxis
        {
            get
            {
#if XBOX
                return (IsButtonFirstPress(Buttons.LeftShoulder) && IsButtonFirstPress(Buttons.RightShoulder));
#endif
                return IsNewKeyPress(Keys.N);
            }
        }
        /// <summary>
        /// Check for a "zoom in" input action
        /// </summary>
        public float ZoomIn
        {
            get
            {
#if XBOX
                return currentGamePadState.Triggers.Right;
#else
                return (this.currentKeyboardState.IsKeyDown(Keys.PageUp) ? 1.0f : 0.0f);
#endif
            }
        }
        /// <summary>
        /// Checks for a "zoom out" input action
        /// </summary>
        public float ZoomOut
        {
            get
            {
#if XBOX
                return currentGamePadState.Triggers.Left;
#else
                return (this.currentKeyboardState.IsKeyDown(Keys.PageDown) ? 1.0f : 0.0f);
#endif
            }
        }
        /// <summary>
        /// Checks for a "Center cursor" input action
        /// </summary>
        public bool CenterCursor
        {
#if XBOX
            get { return IsButtonFirstPress(Buttons.LeftStick); }
#else
            get { return false;}
#endif
        }
        /// <summary>
        /// Checks for a "zoom by default" input action
        /// </summary>
        public bool ZoomDefault
        {
#if XBOX
            get { return IsButtonFirstPress(Buttons.RightStick); }
#else
            get { return false; }
#endif
        }

        #endregion

        #region ControlPanel
        /// <summary>
        /// 
        /// </summary>
        public bool RightControl
        {
            get { return IsButtonFirstPress(Buttons.DPadRight); }
        }
        /// <summary>
        /// 
        /// </summary>
        public bool LeftControl
        {
            get { return IsButtonFirstPress(Buttons.DPadLeft); }
        }
        /// <summary>
        /// 
        /// </summary>
        public bool UpControl
        {
            get { return IsButtonFirstPress(Buttons.DPadUp); }
        }
        /// <summary>
        /// 
        /// </summary>
        public bool BottomControl
        {
            get { return IsButtonFirstPress(Buttons.DPadDown); }
        }
        ///// <summary>
        ///// 
        ///// </summary>
        //public bool UpperControl
        //{
        //    get { return IsButtonFirstPress(Buttons.DPadUp); }
        //}
        ///// <summary>
        ///// 
        ///// </summary>
        //public bool BottomControl
        //{
        //    get { return IsButtonFirstPress(Buttons.DPadDown); }
        //}

        #endregion

        #endregion

        public bool InterfaceValidate
        {
            get
            {
#if !XBOX
                return this.MenuSelect && this.propagateEvent;
#else
                return this.IsButtonFirstPress(Buttons.X) && this.propagateEvent;
#endif
            }
        }

        public bool PropagateEvent
        {
            get { return this.propagateEvent; }
            set { this.propagateEvent = value; }
        }

        public bool PropagateClick
        {
            get { return propagateClick; }
            set { propagateClick = value; }
        }

        public bool ViewCityEnabled
        {
            get { return this.viewCityEnabled; }
            set { this.viewCityEnabled = value; }
        }

        #region Methods

        /// <summary>
        /// Reads the latest state of the keyboard and gamepad.
        /// </summary>
        public void Update()
        {
            this.propagateEvent = true;
            this.propagateClick = true;

            if (this.active)
                CheckForDisconnection();
            else
                CheckForConnection();

            if (this.vibrationDuration > 0)
            {
                this.vibrationDuration -= Kernel.Instance.Time.ElapsedGameTime.Milliseconds;
                if (this.vibrationDuration <= 0)
                {
                    GamePad.SetVibration(this.currentPlayerIndex, 0.0f, 0.0f);
                    this.vibrationDuration = 0;
                }
            }

            lastKeyboardState = currentKeyboardState;
            lastGamePadState = currentGamePadState;

            currentKeyboardState = Keyboard.GetState();
            currentGamePadState = GamePad.GetState(this.currentPlayerIndex);

#if !XBOX
            lastMouseState = currentMouseState;
            if (this.InverseControl == true)
            {
                int x = Mouse.GetState().X;
                int y = Mouse.GetState().Y;
                
                int xDiff = (x - lastMouseState.X)*2;
                int yDiff = (y - lastMouseState.Y)*2;
                if (x - xDiff < 0)
                {
                    xDiff = 0;
                }
                else if (x - xDiff > 1280)
                {
                    xDiff = 0;
                }
                if (y - yDiff < 0)
                {
                    yDiff = 0;
                }
                else if (y - yDiff > 720)
                {
                    yDiff = 0;
                }
                Mouse.SetPosition(x - (xDiff), y - (yDiff));

            }
            currentMouseState = Mouse.GetState();
            
            // update position vector with mouse coordinates
            this.position = new Vector2(currentMouseState.X, currentMouseState.Y);

#else
            this.internalPosition += this.mouseSpeed * new Vector2(this.currentGamePadState.ThumbSticks.Left.X, -this.currentGamePadState.ThumbSticks.Left.Y);
            this.position = internalPosition;

            if (this.position.X < 0) this.position.X = 0;
            else if (this.position.X > Kernel.Instance.Width) this.position.X = Kernel.Instance.Width;
            
            if (this.position.Y < 0) this.position.Y = 0;
            else if (this.position.Y > Kernel.Instance.Height) this.position.Y = Kernel.Instance.Height;

            /*if (this.currentGamePadState.IsConnected)
            {
                double timeElapsed = Kernel.Instance.Time.TotalGameTime.TotalMilliseconds - lastInput;
                int speed = mouseSpeed;

                if (timeElapsed > 200)
                {
                    Vector2 cursor_size = new Vector2(64 * Kernel.Instance.Zoom, 64 * Kernel.Instance.Zoom);

                    if (this.currentGamePadState.ThumbSticks.Left.Y > 0.2)
                        this.position.Y -= cursor_size.Y;
                    else if (this.currentGamePadState.ThumbSticks.Left.Y < -0.2)
                        this.position.Y += cursor_size.Y;
                    else if (this.currentGamePadState.ThumbSticks.Left.X < -0.2)
                        this.position.X -= cursor_size.X;
                    else if (this.currentGamePadState.ThumbSticks.Left.X > 0.2)
                        this.position.X += cursor_size.X;

                    if (this.position.X < 0) this.position.X = 0;
                    else if (this.position.X > Kernel.Instance.Width) this.position.X = Kernel.Instance.Width;
                    if (this.position.Y < 0) this.position.Y = 0;
                    else if (this.position.Y > Kernel.Instance.Height) this.position.Y = Kernel.Instance.Height;

                    lastInput = Kernel.Instance.Time.TotalGameTime.TotalMilliseconds;
                }
            }*/
#endif
        }

        /// <summary>
        /// Fait vibrer la manette
        /// </summary>
        /// <param name="lMotor">Intensité du moteur gauche (0 -> 1)</param>
        /// <param name="rMotor">Intensité du moteur droit (0 -> 1)</param>
        public void VibrateGamepad(float lMotor, float rMotor, int duration)
        {
            GamePad.SetVibration(this.currentPlayerIndex, lMotor, rMotor);
            this.vibrationDuration = duration;
        }

        public void CheckForDisconnection()
        {
            if (!GamePad.GetState(this.currentPlayerIndex).IsConnected)
            {
                this.active = false;

                if (this.OnDisconnection != null)
                    this.OnDisconnection(this, null);
            }
        }

        public void CheckForConnection()
        {
            if (GamePad.GetState(this.currentPlayerIndex).IsConnected)
            {
                this.active = true;

                if (this.OnConnection != null)
                    this.OnConnection(this, null);
            }
        }

        public void InitControllers()
        {
            PlayerIndex index = PlayerIndex.One;
            for (int i = 0; i < 4; i++, index++)
            {
                if (GamePad.GetState(index).IsConnected)
                    activeControllers[i] = true;
            }
        }

        public bool IsGamepadConnected(PlayerIndex? playerIndex)
        {
            if (playerIndex != null)
                return GamePad.GetState((PlayerIndex)playerIndex).IsConnected;
            else
            {
                int index = 0;
                for (int i = 0; i < 4; i++, index++)
                {
                    if (GamePad.GetState((PlayerIndex)index).IsConnected)
                        return true;
                }
                return false;
            }
            
        }

        public void InitMouse(Vector2? pos)
        {
            if (pos != null)
                this.position = (Vector2)pos;
            else
                this.position = new Vector2((Kernel.Instance.Width / 2) * 64, (Kernel.Instance.Height / 2) * 64);
        }

        /// <summary>
        /// Helper for checking if a key was newly pressed during this update.
        /// </summary>
        public bool IsNewKeyPress(Keys key)
        {
            return (currentKeyboardState.IsKeyDown(key) &&
                    lastKeyboardState.IsKeyUp(key));
        }



        public bool IsNewKeyPress(Keys key, Buttons button)
        {
            return (this.IsNewKeyPress(key) ||
                    (currentGamePadState.IsButtonDown(button) &&
                    lastGamePadState.IsButtonUp(button)));
        }

        public bool IsButtonFirstPress(Buttons button)
        {
            return (currentGamePadState.IsButtonDown(button) && lastGamePadState.IsButtonUp(button));
        }

        public bool IsKeyDown(Keys key)
        {
            return currentKeyboardState.IsKeyDown(key);
        }

        public bool IsKeyDown(Keys key, Buttons button)
        {
            return currentKeyboardState.IsKeyDown(key) || currentGamePadState.IsButtonDown(button);
        }

        public bool IsKeyUp(Keys key)
        {
            return currentKeyboardState.IsKeyUp(key);
        }

        public bool HasChanged(Keys key)
        {
            return currentKeyboardState.IsKeyDown(key) != lastKeyboardState.IsKeyDown(key);
        }

        #endregion

        private bool propagateEvent = true;
        private bool propagateClick = true;
        private bool viewCityEnabled = true;
    }
}
