/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2005 The OGRE Team
Also see acknowledgements in Readme.html

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.
-----------------------------------------------------------------------------
*/

#include "OgreWin32Input.h"
#ifndef OGRE_NO_DX_INPUT
#ifdef DX7INPUTONLY

#include "OgreRenderWindow.h"
#include "OgreLogManager.h"
#include "OgreException.h"
#include "OgreRoot.h"
#include "OgreRenderSystem.h"

#define DINPUT_BUFFERSIZE  64

namespace Ogre {
    //-----------------------------------------------------------------------
    Win32Input::Win32Input() :
		InputReader()
    {
        mlpDI = 0;
        mlpDIKeyboard = 0;
        mlpDIMouse = 0;

        memset(mKeyboardBuffer,0,256);


    }
    //-----------------------------------------------------------------------
    Win32Input::~Win32Input()
    {
        // Shutdown
        if (mlpDIKeyboard)
        {
            mlpDIKeyboard->Unacquire();
            mlpDIKeyboard->Release();
            mlpDIKeyboard = 0;
        }
        if (mlpDIMouse)
        {
            mlpDIMouse->Unacquire();
            mlpDIMouse->Release();
            mlpDIMouse = 0;
        }
        if (mlpDI)
        {
            mlpDI->Release();
            mlpDI = 0;
        }

    }

