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

namespace MonoGame.FourEngine.Services
{
    public class BasicPointerControl : IPointerControl
    {
        public Point Position => _mouseState.Position;
        public Point PreviousPosition => _mousePrev.Position;
        public bool IsPrimaryPressed => _mouseState.LeftButton == ButtonState.Pressed;
        public bool WasPrimaryPressed => _mousePrev.LeftButton == ButtonState.Pressed;
        public bool IsSecondaryPressed => _mouseState.RightButton == ButtonState.Pressed;
        public bool WasSecondaryPressed => _mousePrev.RightButton == ButtonState.Pressed;
        public CancelableEventArgs PrimaryClickCancellable { get; }
        public CancelableEventArgs SecondaryClickCancellable { get; }

        public bool IsTouchMode { get; private set; } = true;

        public bool HasPrimaryClicked => !PrimaryClickCancellable.IsCanceled
            && IsPrimaryPressed && !WasPrimaryPressed;

        public bool HasSecondaryClicked => !SecondaryClickCancellable.IsCanceled
            && IsSecondaryPressed && !WasSecondaryPressed;

        private MouseState _mouseState;
        private MouseState _mousePrev;

        private TouchLocation _touchState;
        private TouchLocation _touchPrev;

        private bool _touchAvailable;

        public BasicPointerControl()
        {
            PrimaryClickCancellable = new CancelableEventArgs(this);
            SecondaryClickCancellable = new CancelableEventArgs(this);
            _touchAvailable = TouchPanel.GetCapabilities().IsConnected;
        }

        public void Update(TimeSpan deltaTime)
        {
            UpdateMouse();
            if (UpdateTouch())
                IsTouchMode = true;
            else
            {
                if (_mouseState.LeftButton == ButtonState.Pressed
                    || _mouseState.RightButton == ButtonState.Pressed
                    || _mouseState.MiddleButton == ButtonState.Pressed
                    || _mouseState.XButton1 == ButtonState.Pressed
                    || _mouseState.XButton1 == ButtonState.Pressed
                    || ServiceLocator.KeyboardControl.State.Values.Where(x => x.IsPressed).Any())
                    IsTouchMode = false;
            }
            PrimaryClickCancellable.Reset();
            SecondaryClickCancellable.Reset();
        }

        private void UpdateMouse()
        {
            _mousePrev = _mouseState;
            _mouseState = Mouse.GetState();
        }

        private bool UpdateTouch()
        {
            if (!_touchAvailable)
                return false;
            _touchPrev = _touchState;
            _touchState = TouchPanel.GetState().FirstOrDefault();
            if (_touchState != null && _touchState.State != TouchLocationState.Invalid)
            {
                _mousePrev = EmulateMouse(_touchPrev);
                _mouseState = EmulateMouse(_touchState);
                return true;
            }
            return false;
        }

        private MouseState EmulateMouse(TouchLocation touch)
        {
            if (touch.State != TouchLocationState.Invalid)
            {
                return new MouseState(
                    x: (int)touch.Position.X,
                    y: (int)touch.Position.Y,
                    scrollWheel: 0,
                    leftButton: ButtonState.Pressed,
                    middleButton: ButtonState.Released,
                    rightButton: ButtonState.Released,
                    xButton1: ButtonState.Released,
                    xButton2: ButtonState.Released);
            }
            else
            {
                return new MouseState(
                    x: (int)touch.Position.X,
                    y: (int)touch.Position.Y,
                    scrollWheel: 0,
                    leftButton: ButtonState.Released,
                    middleButton: ButtonState.Released,
                    rightButton: ButtonState.Released,
                    xButton1: ButtonState.Released,
                    xButton2: ButtonState.Released);
            }
        }
    }
}