    //------------------------------------------------------------------------------
    // emInput.h
    //
    // Copyright (C) 2005-2012,2014-2015 Oliver Hamann.
    //
    // Homepage: http://eaglemode.sourceforge.net/
    //
    // This program is free software: you can redistribute it and/or modify it under
    // the terms of the GNU General Public License version 3 as published by the
    // Free Software Foundation.
    //
    // 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 General Public License version 3 for
    // more details.
    //
    // You should have received a copy of the GNU General Public License version 3
    // along with this program. If not, see <http://www.gnu.org/licenses/>.
    //------------------------------------------------------------------------------

    #ifndef emInput_h
    #define emInput_h

    #ifndef emStd2_h
    #include <emCore/emStd2.h>
    #endif

    #ifndef emArray_h
    #include <emCore/emArray.h>
    #endif

    //==============================================================================
    //================================= emInputKey =================================
    //==============================================================================

    enum emInputKey {

            // None:
            EM_KEY_NONE           = 0x00,

            // Mouse buttons and wheel:
            EM_KEY_LEFT_BUTTON    = 0xF0,
            EM_KEY_MIDDLE_BUTTON  = 0xF1,
            EM_KEY_RIGHT_BUTTON   = 0xF2,
            EM_KEY_WHEEL_UP       = 0xF3,
            EM_KEY_WHEEL_DOWN     = 0xF4,
            EM_KEY_WHEEL_LEFT     = 0xF5,
            EM_KEY_WHEEL_RIGHT    = 0xF6,
            EM_KEY_BACK_BUTTON    = 0xF7,
            EM_KEY_FORWARD_BUTTON = 0xF8,

            // Screen touch:
            EM_KEY_TOUCH          = 0xEF,

            // Keyboard keys:
            EM_KEY_SHIFT          = 0x81,
            EM_KEY_CTRL           = 0x82,
            EM_KEY_ALT            = 0x83,
            EM_KEY_META           = 0x84,
            EM_KEY_ALT_GR         = 0x85,
            EM_KEY_CURSOR_UP      = 0x91,
            EM_KEY_CURSOR_DOWN    = 0x92,
            EM_KEY_CURSOR_LEFT    = 0x93,
            EM_KEY_CURSOR_RIGHT   = 0x94,
            EM_KEY_PAGE_UP        = 0x95,
            EM_KEY_PAGE_DOWN      = 0x96,
            EM_KEY_HOME           = 0x97,
            EM_KEY_END            = 0x98,
            EM_KEY_PRINT          = 0x99,
            EM_KEY_PAUSE          = 0x9A,
            EM_KEY_MENU           = 0x9B,
            EM_KEY_INSERT         = 0x9F,
            EM_KEY_DELETE         = 0x7F,
            EM_KEY_BACKSPACE      = 0x08,
            EM_KEY_TAB            = 0x09,
            EM_KEY_ENTER          = 0x0D,
            EM_KEY_ESCAPE         = 0x1B,
            EM_KEY_SPACE          = 0x20,
            EM_KEY_0              = 0x30,
            EM_KEY_1              = 0x31,
            EM_KEY_2              = 0x32,
            EM_KEY_3              = 0x33,
            EM_KEY_4              = 0x34,
            EM_KEY_5              = 0x35,
            EM_KEY_6              = 0x36,
            EM_KEY_7              = 0x37,
            EM_KEY_8              = 0x38,
            EM_KEY_9              = 0x39,
            EM_KEY_A              = 0x41,
            EM_KEY_B              = 0x42,
            EM_KEY_C              = 0x43,
            EM_KEY_D              = 0x44,
            EM_KEY_E              = 0x45,
            EM_KEY_F              = 0x46,
            EM_KEY_G              = 0x47,
            EM_KEY_H              = 0x48,
            EM_KEY_I              = 0x49,
            EM_KEY_J              = 0x4A,
            EM_KEY_K              = 0x4B,
            EM_KEY_L              = 0x4C,
            EM_KEY_M              = 0x4D,
            EM_KEY_N              = 0x4E,
            EM_KEY_O              = 0x4F,
            EM_KEY_P              = 0x50,
            EM_KEY_Q              = 0x51,
            EM_KEY_R              = 0x52,
            EM_KEY_S              = 0x53,
            EM_KEY_T              = 0x54,
            EM_KEY_U              = 0x55,
            EM_KEY_V              = 0x56,
            EM_KEY_W              = 0x57,
            EM_KEY_X              = 0x58,
            EM_KEY_Y              = 0x59,
            EM_KEY_Z              = 0x5A,
            EM_KEY_F1             = 0xA1,
            EM_KEY_F2             = 0xA2,
            EM_KEY_F3             = 0xA3,
            EM_KEY_F4             = 0xA4,
            EM_KEY_F5             = 0xA5,
            EM_KEY_F6             = 0xA6,
            EM_KEY_F7             = 0xA7,
            EM_KEY_F8             = 0xA8,
            EM_KEY_F9             = 0xA9,
            EM_KEY_F10            = 0xAA,
            EM_KEY_F11            = 0xAB,
            EM_KEY_F12            = 0xAC
    };

    bool emIsMouseInputKey(emInputKey key);
            // True for mouse buttons and mouse wheel.

    bool emIsTouchInputKey(emInputKey key);
            // True screen touch.

    bool emIsKeyboardInputKey(emInputKey key);
            // True for all keyboard keys, including modifiers.

    bool emIsModifierInputKey(emInputKey key);
            // True for shift, ctrl, alt and meta.

    const char * emInputKeyToString(emInputKey key);
    emInputKey emStringToInputKey(const char * str);
            // Convert an input key to and from string representation.

    //==============================================================================
    //================================ emInputEvent ================================
    //==============================================================================

    class emInputEvent : public emUncopyable {

    public:

            // Class for an input event. Such an event consists of an input key
            // (mouse buttons and wheel are even "keys" here), which has changed
            // from non-pressed to pressed state. Instead of a key, or in addition
            // to the key, a translation into text characters may be provided. It is
            // possible that an original event comes through two input events: one
            // with the input key, and one with the translated text characters (at
            // the time of writing this, it is so with emWndsWindowPort, but not
            // with emX11WindowPort).

            emInputEvent();
                    // Construct an event which is initially empty.

            void Setup(emInputKey key, const emString & chars, int repeat,
                       int variant);
                    // Set-up the event.
                    // Arguments:
                    //   key     - see GetKey().
                    //   chars   - see GetChars().
                    //   repeat  - see GetRepeat().
                    //   variant - see GetVariant().

            emInputKey GetKey() const;
                    // Get the key. This is EM_KEY_NONE if the event is empty or if
                    // the event provides characters only (see GetChars()).

            const emString & GetChars() const;
                    // Get the translated text characters. This should be at most
                    // one text character, but possibly encoded as a multi-byte
                    // sequence (e.g. UTF-8). This is an empty string if the event
                    // is empty, or if the key could not be translated.

            int GetRepeat() const;
                    // Get number of event repetitions. For example, 1 means
                    // double-click for mouse buttons.

            int GetVariant() const;
                    // Get variant of the event source. If the key is on the numeric
                    // key pad, or if it is the right shift, right ctrl or right alt
                    // key, then this should be 1. Otherwise this should be 0.

            void Eat();
                    // Eat this event (make it an empty event).

            bool IsEmpty() const;
            bool IsMouseEvent() const;
            bool IsTouchEvent() const;
            bool IsKeyboardEvent() const;
                    // Ask for the type of event. One of these returns true.

            bool IsKey(emInputKey key) const;
                    // Like GetKey()==key.

            bool IsLeftButton() const;
            bool IsMiddleButton() const;
            bool IsRightButton() const;
                    // Like GetKey()==EM_KEY_LEFT_BUTTON and so on.

    private:
            emInputKey Key;
            emString Chars;
            int Repeat,Variant;
    };

    //==============================================================================
    //================================ emInputState ================================
    //==============================================================================

    class emInputState {

    public:

            // Class for the state of keyboard and mouse. The state consists of the
            // mouse pointer position, and which keys are pressed. (Mouse buttons
            // are even keys)

            emInputState();
                    // Construct without having any key pressed.

            emInputState(const emInputState & inputState);
                    // Construct a copy of an input state.

            emInputState & operator = (const emInputState & inputState);
                    // Copy another input state to this input state.

            bool operator == (const emInputState & inputState) const;
            bool operator != (const emInputState & inputState) const;
                    // Comparison operators.

            double GetMouseX() const;
            double GetMouseY() const;
            void SetMouse(double mouseX, double mouseY);
                    // Get or set the mouse position in pixel coordinates of the
                    // view.

            int GetTouchCount() const;
                    // Get number of touches.

            int SearchTouch(emUInt64 id) const;
                    // Search a touch by id. Returns the index or -1 if not found.

            emUInt64 GetTouchId(int index) const;
                    // Get the id of a touch. A touch may get another index over the
                    // time, but it should keep its id.

            double GetTouchX(int index) const;
            double GetTouchY(int index) const;
                    // Get position of a touch in pixel coordinates of the view.

            void AddTouch(emUInt64 id, double x, double y);
                    // Add a touch.

            void SetTouch(int index, emUInt64 id, double x, double y);
                    // Modify an existing touch.

            void RemoveTouch(int index);
                    // Remove a touch.

            void ClearTouches();
                    // Remove all touches.

            bool Get(emInputKey key) const;
            void Set(emInputKey key, bool pressed);
                    // Get or set the state of a particular key. It is true if the
                    // key or button is pressed, false otherwise. EM_KEY_WHEEL_UP
                    // and EM_KEY_WHEEL_DOWN should never be set here.

            bool GetLeftButton() const;
            bool GetMiddleButton() const;
            bool GetRightButton() const;
            bool GetShift() const;
            bool GetCtrl() const;
            bool GetAlt() const;
            bool GetMeta() const;
                    // These are just some abbreviations. It's like
                    // Get(EM_KEY_LEFT_BUTTON) and so on.

            bool IsNoMod() const;
            bool IsShiftMod() const;
            bool IsShiftCtrlMod() const;
            bool IsShiftCtrlAltMod() const;
            bool IsShiftCtrlAltMetaMod() const;
            bool IsShiftCtrlMetaMod() const;
            bool IsShiftAltMod() const;
            bool IsShiftAltMetaMod() const;
            bool IsShiftMetaMod() const;
            bool IsCtrlMod() const;
            bool IsCtrlAltMod() const;
            bool IsCtrlAltMetaMod() const;
            bool IsCtrlMetaMod() const;
            bool IsAltMod() const;
            bool IsAltMetaMod() const;
            bool IsMetaMod() const;
                    // Test the four modifier keys at once. For example,
                    // IsShiftMetaMod() is like (GetShift() && !GetCtrl() &&
                    // !GetAlt() && GetMeta()).

            bool ClearKeyStates();
                    // Set all keys to non-pressed state. Returns true if something
                    // was pressed.

            const unsigned char * GetKeyStates() const;
            unsigned char * GetKeyStates();
                    // Get a pointer to the states of all keyboard keys and mouse
                    // buttons, even for modification. The size of the returned
                    // array is 32 bytes. A key is pressed if
                    // GetKeyStates()[key>>3]&(1<<(key&7)) is non-zero.

    private:

            struct Touch {
                    emUInt64 Id;
                    double X, Y;
            };

            double MouseX, MouseY;
            unsigned char KeyStates[32];
            emArray<Touch> Touches;
    };

    //==============================================================================
    //=============================== emInputHotkey ================================
    //==============================================================================

    class emInputHotkey {

    public:

            // Class for a hotkey. It is a combination of modifier keys and a
            // non-modifier keyboard key.

            emInputHotkey();
                    // Construct an invalid hotkey.

            emInputHotkey(const emInputHotkey & hotkey);
                    // Copy constructor.

            emInputHotkey(emInputKey key);
            emInputHotkey(emInputKey modifier, emInputKey key);
            emInputHotkey(emInputKey modifier1, emInputKey modifier2,
                          emInputKey key);
            emInputHotkey(emInputKey modifier1, emInputKey modifier2,
                          emInputKey modifier3, emInputKey key);
            emInputHotkey(emInputKey modifier1, emInputKey modifier2,
                          emInputKey modifier3, emInputKey modifier4,
                          emInputKey key);
                    // Construct a valid hotkey. The last argument must be a
                    // non-modifier keyboard key, the other arguments must be
                    // modifier keys.

            emInputHotkey(const emInputEvent & event, const emInputState & state);
                    // Construct form an input event and an input state. The hotkey
                    // gets invalid if the event is not a non-modifier keyboard key.

            emInputHotkey & operator = (const emInputHotkey & hotkey);
                    // Copy operator.

            bool operator == (const emInputHotkey & hotkey) const;
            bool operator != (const emInputHotkey & hotkey) const;
                    // Comparison operators.

            void TryParse(const char * str) throw(emException);
                    // Try to set this hotkey from a human-readable string
                    // representation (e.g. "Ctrl+C"). On failure, the hotkey is set
                    // invalid and an error message is thrown.

            emString GetString() const;
            void GetString(char * buf, int bufSize) const;
                    // Create a human-readable string representation (e.g.
                    // "Ctrl+C"). If the hotkey is invalid, an empty string is
                    // returned.

            bool IsValid() const;
                    // Whether this is a valid hotkey.

            bool GetShift() const;
            bool GetCtrl() const;
            bool GetAlt() const;
            bool GetMeta() const;
                    // Whether the respective modifier key is part of this hotkey.

            emInputKey GetKey() const;
                    // Get the event key. Returns EM_KEY_NONE if the hotkey is not
                    // valid.

            bool Match(const emInputEvent & event, const emInputState & state) const;
                    // Ask whether the input event and input state matches this
                    // hotkey.

            void ClearModifiers();
                    // Remove all modifiers.

            void AddModifier(emInputKey modifier);
                    // Add a modifier.

            void SetKey(emInputKey key);
                    // Set the event key. Must be a non-modifier keyboard key or the
                    // hotkey gets invalid.

    private:

            static const char * Key2Name(emInputKey key);
            static emInputKey Name2Key(const char * name, int len);

            enum {
                    MF_SHIFT=(1<<0),
                    MF_CTRL =(1<<1),
                    MF_ALT  =(1<<2),
                    MF_META =(1<<3)
            };

            union {
                    emUInt16 Packed;
                    struct {
                            emByte MFlags;
                            emByte Key;
                    } Data;
            };
    };

    //==============================================================================
    //=========================== Inline Implementations ===========================
    //==============================================================================

    inline bool emIsMouseInputKey(emInputKey key)
    {
            return key>=EM_KEY_LEFT_BUTTON;
    }

    inline bool emIsTouchInputKey(emInputKey key)
    {
            return key==EM_KEY_TOUCH;
    }

    inline bool emIsKeyboardInputKey(emInputKey key)
    {
            return key && key<EM_KEY_TOUCH;
    }

    inline bool emIsModifierInputKey(emInputKey key)
    {
            return (((int)key)&0xF8)==0x80;
    }

    inline emInputKey emInputEvent::GetKey() const
    {
            return Key;
    }

    inline const emString & emInputEvent::GetChars() const
    {
            return Chars;
    }

    inline int emInputEvent::GetRepeat() const
    {
            return Repeat;
    }

    inline int emInputEvent::GetVariant() const
    {
            return Variant;
    }

    inline bool emInputEvent::IsEmpty() const
    {
            return Key==EM_KEY_NONE && Chars.IsEmpty();
    }

    inline bool emInputEvent::IsMouseEvent() const
    {
            return emIsMouseInputKey(Key);
    }

    inline bool emInputEvent::IsTouchEvent() const
    {
            return emIsTouchInputKey(Key);
    }

    inline bool emInputEvent::IsKeyboardEvent() const
    {
            return emIsKeyboardInputKey(Key) || !Chars.IsEmpty();
    }

    inline bool emInputEvent::IsKey(emInputKey key) const
    {
            return Key==key;
    }

    inline bool emInputEvent::IsLeftButton() const
    {
            return Key==EM_KEY_LEFT_BUTTON;
    }

    inline bool emInputEvent::IsMiddleButton() const
    {
            return Key==EM_KEY_MIDDLE_BUTTON;
    }

    inline bool emInputEvent::IsRightButton() const
    {
            return Key==EM_KEY_RIGHT_BUTTON;
    }

    inline double emInputState::GetMouseX() const
    {
            return MouseX;
    }

    inline double emInputState::GetMouseY() const
    {
            return MouseY;
    }

    inline void emInputState::SetMouse(double mouseX, double mouseY)
    {
            MouseX=mouseX;
            MouseY=mouseY;
    }

    inline int emInputState::GetTouchCount() const
    {
            return Touches.GetCount();
    }

    inline emUInt64 emInputState::GetTouchId(int index) const
    {
            return Touches[index].Id;
    }

    inline double emInputState::GetTouchX(int index) const
    {
            return Touches[index].X;
    }

    inline double emInputState::GetTouchY(int index) const
    {
            return Touches[index].Y;
    }

    inline bool emInputState::GetLeftButton() const
    {
            return (KeyStates[EM_KEY_LEFT_BUTTON>>3]&(1<<(EM_KEY_LEFT_BUTTON&7)))!=0;
    }

    inline bool emInputState::GetMiddleButton() const
    {
            return (KeyStates[EM_KEY_MIDDLE_BUTTON>>3]&(1<<(EM_KEY_MIDDLE_BUTTON&7)))!=0;
    }

    inline bool emInputState::GetRightButton() const
    {
            return (KeyStates[EM_KEY_RIGHT_BUTTON>>3]&(1<<(EM_KEY_RIGHT_BUTTON&7)))!=0;
    }

    inline bool emInputState::GetShift() const
    {
            return (KeyStates[EM_KEY_SHIFT>>3]&(1<<(EM_KEY_SHIFT&7)))!=0;
    }

    inline bool emInputState::GetCtrl() const
    {
            return (KeyStates[EM_KEY_CTRL>>3]&(1<<(EM_KEY_CTRL&7)))!=0;
    }

    inline bool emInputState::GetAlt() const
    {
            return (KeyStates[EM_KEY_ALT>>3]&(1<<(EM_KEY_ALT&7)))!=0;
    }

    inline bool emInputState::GetMeta() const
    {
            return (KeyStates[EM_KEY_META>>3]&(1<<(EM_KEY_META&7)))!=0;
    }

    inline const unsigned char * emInputState::GetKeyStates() const
    {
            return KeyStates;
    }

    inline unsigned char * emInputState::GetKeyStates()
    {
            return KeyStates;
    }

    inline emInputHotkey::emInputHotkey()
    {
            Packed=0;
    }

    inline emInputHotkey::emInputHotkey(const emInputHotkey & hotkey)
    {
            Packed=hotkey.Packed;
    }

    inline emInputHotkey & emInputHotkey::operator = (const emInputHotkey & hotkey)
    {
            Packed=hotkey.Packed;
            return *this;
    }

    inline bool emInputHotkey::operator == (const emInputHotkey & hotkey) const
    {
            return Packed==hotkey.Packed;
    }

    inline bool emInputHotkey::operator != (const emInputHotkey & hotkey) const
    {
            return Packed!=hotkey.Packed;
    }

    inline bool emInputHotkey::IsValid() const
    {
            return Data.Key!=(emByte)EM_KEY_NONE;
    }

    inline bool emInputHotkey::GetShift() const
    {
            return (Data.MFlags&MF_SHIFT)!=0;
    }

    inline bool emInputHotkey::GetCtrl() const
    {
            return (Data.MFlags&MF_CTRL)!=0;
    }

    inline bool emInputHotkey::GetAlt() const
    {
            return (Data.MFlags&MF_ALT)!=0;
    }

    inline bool emInputHotkey::GetMeta() const
    {
            return (Data.MFlags&MF_META)!=0;
    }

    inline emInputKey emInputHotkey::GetKey() const
    {
            return (emInputKey)Data.Key;
    }

    inline void emInputHotkey::ClearModifiers()
    {
            Data.MFlags=0;
    }

    #endif