    //-----------------------------------------------------------------------
    void Win32Input::initialise(RenderWindow* pWindow, bool useKeyboard, bool useMouse, bool useGameController)
    {
        HRESULT hr;

        LogManager::getSingleton().logMessage("Win32Input: DirectInput Activation Starts");

        // Get HINST
        HINSTANCE hInst = GetModuleHandle(OGRE_PLATFORM_LIB);


        // Get HWND
        HWND hWnd;
        //pWindow->getCustomAttribute("HWND", &hWnd);
        // Decouple from Win32Window
        hWnd = GetActiveWindow();

        mHWnd = hWnd;
        RECT rect;
        GetClientRect(mHWnd, &rect);
        mMouseCenterX = (rect.right - rect.left) / 2;
        mMouseCenterY = (rect.bottom - rect.top) / 2;
        POINT p;
        p.x = mMouseCenterX;
        p.y = mMouseCenterY;
        ClientToScreen(mHWnd, &p);
        SetCursorPos(p.x, p.y);
        // hide cursor
        ShowCursor(FALSE);



        // Create direct input
        hr = DirectInputCreateEx(hInst, DIRECTINPUT_VERSION,
            IID_IDirectInput7, (void**)&mlpDI, NULL);

        if (FAILED(hr))
            throw Exception(hr, "Unable to initialise DirectInput.",
                "Win32Input - initialise");

        if (useKeyboard)
        {
            LogManager::getSingleton().logMessage("Win32Input: Establishing keyboard input.");

            // Create keyboard device
            hr = mlpDI->CreateDeviceEx(GUID_SysKeyboard, IID_IDirectInputDevice7,
                (void**)&mlpDIKeyboard, NULL);


            if (FAILED(hr))
                throw Exception(hr, "Unable to create DirectInput keyboard device.",
                    "Win32Input - initialise");

            // Set data format
            hr = mlpDIKeyboard->SetDataFormat(&c_dfDIKeyboard);
            if (FAILED(hr))
                throw Exception(hr, "Unable to set DirectInput keyboard device data format.",
                    "Win32Input - initialise");

            // Make the window grab keyboard behaviour when foreground
            // NB Keyboard is never exclusive
            hr = mlpDIKeyboard->SetCooperativeLevel(hWnd,
                       DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
            if (FAILED(hr))
                throw Exception(hr, "Unable to set DirectInput keyboard device co-operative level.",
                    "Win32Input - initialise");


            // Acquire input
            hr = mlpDIKeyboard->Acquire();
            if (FAILED(hr))
                throw Exception(hr, "Unable to set aquire DirectInput keyboard device.",
                    "Win32Input - initialise");

            LogManager::getSingleton().logMessage("Win32Input: Keyboard input established.");
        }
        if (useMouse)
        {
            /* don't use DI
            LogManager::getSingleton().logMessage("Win32Input: Establishing mouse input.");

            // Create mouse device
            hr = mlpDI->CreateDeviceEx(GUID_SysMouse, IID_IDirectInputDevice7,
                (void**)&mlpDIMouse, NULL);


            if (FAILED(hr))
                throw Exception(hr, "Unable to create DirectInput mouse device.",
                    "Win32Input - initialise");

            // Set data format
            hr = mlpDIMouse->SetDataFormat(&c_dfDIMouse);
            if (FAILED(hr))
                throw Exception(hr, "Unable to set DirectInput mouse device data format.",
                    "Win32Input - initialise");

            // Make the window grab mouse behaviour when foreground
            hr = mlpDIMouse->SetCooperativeLevel(hWnd,
                       DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
            if (FAILED(hr))
                throw Exception(hr, "Unable to set DirectInput mouse device co-operative level.",
                    "Win32Input - initialise");

            // Acquire input
            hr = mlpDIKeyboard->Acquire();
            if (FAILED(hr))
                throw Exception(hr, "Unable to set aquire DirectInput mouse device.",
                    "Win32Input - initialise");

            LogManager::getSingleton().logMessage("Win32Input: Mouse input established.");
            */

        }


        LogManager::getSingleton().logMessage("Win32Input: DirectInput OK.(**** YES !!!, the old version using DX7****)");

    }

    //-----------------------------------------------------------------------
    void Win32Input::capture(void)
    {

        HRESULT  hr;

        // Get keyboard state
        hr = mlpDIKeyboard->GetDeviceState(256,(LPVOID)&mKeyboardBuffer);
        if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
        {
            hr = mlpDIKeyboard->Acquire();
            if (hr == DIERR_OTHERAPPHASPRIO)
            {
                hr = 0;
            }
            else
            {
                hr = mlpDIKeyboard->GetDeviceState(256,(LPVOID)&mKeyboardBuffer);
            }
        }
        else if (hr == DIERR_OTHERAPPHASPRIO)
        {
            // We've gone into the background - ignore
            hr = 0;
        }
        else if (hr == DIERR_NOTINITIALIZED)
        {
            hr = 0;
        }
        else if (hr == E_PENDING)
        {
            hr = 0;
        }
        else if (FAILED(hr))
        {
            // Ignore for now
            // TODO - sort this out
            hr = 0;
        }

        /*
        DIMOUSESTATE diMouseState;

        if (mlpDIMouse)
        {
            hr = mlpDIMouse->GetDeviceState(sizeof(DIMOUSESTATE),(LPVOID)&diMouseState);
            if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
            {
                hr = mlpDIMouse->Acquire();
                if (hr == DIERR_OTHERAPPHASPRIO)
                {
                    hr = 0;
                }
                else
                {
                    hr = mlpDIKeyboard->GetDeviceState(sizeof(DIMOUSESTATE),(LPVOID)&diMouseState);
                }
            }
            else if (hr == DIERR_OTHERAPPHASPRIO)
            {
                // We've gone into the background - ignore
                hr = 0;
            }
            else if (hr == DIERR_NOTINITIALIZED)
            {
                hr = 0;
            }
            else if (hr == E_PENDING)
            {
                hr = 0;
            }
            else if (FAILED(hr))
            {
                // Ignore for now
                // TODO - sort this out
                hr = 0;
            }
            else
            {
                mMouseRelX = diMouseState.lX;
                mMouseRelY = diMouseState.lY;

            }
        }
        */
        /*
            Only update mouse position if the window has the focus
         */
        if( mHWnd == GetForegroundWindow() )
        {
            POINT p;
            GetCursorPos(&p);
            ScreenToClient(mHWnd,&p);
            mMouseX = (Real)p.x;
            mMouseY = (Real)p.y;
            p.x = mMouseCenterX;
            p.y = mMouseCenterY;
            ClientToScreen(mHWnd, &p);
            if( IsWindowVisible( mHWnd ) )
            SetCursorPos(p.x, p.y);
        }
    }
    //-----------------------------------------------------------------------
    bool Win32Input::isKeyDownImmediate(KeyCode kc) const 
    {
        if (mKeyboardBuffer[kc] & 0x80)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    //-----------------------------------------------------------------------
    long Win32Input::getMouseRelX(void) const 
    {
        return (long)(mMouseX - mMouseCenterX);
    }
    //-----------------------------------------------------------------------
    long Win32Input::getMouseRelY(void) const 
    {
        return (long)(mMouseY - mMouseCenterY);
    }


} // namespace
#endif
#endif
