//------------------------------------------------------------------------------
// emInput.h
//
// Copyright (C) 2005-2012,2014-2015,2018 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);
                // 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