/* 
 *  Copyright 2008-2018 Alex Makeev
 * 
 *  This file is part of ZXMAK2 (ZX Spectrum virtual machine).
 *
 *  ZXMAK2 is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  ZXMAK2 is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with ZXMAK2.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Description: ZX Spectrum mouse emulator
 *  Author: Alex Makeev
 *  Date: 26.03.2008, 10.07.2018
 */
using System;
using System.Windows.Forms;
using ZXMAK2.Host.Interfaces;
using ZXMAK2.DirectX;
using ZXMAK2.DirectX.DirectInput;


namespace ZXMAK2.Host.WinForms.Mdx
{
    public sealed class DirectMouse : IHostMouse, IDisposable
    {
        private readonly MouseStateWrapper m_state = new MouseStateWrapper();
        private Form m_form;
        private DirectInputDevice8W m_device;
        private bool m_active;


        #region .ctor

        public unsafe DirectMouse(Form form)
        {
            m_form = form;
            if (m_device == null)
            {
                using (var dinput = new DirectInput8W())
                {
                    m_device = dinput.CreateDevice(SystemGuid.GUID_SysMouse, null);
                }
                m_device.SetDataFormat(DIDATAFORMAT.c_dfDIMouse).CheckError();

                form.Deactivate += WndDeactivate;
            }
        }

        public void Dispose()
        {
            if (m_device != null)
            {
                m_active = false;
                m_device.Unacquire();
            }
            Dispose(ref m_device);
        }

        #endregion .ctor


        #region IHostMouse

        public IMouseState MouseState
        {
            get { return m_state; }
        }

        public bool IsCaptured
        {
            get { return m_active; }
        }

        private DIMOUSESTATE _state;
        public void Scan()
        {
            if (!m_active || m_device==null)
            {
                return;
            }
            var result = m_device.GetDeviceState(out _state);
            if (result == ErrorCode.S_OK)
            {
                m_state.Update(ref _state);
            }
            else//if(ErrorCode.DIERR_NOTACQUIRED)
            {
                Uncapture();    
            }
        }

        public void Capture()
        {
            if (m_device == null || m_active)
            {
                return;
            }
            var hr = m_device.SetCooperativeLevel(
                    m_form == null ? IntPtr.Zero : m_form.Handle,
                    DISCL.EXCLUSIVE | DISCL.FOREGROUND);
            if (hr == ErrorCode.S_OK)
            {
                hr = m_device.Acquire();
                m_active = true;
            }
            if (hr != ErrorCode.S_OK)
            {
                Uncapture();
            }
        }

        public void Uncapture()
        {
            if (m_device == null)
            {
                return;
            }
            if (m_active)
            {
                m_device.Unacquire();
            }
            m_device.SetCooperativeLevel(
                m_form == null ? IntPtr.Zero : m_form.Handle,
                DISCL.NONEXCLUSIVE | DISCL.FOREGROUND);
            m_active = false;
        }


        #endregion IHostMouse


        #region Private

        private void WndDeactivate(object sender, EventArgs e)
        {
            Uncapture();
        }

        private static void Dispose<T>(ref T disposable)
            where T : IDisposable
        {
            var value = disposable;
            disposable = default(T);
            value.Dispose();
        }

        #endregion Private


        private class MouseStateWrapper : IMouseState
        {
            private int m_x = 128;
            private int m_y = 128;
            private int m_b = 0;

            internal MouseStateWrapper()
            {
            }

            internal void Update(ref DIMOUSESTATE state)
            {
                m_x += state.lX;
                m_y += state.lY;

                m_b = 0;
                if ((state.rgbButton0 & 0x80) != 0) m_b |= 1;
                if ((state.rgbButton1 & 0x80) != 0) m_b |= 2;
                if ((state.rgbButton2 & 0x80) != 0) m_b |= 4;
                if ((state.rgbButton3 & 0x80) != 0) m_b |= 8;
                //if ((state.rgbButton4 & 0x80) != 0) m_b |= 16;
                //if ((state.rgbButton5 & 0x80) != 0) m_b |= 32;
            }

            public int X { get { return m_x; } }
            public int Y { get { return m_y; } }
            public int Buttons { get { return m_b; } }
        }
    }
}
