/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program 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.
 *
 * 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#if defined(__ANDROID__)

// Allow use of stuff in <time.h>
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h

// Disable printf override in common/forbidden.h to avoid
// clashes with log.h from the Android SDK.
// That header file uses
//   __attribute__ ((format(printf, 3, 4)))
// which gets messed up by our override mechanism; this could
// be avoided by either changing the Android SDK to use the equally
// legal and valid
//   __attribute__ ((format(__printf__, 3, 4)))
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the Android port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf

#include <android/input.h>

#include "backends/graphics/android/android-graphics.h"
#include "backends/platform/android/android.h"
#include "backends/platform/android/jni-android.h"

// floating point. use sparingly
template<class T>
static inline T scalef(T in, float numerator, float denominator) {
	return static_cast<float>(in) * numerator / denominator;
}

static const int kQueuedInputEventDelay = 50;

// event type
enum {
	JE_SYS_KEY = 0,
	JE_KEY = 1,
	JE_DPAD = 2,
	JE_DOWN = 3,
	JE_SCROLL = 4,
	JE_TAP = 5,
	JE_DOUBLE_TAP = 6,
	JE_MULTI = 7,
	JE_BALL = 8,
	JE_LMB_DOWN = 9,
	JE_LMB_UP = 10,
	JE_RMB_DOWN = 11,
	JE_RMB_UP = 12,
	JE_MOUSE_MOVE = 13,
	JE_GAMEPAD = 14,
	JE_JOYSTICK = 15,
	JE_MMB_DOWN = 16,
	JE_MMB_UP = 17,
	JE_BMB_DOWN = 18,
	JE_BMB_UP = 19,
	JE_FMB_DOWN = 20,
	JE_FMB_UP = 21,
	JE_QUIT = 0x1000,
	JE_MENU = 0x1001
};

// meta modifier
enum {
	AMETA_CTRL_MASK  = AMETA_CTRL_ON  | AMETA_CTRL_LEFT_ON  | AMETA_CTRL_RIGHT_ON,
	AMETA_META_MASK  = AMETA_META_ON  | AMETA_META_LEFT_ON  | AMETA_META_RIGHT_ON,
	AMETA_SHIFT_MASK = AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON,
	AMETA_ALT_MASK   = AMETA_ALT_ON   | AMETA_ALT_LEFT_ON   | AMETA_ALT_RIGHT_ON
};

// map android key codes to our kbd codes (common/keyboard.h)
static const Common::KeyCode jkeymap[] = {
	Common::KEYCODE_INVALID, // AKEYCODE_UNKNOWN
	Common::KEYCODE_LEFTSOFT, // AKEYCODE_SOFT_LEFT
	Common::KEYCODE_RIGHTSOFT, // AKEYCODE_SOFT_RIGHT
	Common::KEYCODE_AC_HOME, // AKEYCODE_HOME
	Common::KEYCODE_AC_BACK, // AKEYCODE_BACK
	Common::KEYCODE_CALL, // AKEYCODE_CALL
	Common::KEYCODE_HANGUP, // AKEYCODE_ENDCALL
	Common::KEYCODE_0, // AKEYCODE_0
	Common::KEYCODE_1, // AKEYCODE_1
	Common::KEYCODE_2, // AKEYCODE_2
	Common::KEYCODE_3, // AKEYCODE_3
	Common::KEYCODE_4, // AKEYCODE_4
	Common::KEYCODE_5, // AKEYCODE_5
	Common::KEYCODE_6, // AKEYCODE_6
	Common::KEYCODE_7, // AKEYCODE_7
	Common::KEYCODE_8, // AKEYCODE_8
	Common::KEYCODE_9, // AKEYCODE_9
	Common::KEYCODE_ASTERISK, // AKEYCODE_STAR
	Common::KEYCODE_HASH, // AKEYCODE_POUND
	Common::KEYCODE_UP, // AKEYCODE_DPAD_UP
	Common::KEYCODE_DOWN, // AKEYCODE_DPAD_DOWN
	Common::KEYCODE_LEFT, // AKEYCODE_DPAD_LEFT
	Common::KEYCODE_RIGHT, // AKEYCODE_DPAD_RIGHT
	Common::KEYCODE_SELECT, // AKEYCODE_DPAD_CENTER
	Common::KEYCODE_VOLUMEUP, // AKEYCODE_VOLUME_UP
	Common::KEYCODE_VOLUMEDOWN, // AKEYCODE_VOLUME_DOWN
	Common::KEYCODE_POWER, // AKEYCODE_POWER
	Common::KEYCODE_CAMERA, // AKEYCODE_CAMERA
	Common::KEYCODE_CLEAR, // AKEYCODE_CLEAR
	Common::KEYCODE_a, // AKEYCODE_A
	Common::KEYCODE_b, // AKEYCODE_B
	Common::KEYCODE_c, // AKEYCODE_C
	Common::KEYCODE_d, // AKEYCODE_D
	Common::KEYCODE_e, // AKEYCODE_E
	Common::KEYCODE_f, // AKEYCODE_F
	Common::KEYCODE_g, // AKEYCODE_G
	Common::KEYCODE_h, // AKEYCODE_H
	Common::KEYCODE_i, // AKEYCODE_I
	Common::KEYCODE_j, // AKEYCODE_J
	Common::KEYCODE_k, // AKEYCODE_K
	Common::KEYCODE_l, // AKEYCODE_L
	Common::KEYCODE_m, // AKEYCODE_M
	Common::KEYCODE_n, // AKEYCODE_N
	Common::KEYCODE_o, // AKEYCODE_O
	Common::KEYCODE_p, // AKEYCODE_P
	Common::KEYCODE_q, // AKEYCODE_Q
	Common::KEYCODE_r, // AKEYCODE_R
	Common::KEYCODE_s, // AKEYCODE_S
	Common::KEYCODE_t, // AKEYCODE_T
	Common::KEYCODE_u, // AKEYCODE_U
	Common::KEYCODE_v, // AKEYCODE_V
	Common::KEYCODE_w, // AKEYCODE_W
	Common::KEYCODE_x, // AKEYCODE_X
	Common::KEYCODE_y, // AKEYCODE_Y
	Common::KEYCODE_z, // AKEYCODE_Z
	Common::KEYCODE_COMMA, // AKEYCODE_COMMA
	Common::KEYCODE_PERIOD, // AKEYCODE_PERIOD
	Common::KEYCODE_LALT, // AKEYCODE_ALT_LEFT
	Common::KEYCODE_RALT, // AKEYCODE_ALT_RIGHT
	Common::KEYCODE_LSHIFT, // AKEYCODE_SHIFT_LEFT
	Common::KEYCODE_RSHIFT, // AKEYCODE_SHIFT_RIGHT
	Common::KEYCODE_TAB, // AKEYCODE_TAB
	Common::KEYCODE_SPACE, // AKEYCODE_SPACE
	Common::KEYCODE_LCTRL, // AKEYCODE_SYM
	Common::KEYCODE_WWW, // AKEYCODE_EXPLORER
	Common::KEYCODE_MAIL, // AKEYCODE_ENVELOPE
	Common::KEYCODE_RETURN, // AKEYCODE_ENTER
	Common::KEYCODE_BACKSPACE, // AKEYCODE_DEL
	Common::KEYCODE_BACKQUOTE, // AKEYCODE_GRAVE
	Common::KEYCODE_MINUS, // AKEYCODE_MINUS
	Common::KEYCODE_EQUALS, // AKEYCODE_EQUALS
	Common::KEYCODE_LEFTPAREN, // AKEYCODE_LEFT_BRACKET
	Common::KEYCODE_RIGHTPAREN, // AKEYCODE_RIGHT_BRACKET
	Common::KEYCODE_BACKSLASH, // AKEYCODE_BACKSLASH
	Common::KEYCODE_SEMICOLON, // AKEYCODE_SEMICOLON
	Common::KEYCODE_QUOTE, // AKEYCODE_APOSTROPHE
	Common::KEYCODE_SLASH, // AKEYCODE_SLASH
	Common::KEYCODE_AT, // AKEYCODE_AT
	Common::KEYCODE_INVALID, // AKEYCODE_NUM
	Common::KEYCODE_INVALID, // AKEYCODE_HEADSETHOOK
	Common::KEYCODE_INVALID, // AKEYCODE_FOCUS
	Common::KEYCODE_PLUS, // AKEYCODE_PLUS
	Common::KEYCODE_MENU, // AKEYCODE_MENU
	Common::KEYCODE_INVALID, // AKEYCODE_NOTIFICATION
	Common::KEYCODE_AC_SEARCH, // AKEYCODE_SEARCH
	Common::KEYCODE_AUDIOPLAYPAUSE, // AKEYCODE_MEDIA_PLAY_PAUSE
	Common::KEYCODE_AUDIOSTOP, // AKEYCODE_MEDIA_STOP
	Common::KEYCODE_AUDIONEXT, // AKEYCODE_MEDIA_NEXT
	Common::KEYCODE_AUDIOPREV, // AKEYCODE_MEDIA_PREVIOUS
	Common::KEYCODE_AUDIOREWIND, // AKEYCODE_MEDIA_REWIND
	Common::KEYCODE_AUDIOFASTFORWARD, // AKEYCODE_MEDIA_FAST_FORWARD
	Common::KEYCODE_MUTE, // AKEYCODE_MUTE
	Common::KEYCODE_PAGEUP, // AKEYCODE_PAGE_UP
	Common::KEYCODE_PAGEDOWN, // AKEYCODE_PAGE_DOWN
	Common::KEYCODE_INVALID, // AKEYCODE_PICTSYMBOLS
	Common::KEYCODE_INVALID, // AKEYCODE_SWITCH_CHARSET
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_A
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_B
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_C
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_X
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_Y
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_Z
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_L1
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_R1
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_L2
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_R2
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_THUMBL
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_THUMBR
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_START
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_SELECT
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_MODE
	Common::KEYCODE_ESCAPE, // AKEYCODE_ESCAPE
	Common::KEYCODE_DELETE, // AKEYCODE_FORWARD_DEL
	Common::KEYCODE_LCTRL, // AKEYCODE_CTRL_LEFT
	Common::KEYCODE_RCTRL, // AKEYCODE_CTRL_RIGHT
	Common::KEYCODE_CAPSLOCK, // AKEYCODE_CAPS_LOCK
	Common::KEYCODE_SCROLLOCK, // AKEYCODE_SCROLL_LOCK
	Common::KEYCODE_LSUPER, // AKEYCODE_META_LEFT
	Common::KEYCODE_RSUPER, // AKEYCODE_META_RIGHT
	Common::KEYCODE_INVALID, // AKEYCODE_FUNCTION
	Common::KEYCODE_SYSREQ, // AKEYCODE_SYSRQ
	Common::KEYCODE_BREAK, // AKEYCODE_BREAK
	Common::KEYCODE_HOME, // AKEYCODE_MOVE_HOME
	Common::KEYCODE_END, // AKEYCODE_MOVE_END
	Common::KEYCODE_INSERT, // AKEYCODE_INSERT
	Common::KEYCODE_AC_FORWARD, // AKEYCODE_FORWARD
	Common::KEYCODE_AUDIOPLAY, // AKEYCODE_MEDIA_PLAY
	Common::KEYCODE_AUDIOPAUSE, // AKEYCODE_MEDIA_PAUSE
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_CLOSE
	Common::KEYCODE_EJECT, // AKEYCODE_MEDIA_EJECT
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_RECORD
	Common::KEYCODE_F1, // AKEYCODE_F1
	Common::KEYCODE_F2, // AKEYCODE_F2
	Common::KEYCODE_F3, // AKEYCODE_F3
	Common::KEYCODE_F4, // AKEYCODE_F4
	Common::KEYCODE_F5, // AKEYCODE_F5
	Common::KEYCODE_F6, // AKEYCODE_F6
	Common::KEYCODE_F7, // AKEYCODE_F7
	Common::KEYCODE_F8, // AKEYCODE_F8
	Common::KEYCODE_F9, // AKEYCODE_F9
	Common::KEYCODE_F10, // AKEYCODE_F10
	Common::KEYCODE_F11, // AKEYCODE_F11
	Common::KEYCODE_F12, // AKEYCODE_F12
	Common::KEYCODE_NUMLOCK, // AKEYCODE_NUM_LOCK
	Common::KEYCODE_KP0, // AKEYCODE_NUMPAD_0
	Common::KEYCODE_KP1, // AKEYCODE_NUMPAD_1
	Common::KEYCODE_KP2, // AKEYCODE_NUMPAD_2
	Common::KEYCODE_KP3, // AKEYCODE_NUMPAD_3
	Common::KEYCODE_KP4, // AKEYCODE_NUMPAD_4
	Common::KEYCODE_KP5, // AKEYCODE_NUMPAD_5
	Common::KEYCODE_KP6, // AKEYCODE_NUMPAD_6
	Common::KEYCODE_KP7, // AKEYCODE_NUMPAD_7
	Common::KEYCODE_KP8, // AKEYCODE_NUMPAD_8
	Common::KEYCODE_KP9, // AKEYCODE_NUMPAD_9
	Common::KEYCODE_KP_DIVIDE, // AKEYCODE_NUMPAD_DIVIDE
	Common::KEYCODE_KP_MULTIPLY, // AKEYCODE_NUMPAD_MULTIPLY
	Common::KEYCODE_KP_MINUS, // AKEYCODE_NUMPAD_SUBTRACT
	Common::KEYCODE_KP_PLUS, // AKEYCODE_NUMPAD_ADD
	Common::KEYCODE_KP_PERIOD, // AKEYCODE_NUMPAD_DOT
	Common::KEYCODE_INVALID, // AKEYCODE_NUMPAD_COMMA
	Common::KEYCODE_KP_ENTER, // AKEYCODE_NUMPAD_ENTER
	Common::KEYCODE_KP_EQUALS, // AKEYCODE_NUMPAD_EQUALS
	Common::KEYCODE_INVALID, // AKEYCODE_NUMPAD_LEFT_PAREN
	Common::KEYCODE_INVALID, // AKEYCODE_NUMPAD_RIGHT_PAREN
	Common::KEYCODE_INVALID, // AKEYCODE_VOLUME_MUTE
	Common::KEYCODE_INVALID, // AKEYCODE_INFO
	Common::KEYCODE_INVALID, // AKEYCODE_CHANNEL_UP
	Common::KEYCODE_INVALID, // AKEYCODE_CHANNEL_DOWN
	Common::KEYCODE_INVALID, // AKEYCODE_ZOOM_IN
	Common::KEYCODE_INVALID, // AKEYCODE_ZOOM_OUT
	Common::KEYCODE_INVALID, // AKEYCODE_TV
	Common::KEYCODE_INVALID, // AKEYCODE_WINDOW
	Common::KEYCODE_INVALID, // AKEYCODE_GUIDE
	Common::KEYCODE_INVALID, // AKEYCODE_DVR
	Common::KEYCODE_AC_BOOKMARKS, // AKEYCODE_BOOKMARK
	Common::KEYCODE_INVALID, // AKEYCODE_CAPTIONS
	Common::KEYCODE_INVALID, // AKEYCODE_SETTINGS
	Common::KEYCODE_INVALID, // AKEYCODE_TV_POWER
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT
	Common::KEYCODE_INVALID, // AKEYCODE_STB_POWER
	Common::KEYCODE_INVALID, // AKEYCODE_STB_INPUT
	Common::KEYCODE_INVALID, // AKEYCODE_AVR_POWER
	Common::KEYCODE_INVALID, // AKEYCODE_AVR_INPUT
	Common::KEYCODE_INVALID, // AKEYCODE_PROG_RED
	Common::KEYCODE_INVALID, // AKEYCODE_PROG_GREEN
	Common::KEYCODE_INVALID, // AKEYCODE_PROG_YELLOW
	Common::KEYCODE_INVALID, // AKEYCODE_PROG_BLUE
	Common::KEYCODE_INVALID, // AKEYCODE_APP_SWITCH
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_1
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_2
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_3
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_4
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_5
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_6
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_7
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_8
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_9
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_10
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_11
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_12
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_13
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_14
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_15
	Common::KEYCODE_INVALID, // AKEYCODE_BUTTON_16
	Common::KEYCODE_INVALID, // AKEYCODE_LANGUAGE_SWITCH
	Common::KEYCODE_INVALID, // AKEYCODE_MANNER_MODE
	Common::KEYCODE_INVALID, // AKEYCODE_3D_MODE
	Common::KEYCODE_INVALID, // AKEYCODE_CONTACTS
	Common::KEYCODE_INVALID, // AKEYCODE_CALENDAR
	Common::KEYCODE_INVALID, // AKEYCODE_MUSIC
	Common::KEYCODE_CALCULATOR, // AKEYCODE_CALCULATOR
	Common::KEYCODE_INVALID, // AKEYCODE_ZENKAKU_HANKAKU
	Common::KEYCODE_INVALID, // AKEYCODE_EISU
	Common::KEYCODE_INVALID, // AKEYCODE_MUHENKAN
	Common::KEYCODE_INVALID, // AKEYCODE_HENKAN
	Common::KEYCODE_INVALID, // AKEYCODE_KATAKANA_HIRAGANA
	Common::KEYCODE_INVALID, // AKEYCODE_YEN
	Common::KEYCODE_INVALID, // AKEYCODE_RO
	Common::KEYCODE_INVALID, // AKEYCODE_KANA
	Common::KEYCODE_INVALID, // AKEYCODE_ASSIST
	Common::KEYCODE_INVALID, // AKEYCODE_BRIGHTNESS_DOWN
	Common::KEYCODE_INVALID, // AKEYCODE_BRIGHTNESS_UP
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_AUDIO_TRACK
	Common::KEYCODE_SLEEP, // AKEYCODE_SLEEP
	Common::KEYCODE_INVALID, // AKEYCODE_WAKEUP
	Common::KEYCODE_INVALID, // AKEYCODE_PAIRING
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_TOP_MENU
	Common::KEYCODE_INVALID, // AKEYCODE_11
	Common::KEYCODE_INVALID, // AKEYCODE_12
	Common::KEYCODE_INVALID, // AKEYCODE_LAST_CHANNEL
	Common::KEYCODE_INVALID, // AKEYCODE_TV_DATA_SERVICE
	Common::KEYCODE_INVALID, // AKEYCODE_VOICE_ASSIST
	Common::KEYCODE_INVALID, // AKEYCODE_TV_RADIO_SERVICE
	Common::KEYCODE_INVALID, // AKEYCODE_TV_TELETEXT
	Common::KEYCODE_INVALID, // AKEYCODE_TV_NUMBER_ENTRY
	Common::KEYCODE_INVALID, // AKEYCODE_TV_TERRESTRIAL_ANALOG
	Common::KEYCODE_INVALID, // AKEYCODE_TV_TERRESTRIAL_DIGITAL
	Common::KEYCODE_INVALID, // AKEYCODE_TV_SATELLITE
	Common::KEYCODE_INVALID, // AKEYCODE_TV_SATELLITE_BS
	Common::KEYCODE_INVALID, // AKEYCODE_TV_SATELLITE_CS
	Common::KEYCODE_INVALID, // AKEYCODE_TV_SATELLITE_SERVICE
	Common::KEYCODE_INVALID, // AKEYCODE_TV_NETWORK
	Common::KEYCODE_INVALID, // AKEYCODE_TV_ANTENNA_CABLE
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_HDMI_1
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_HDMI_2
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_HDMI_3
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_HDMI_4
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_COMPOSITE_1
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_COMPOSITE_2
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_COMPONENT_1
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_COMPONENT_2
	Common::KEYCODE_INVALID, // AKEYCODE_TV_INPUT_VGA_1
	Common::KEYCODE_INVALID, // AKEYCODE_TV_AUDIO_DESCRIPTION
	Common::KEYCODE_INVALID, // AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP
	Common::KEYCODE_INVALID, // AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN
	Common::KEYCODE_INVALID, // AKEYCODE_TV_ZOOM_MODE
	Common::KEYCODE_INVALID, // AKEYCODE_TV_CONTENTS_MENU
	Common::KEYCODE_INVALID, // AKEYCODE_TV_MEDIA_CONTEXT_MENU
	Common::KEYCODE_INVALID, // AKEYCODE_TV_TIMER_PROGRAMMING
	Common::KEYCODE_HELP, // AKEYCODE_HELP
	Common::KEYCODE_INVALID, // AKEYCODE_NAVIGATE_PREVIOUS
	Common::KEYCODE_INVALID, // AKEYCODE_NAVIGATE_NEXT
	Common::KEYCODE_INVALID, // AKEYCODE_NAVIGATE_IN
	Common::KEYCODE_INVALID, // AKEYCODE_NAVIGATE_OUT
	Common::KEYCODE_INVALID, // AKEYCODE_STEM_PRIMARY
	Common::KEYCODE_INVALID, // AKEYCODE_STEM_1
	Common::KEYCODE_INVALID, // AKEYCODE_STEM_2
	Common::KEYCODE_INVALID, // AKEYCODE_STEM_3
	Common::KEYCODE_INVALID, // AKEYCODE_DPAD_UP_LEFT
	Common::KEYCODE_INVALID, // AKEYCODE_DPAD_DOWN_LEFT
	Common::KEYCODE_INVALID, // AKEYCODE_DPAD_UP_RIGHT
	Common::KEYCODE_INVALID, // AKEYCODE_DPAD_DOWN_RIGHT
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_SKIP_FORWARD
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_SKIP_BACKWARD
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_STEP_FORWARD
	Common::KEYCODE_INVALID, // AKEYCODE_MEDIA_STEP_BACKWARD
	Common::KEYCODE_INVALID, // AKEYCODE_SOFT_SLEEP
	Common::KEYCODE_CUT, // AKEYCODE_CUT
	Common::KEYCODE_COPY, // AKEYCODE_COPY
	Common::KEYCODE_PASTE // AKEYCODE_PASTE
};

void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
								int arg4, int arg5, int arg6) {
	Common::Event e;

	switch (type) {
	case JE_SYS_KEY:
		// fall through
	case JE_KEY:
		switch (arg1) {
		case AKEY_EVENT_ACTION_DOWN:
			e.type = Common::EVENT_KEYDOWN;
			break;

		case AKEY_EVENT_ACTION_UP:
			e.type = Common::EVENT_KEYUP;
			break;

		default:
			LOGE("unhandled jaction on key: %d", arg1);
			return;
		}

		if (arg2 < 1 || arg2 > ARRAYSIZE(jkeymap)) {
			if (arg3 < 1) {
				LOGE("received invalid keycode: %d (%d)", arg2, arg3);
				return;
			} else {
				// lets bet on the ascii code
				e.kbd.keycode = Common::KEYCODE_INVALID;
			}
		} else {
			e.kbd.keycode = jkeymap[arg2];
		}

		if (arg5 > 0) {
			e.kbdRepeat = true;
		}

		// map special keys to 'our' ascii codes
		switch (e.kbd.keycode) {
		case Common::KEYCODE_BACKSPACE:
//			LOGD("received BACKSPACE");
			e.kbd.ascii = Common::ASCII_BACKSPACE;
			break;

		case Common::KEYCODE_TAB:
			e.kbd.ascii = Common::ASCII_TAB;
			break;

		case Common::KEYCODE_RETURN:
			e.kbd.ascii = Common::ASCII_RETURN;
			break;

		case Common::KEYCODE_ESCAPE:
			e.kbd.ascii = Common::ASCII_ESCAPE;
			break;

		case Common::KEYCODE_SPACE:
			e.kbd.ascii = Common::ASCII_SPACE;
			break;

		case Common::KEYCODE_F1:
			e.kbd.ascii = Common::ASCII_F1;
			break;

		case Common::KEYCODE_F2:
			e.kbd.ascii = Common::ASCII_F2;
			break;

		case Common::KEYCODE_F3:
			e.kbd.ascii = Common::ASCII_F3;
			break;

		case Common::KEYCODE_F4:
			e.kbd.ascii = Common::ASCII_F4;
			break;

		case Common::KEYCODE_F5:
			e.kbd.ascii = Common::ASCII_F5;
			break;

		case Common::KEYCODE_F6:
			e.kbd.ascii = Common::ASCII_F6;
			break;

		case Common::KEYCODE_F7:
			e.kbd.ascii = Common::ASCII_F7;
			break;

		case Common::KEYCODE_F8:
			e.kbd.ascii = Common::ASCII_F8;
			break;

		case Common::KEYCODE_F9:
			e.kbd.ascii = Common::ASCII_F9;
			break;

		case Common::KEYCODE_F10:
			e.kbd.ascii = Common::ASCII_F10;
			break;

		case Common::KEYCODE_F11:
			e.kbd.ascii = Common::ASCII_F11;
			break;

		case Common::KEYCODE_F12:
			e.kbd.ascii = Common::ASCII_F12;
			break;

		default:
			e.kbd.ascii = arg3;
			break;
		}

		// arg4 is the metastate of the key press event
		// check for "Shift" key modifier
		if (arg4 & AMETA_SHIFT_MASK) {
			e.kbd.flags |= Common::KBD_SHIFT;
		}

		// We revert the commit to disable the Alt modifier
		// TODO revisit this old comment from commit https://github.com/scummvm/scummvm/commit/bfecb37501b6fdc35f2802216db5fb2b0e54b8ee
		//      for possible issues with physical keyboards and Fn key combos.
		//      It still seems like a bad idea to disable Alt key combos over Fn key combos
		//      and our keymapper should be able to handle this issue anyway
		//      (since it explicitly shows what key combination results from pressing keys, when the user edits a key mapping)
		//      More info: Back then we were checking for JMETA_ALT (defined as 0x02) which is now NDK's AMETA_ALT_ON (0x02) (see https://developer.android.com/ndk/reference/group/input)
		//      JMETA_ALT was defined in our own enum. (see full old file: https://github.com/scummvm/scummvm/blob/bfecb37501b6fdc35f2802216db5fb2b0e54b8ee/backends/platform/android/events.cpp#L113)
		// Old comment:
		// JMETA_ALT (0x02) is Fn on physical keyboards!
		// when mapping this to ALT - as we know it from PC keyboards - all
		// Fn combos will be broken (like Fn+q, which needs to end as 1 and
		// not ALT+1). Do not want.
		//if (arg4 & JMETA_ALT)
		//	e.kbd.flags |= Common::KBD_ALT;
		// end of old comment --
		// check for "Alt" key modifier
		if (arg4 & (AMETA_ALT_MASK)) {
			e.kbd.flags |= Common::KBD_ALT;
		}

		// check for "Ctrl" key modifier (We set Sym key to also behave as Ctrl)
		if (arg4 & (AMETA_SYM_ON | AMETA_CTRL_MASK)) {
			e.kbd.flags |= Common::KBD_CTRL;
		}

		// check for "Meta" key modifier
		if (arg4 & (AMETA_META_MASK)) {
			e.kbd.flags |= Common::KBD_META;
		}

		//  check for CAPS key modifier
		if (arg4 & (AMETA_CAPS_LOCK_ON)) {
			e.kbd.flags |= Common::KBD_CAPS;
		}

		//  check for NUM Lock key modifier
		if (arg4 & (AMETA_NUM_LOCK_ON)) {
			e.kbd.flags |= Common::KBD_NUM;
		}

		//  check for Scroll Lock key modifier
		if (arg4 & (AMETA_SCROLL_LOCK_ON)) {
			e.kbd.flags |= Common::KBD_SCRL;
		}

		pushEvent(e);

		return;

	case JE_DPAD:
		switch (arg2) {
		case AKEYCODE_DPAD_UP:
		// fall through
		case AKEYCODE_DPAD_DOWN:
		// fall through
		case AKEYCODE_DPAD_LEFT:
		// fall through
		case AKEYCODE_DPAD_RIGHT:
			if (arg1 != AKEY_EVENT_ACTION_DOWN)
				return;

			e.type = Common::EVENT_MOUSEMOVE;

			e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();

			{
				int16 *c;
				int s;

				if (arg2 == AKEYCODE_DPAD_UP || arg2 == AKEYCODE_DPAD_DOWN) {
					c = &e.mouse.y;
					s = _eventScaleY;
				} else {
					c = &e.mouse.x;
					s = _eventScaleX;
				}

				// the longer the button held, the faster the pointer is
				// TODO put these values in some option dlg?
				int f = CLIP(arg5, 1, 8) * _dpad_scale * 100 / s;

				if (arg2 == AKEYCODE_DPAD_UP || arg2 == AKEYCODE_DPAD_LEFT) {
					*c -= f;
				} else {
					*c += f;
				}
			}

			pushEvent(e);

			return;

		case AKEYCODE_DPAD_CENTER:
			switch (arg1) {
			case AKEY_EVENT_ACTION_DOWN:
				e.type = Common::EVENT_LBUTTONDOWN;
				break;

			case AKEY_EVENT_ACTION_UP:
				e.type = Common::EVENT_LBUTTONUP;
				break;

			default:
				LOGE("unhandled jaction on dpad key: %d", arg1);
				return;
			}

			e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();

			pushEvent(e);

			return;
		}

	case JE_DOWN:
//		LOGD("JE_DOWN");
		_touch_pt_down = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();
		_touch_pt_scroll.x = -1;
		_touch_pt_scroll.y = -1;
		break;

	case JE_SCROLL:
//		LOGD("JE_SCROLL");
		e.type = Common::EVENT_MOUSEMOVE;

		if (_touch_mode == TOUCH_MODE_TOUCHPAD) {
			if (_touch_pt_scroll.x == -1 && _touch_pt_scroll.y == -1) {
				_touch_pt_scroll.x = arg3;
				_touch_pt_scroll.y = arg4;
				return;
			}

			e.mouse.x = (arg3 - _touch_pt_scroll.x) * 100 / _touchpad_scale;
			e.mouse.y = (arg4 - _touch_pt_scroll.y) * 100 / _touchpad_scale;
			e.mouse += _touch_pt_down;
		} else {
			e.mouse.x = arg3;
			e.mouse.y = arg4;
		}
		e.relMouse.x = arg5;
		e.relMouse.y = arg6;

		pushEvent(e);

		return;

	case JE_TAP:
		// arg1 = mouse x
		// arg2 = mouse y
		// arg3 is (int)(e.getEventTime() - e.getDownTime())
//		LOGD("JE_TAP - arg3 %d", arg3);

		e.type = Common::EVENT_MOUSEMOVE;

		if (_touch_mode == TOUCH_MODE_TOUCHPAD) {
			e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();
		} else {
			e.mouse.x = arg1;
			e.mouse.y = arg2;
		}

		{
			Common::EventType down, up;

			// Based on this check, we check how long the tap finger was held down
			// and distinguish cases for:
			//  > 1.0 seconds: Middle Mouse Button
			//  > 0.5 seconds: Right Mouse Button
			//  < 0.5 seconds: Left Mouse Button
			// Due to how these are detected we cannot have hold and move detection for any of them
			// They only act as clicks
			// Note: for hold one finger and move gesture, this is the "move cursor around" action.
			//       Thus, the simple tap and hold cannot be used for "hold left mouse button and drag" behavior.
			//       This is the reason we use "double tap and move" to emulate that specific behavior.
			// TODO This might be unwanted "alternate" behavior (better to have it as optional?)
			// TODO put these time (in milliseconds) values in some option dlg?
			if (arg3 > 1000) {
//				LOGD("JE_TAP - arg3 %d --> Middle Mouse Button", arg3);
				down = Common::EVENT_MBUTTONDOWN;
				up = Common::EVENT_MBUTTONUP;
			} else if (arg3 > 500) {
//				LOGD("JE_TAP - arg3 %d --> Right Mouse Button", arg3);
				down = Common::EVENT_RBUTTONDOWN;
				up = Common::EVENT_RBUTTONUP;
			} else {
//				LOGD("JE_TAP - arg3 %d --> Left Mouse Button", arg3);
				down = Common::EVENT_LBUTTONDOWN;
				up = Common::EVENT_LBUTTONUP;
			}

			_event_queue_lock->lock();

//			LOGD("JE_TAP - _queuedEventTime %d ", _queuedEventTime);
			if (_queuedEventTime) {
				// if another event is queued to be served by PollEvent (but not in _event_queue)
				// at the time of this JE_TAP event
				// then push that pending _queuedEvent event to the queue for direct service
				// This is done because we will replace the _queuedEvent below
//				LOGD("JE_TAP -pushing old _queuedEvent to event queue");
				_event_queue.push(_queuedEvent);
			}

			if (_touch_mode != TOUCH_MODE_TOUCHPAD) {
				// In this case the mouse move is done in "direct mode"
				// ie. the cursor jumps to where the tap occurred
				// so we don't have relMouse coordinates to set for the event
				_event_queue.push(e);
			}

			e.type = down;
			_event_queue.push(e);

			e.type = up;
			_queuedEvent = e;
			_queuedEventTime = getMillis() + kQueuedInputEventDelay;

			_event_queue_lock->unlock();
		}

		return;

	case JE_DOUBLE_TAP:
		// arg1 = mouse x
		// arg2 = mouse y
		// arg3 = AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE
//		LOGD("JE_DOUBLE_TAP - arg3 %d", arg3);

		e.type = Common::EVENT_MOUSEMOVE;

		if (_touch_mode == TOUCH_MODE_TOUCHPAD) {
			e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();
		} else {
			e.mouse.x = arg1;
			e.mouse.y = arg2;
		}

		{
			Common::EventType dptype = Common::EVENT_INVALID;

			switch (arg3) {
			case AMOTION_EVENT_ACTION_DOWN:
				dptype = Common::EVENT_LBUTTONDOWN;
				_touch_pt_dt.x = -1;
				_touch_pt_dt.y = -1;
				break;

			case AMOTION_EVENT_ACTION_UP:
				dptype = Common::EVENT_LBUTTONUP;
				break;

			case AMOTION_EVENT_ACTION_MOVE:
				// held and moved
				if (_touch_pt_dt.x == -1 && _touch_pt_dt.y == -1) {
					_touch_pt_dt.x = arg1;
					_touch_pt_dt.y = arg2;
					return;
				}

				dptype = Common::EVENT_MOUSEMOVE;

				if (_touch_mode == TOUCH_MODE_TOUCHPAD) {
					e.mouse.x = (arg1 - _touch_pt_dt.x) * 100 / _touchpad_scale;
					e.mouse.y = (arg2 - _touch_pt_dt.y) * 100 / _touchpad_scale;
					e.mouse += _touch_pt_down;
				}

				break;

			default:
				LOGE("JE_DOUBLE_TAP - unhandled jaction on double tap: %d", arg3);
				return;
			}

			_event_queue_lock->lock();
			_event_queue.push(e);
			e.type = dptype;
			_event_queue.push(e);
			_event_queue_lock->unlock();
		}

		return;

	case JE_MULTI:
		// Documentation: https://developer.android.com/training/gestures/multi
		// TODO also look into: https://developer.android.com/training/gestures/movement
		//      for possible tweaks to gesture detection
		// arg1 = fingers down
		// arg2 = AMOTION_EVENT_ACTION_POINTER_DOWN, AMOTION_EVENT_ACTION_POINTER_UP, CANCEL, OUTSIDE, MOVE
		//        we handle CANCEL and OUTSIDE externally
		//
		// TODO Other related events for Android multitouch gestures events are:
		// - ACTION_DOWN — For the first pointer that touches the screen. This starts the gesture. The pointer data for this pointer is always at index 0 in the MotionEvent.
		// - ACTION_MOVE — A change has happened during a press gesture
		// - ACTION_UP   — Sent when the last pointer leaves the screen.
//		LOGD("JE_MULTI - fingersDown=%d arg2=%d", arg1, arg2);

		e.type = Common::EVENT_MOUSEMOVE;

		if (_touch_mode == TOUCH_MODE_TOUCHPAD) {
			e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();
		} else {
			e.mouse.x = arg3;
			e.mouse.y = arg4;
		}

		{
			Common::EventType multitype = Common::EVENT_INVALID;

			switch (arg2 & AMOTION_EVENT_ACTION_MASK) {
			case AMOTION_EVENT_ACTION_POINTER_DOWN:
				// (From Android Developers documentation)
				// [This event is fired] For extra pointers that enter the screen beyond the first.
				// The pointer data for this pointer is at the index returned by getActionIndex()
//				LOGD("AMOTION_EVENT_ACTION_POINTER_DOWN fingersDown: %d", arg1);
//				if (arg1 > _fingersDown) {
//					LOGD("AMOTION_EVENT_ACTION_POINTER_DOWN changing  _fingersDown to arg1: %d", arg1);
//					_fingersDown = arg1;
//				}

				_touch_pt_multi.x = -1;
				_touch_pt_multi.y = -1;

				// TODO also handle Cancel event
				// TODO also handle going from 3 to 2 fingers
				// TODO also handle case of receiving a new ACTION_DOWN without first having received an ACTION_UP
				switch (arg1) {
				case 2:
//					LOGD("AMOTION_EVENT_ACTION_POINTER_DOWN arg1: %d --> Right Mouse Button", arg1);
					multitype = Common::EVENT_RBUTTONDOWN;
					//up = Common::EVENT_RBUTTONUP;
//					// Don't add immediately in case it is superseded by a third finger --> middle mouse button
					break;

				case 3:
//					if (_queuedEventTime && _queuedEvent.type == Common::EVENT_RBUTTONDOWN) {
//						_queuedEventTime = 0; // cancel right mouse button down
//						LOGD("AMOTION_EVENT_ACTION_POINTER_DOWN arg1: %d --> Middle Mouse Button", arg1);
//						multitype = Common::EVENT_MBUTTONDOWN;
//					}
					multitype = Common::EVENT_MBUTTONDOWN;
					//up = Common::EVENT_MBUTTONUP;
					break;

				default:
					LOGE("AMOTION_EVENT_ACTION_POINTER_DOWN - unmapped multi tap (arg1): %d", arg1);
					return;
				}

				break;

 			case AMOTION_EVENT_ACTION_CANCEL:
//				LOGD("AMOTION_EVENT_ACTION_CANCEL - (arg1): %d", arg1);
 				return;

  			case AMOTION_EVENT_ACTION_OUTSIDE:
//				LOGD("AMOTION_EVENT_ACTION_OUTSIDE - (arg1): %d", arg1);
 				return;

			case AMOTION_EVENT_ACTION_POINTER_UP:
				// (From Android Developers documentation)
				// Sent when a non-primary pointer goes up.
//				LOGD("AMOTION_EVENT_ACTION_POINTER_UP arg1: %d", arg1);
//				if (arg1 != _fingersDown) {
//					LOGD("returning as AMOTION_EVENT_ACTION_POINTER_UP arg1 != _fingersDown");
//					_fingersDown = 0;
//					return;
//				}

				//Common::EventType up;

				switch (arg1) {
				case 2:
//					LOGD("AMOTION_EVENT_ACTION_POINTER_UP arg1: %d --> Right Mouse Button", arg1);
					//e.type = Common::EVENT_RBUTTONDOWN;
					//up = Common::EVENT_RBUTTONUP;
					multitype = Common::EVENT_RBUTTONUP;
					break;

				case 3:
//					LOGD("AMOTION_EVENT_ACTION_POINTER_UP arg1: %d --> Middle Mouse Button", arg1);
					//e.type = Common::EVENT_MBUTTONDOWN;
					//up = Common::EVENT_MBUTTONUP;
					multitype = Common::EVENT_MBUTTONUP;
					break;

				default:
					LOGE("AMOTION_EVENT_ACTION_POINTER_UP - unmapped multi tap (arg1): %d", arg1);
					return;
				}

//				e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();
//
//				_event_queue_lock->lock();
//
//				LOGD("AMOTION_EVENT_ACTION_POINTER_UP - _queuedEventTime %d ", _queuedEventTime);
//				if (_queuedEventTime) {
//					LOGD("AMOTION_EVENT_ACTION_POINTER_UP -pushing _queuedEvent (up) to event queue");
//					_event_queue.push(_queuedEvent);
//				}
//
//				_event_queue.push(e);
//
//				e.type = up;
//				_queuedEvent = e;
//				_queuedEventTime = getMillis() + kQueuedInputEventDelay;
//
//				_event_queue_lock->unlock();
				break;

			case AMOTION_EVENT_ACTION_MOVE:
				// TODO wrong event?
				// https://developer.android.com/training/gestures/movement
				// https://developer.android.com/training/gestures/multi
				//
//				LOGD("AMOTION_EVENT_ACTION_MOVE arg1: %d", arg1);
				// held and moved
				if (_touch_pt_multi.x == -1 && _touch_pt_multi.y == -1) {
					_touch_pt_multi.x = arg3;
					_touch_pt_multi.y = arg4;
					return;
				}

				multitype = Common::EVENT_MOUSEMOVE;

				// TODO TO TEST for non-touchpad mode too!
				if (_touch_mode == TOUCH_MODE_TOUCHPAD) {
					e.mouse.x = (arg3 - _touch_pt_multi.x) * 100 / _touchpad_scale;
					e.mouse.y = (arg4 - _touch_pt_multi.y) * 100 / _touchpad_scale;
					e.mouse += _touch_pt_down; // TODO maybe we need another reference point???
				}
				break;

			default:
				LOGE("JE_MULTI - unhandled jaction on multi tap: %d", arg2);
				return;
			}

			_event_queue_lock->lock();
			if (_queuedEventTime) {
//				// if another event is queued to be served by PollEvent (but not in _event_queue)
//				// at the time of this JE_MULTI event
//				// then push that pending _queuedEvent event to the queue for direct service
//				// This is done because we will push a new event to the queue and also might replace the _queuedEvent below
//				LOGD("JE_MULTI -pushing old _queuedEvent to event queue");
				_event_queue.push(_queuedEvent);
			}
//			if (_queuedEventTime && _queuedEvent.type != Common::EVENT_RBUTTONDOWN) {
//				// if another event is queued to be served by PollEvent (but not in _event_queue)
//				// and it is not Common::EVENT_RBUTTONDOWN (which is only created by the JE_MULTI event as a _queuedEvent)
//				// at the time of this JE_MULTI event
//				// then push that pending _queuedEvent event to the queue for direct service
//				// This is done because we will push a new event to the queue and also might replace the _queuedEvent below
//				LOGD("JE_MULTI -pushing old _queuedEvent to event queue");
//				_event_queue.push(_queuedEvent);
//			}

			// push the default move event
			_event_queue.push(e);
			e.type = multitype;

			if (e.type != Common::EVENT_INVALID) {
//				if (e.type == Common::EVENT_RBUTTONDOWN) {
//					_queuedEvent = e; // enqueue the Right Button DOWN    event with a 200 ms delay
//					_queuedEventTime = getMillis() + 200; // TODO add to constants like kQueuedInputEventDelay is
//				} else {
//					// TODO what if this is a quick Common::EVENT_RBUTTONUP?
//					if (_queuedEventTime && _queuedEvent.type == Common::EVENT_RBUTTONDOWN && e.type == Common::EVENT_RBUTTONUP) {
//						_event_queue.push(_queuedEvent);
//						_queuedEvent = e; // enqueue the Right Button UP event with a kQueuedInputEventDelay
//						_queuedEventTime = getMillis() + kQueuedInputEventDelay;
//					} else {
						_event_queue.push(e);
//					}
//				}
			}

			_event_queue_lock->unlock();
		}

		return;

	case JE_BALL:
		e.mouse = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();

		switch (arg1) {
		case AMOTION_EVENT_ACTION_DOWN:
			e.type = Common::EVENT_LBUTTONDOWN;
			break;

		case AMOTION_EVENT_ACTION_UP:
			e.type = Common::EVENT_LBUTTONUP;
			break;

		case AMOTION_EVENT_ACTION_MOVE:
			e.type = Common::EVENT_MOUSEMOVE;

			// already multiplied by 100
			e.mouse.x += arg2 * _trackball_scale / _eventScaleX;
			e.mouse.y += arg3 * _trackball_scale / _eventScaleY;

			break;

		default:
			LOGE("unhandled JE_BALL jaction on system key: %d", arg1);
			return;
		}

		pushEvent(e);

		return;

	case JE_MOUSE_MOVE:
		e.type = Common::EVENT_MOUSEMOVE;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_LMB_DOWN:
		e.type = Common::EVENT_LBUTTONDOWN;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_LMB_UP:
		e.type = Common::EVENT_LBUTTONUP;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_RMB_DOWN:
		e.type = Common::EVENT_RBUTTONDOWN;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_RMB_UP:
		e.type = Common::EVENT_RBUTTONUP;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_MMB_DOWN:
		e.type = Common::EVENT_MBUTTONDOWN;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_MMB_UP:
		e.type = Common::EVENT_MBUTTONUP;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_BMB_DOWN:
		e.type = Common::EVENT_X1BUTTONDOWN;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_BMB_UP:
		e.type = Common::EVENT_X1BUTTONUP;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_FMB_DOWN:
		e.type = Common::EVENT_X2BUTTONDOWN;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_FMB_UP:
		e.type = Common::EVENT_X2BUTTONUP;
		e.mouse.x = arg1;
		e.mouse.y = arg2;

		pushEvent(e);

		return;

	case JE_GAMEPAD:
		switch (arg1) {
		case AKEY_EVENT_ACTION_DOWN:
			e.type = Common::EVENT_JOYBUTTON_DOWN;
			break;
		case AKEY_EVENT_ACTION_UP:
			e.type = Common::EVENT_JOYBUTTON_UP;
			break;
		default:
			LOGE("unhandled jaction on gamepad key: %d", arg1);
			return;
		}

		switch (arg2) {
		case AKEYCODE_BUTTON_START:
			e.joystick.button = Common::JOYSTICK_BUTTON_START;
			break;

		case AKEYCODE_BUTTON_SELECT:
			e.joystick.button = Common::JOYSTICK_BUTTON_BACK;
			break;

		case AKEYCODE_BUTTON_MODE:
			e.joystick.button = Common::JOYSTICK_BUTTON_GUIDE;
			break;

		case AKEYCODE_BUTTON_A:
			e.joystick.button = Common::JOYSTICK_BUTTON_A;
			break;

		case AKEYCODE_BUTTON_B:
			e.joystick.button = Common::JOYSTICK_BUTTON_B;
			break;

		case AKEYCODE_BUTTON_X:
			e.joystick.button = Common::JOYSTICK_BUTTON_X;
			break;

		case AKEYCODE_BUTTON_Y:
			e.joystick.button = Common::JOYSTICK_BUTTON_Y;
			break;

		case AKEYCODE_BUTTON_L1:
			e.joystick.button = Common::JOYSTICK_BUTTON_LEFT_SHOULDER;
			break;

		case AKEYCODE_BUTTON_R1:
			e.joystick.button = Common::JOYSTICK_BUTTON_RIGHT_SHOULDER;
			break;

		case AKEYCODE_BUTTON_THUMBL:
			e.joystick.button = Common::JOYSTICK_BUTTON_LEFT_STICK;
			break;

		case AKEYCODE_BUTTON_THUMBR:
			e.joystick.button = Common::JOYSTICK_BUTTON_RIGHT_STICK;
			break;

		default:
			LOGW("unmapped gamepad key: %d", arg2);
			return;
		}

		pushEvent(e);

		break;

	case JE_JOYSTICK:

		switch (arg1) {
		// AMOTION_EVENT_ACTION_MOVE is 2 in NDK (https://developer.android.com/ndk/reference/group/input)
		case AMOTION_EVENT_ACTION_MOVE:
			e.type = Common::EVENT_JOYAXIS_MOTION;

			e.joystick.axis = Common::JOYSTICK_AXIS_LEFT_STICK_X;
			e.joystick.position = CLIP<int32>(arg2, Common::JOYAXIS_MIN, Common::JOYAXIS_MAX);
			pushEvent(e);

			e.joystick.axis = Common::JOYSTICK_AXIS_LEFT_STICK_Y;
			e.joystick.position = CLIP<int32>(arg3, Common::JOYAXIS_MIN, Common::JOYAXIS_MAX);
			pushEvent(e);

			break;
		case AKEY_EVENT_ACTION_DOWN:
			e.type = Common::EVENT_JOYBUTTON_DOWN;
			break;
		case AKEY_EVENT_ACTION_UP:
			e.type = Common::EVENT_JOYBUTTON_UP;
			break;
		default:
			LOGE("unhandled jaction on joystick: %d", arg1);
			return;
		}

		if (arg1 != AMOTION_EVENT_ACTION_MOVE) {
			switch (arg2) {
			case AKEYCODE_BUTTON_1:
				e.joystick.button = Common::JOYSTICK_BUTTON_A;
				break;

			case AKEYCODE_BUTTON_2:
				e.joystick.button = Common::JOYSTICK_BUTTON_B;
				break;

			case AKEYCODE_BUTTON_3:
				e.joystick.button = Common::JOYSTICK_BUTTON_X;
				break;

			case AKEYCODE_BUTTON_4:
				e.joystick.button = Common::JOYSTICK_BUTTON_Y;
				break;

			default:
				LOGW("unmapped gamepad key: %d", arg2);
				return;
			}

			pushEvent(e);
		}

		return;

	case JE_QUIT:
		e.type = Common::EVENT_QUIT;

		pushEvent(e);

		return;

	case JE_MENU:
		e.type = Common::EVENT_MAINMENU;

		pushEvent(e);

		return;

	default:
		LOGE("unknown jevent type: %d", type);

		break;
	}
}

bool OSystem_Android::pollEvent(Common::Event &event) {
	//ENTER();

	if (pthread_self() == _main_thread) {
		if (_screen_changeid != JNI::surface_changeid) {
			_screen_changeid = JNI::surface_changeid;

			if (JNI::egl_surface_width > 0 && JNI::egl_surface_height > 0) {
				// surface changed
				dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->deinitSurface();
				dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->initSurface();

				event.type = Common::EVENT_SCREEN_CHANGED;

				return true;
			} else {
				// surface lost
				dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->deinitSurface();
			}
		}

		if (JNI::pause) {
			LOGD("main thread going to sleep");
			sem_wait(&JNI::pause_sem);
			LOGD("main thread woke up");
		}
	}

	_event_queue_lock->lock();

	if (_queuedEventTime && (getMillis() > _queuedEventTime)) {
//		if (_queuedEvent.type == Common::EVENT_RBUTTONDOWN) {
//			LOGD("Executing delayed RIGHT MOUSE DOWN!!!!");
//		} else if (_queuedEvent.type == Common::EVENT_RBUTTONUP) {
//			LOGD("Executing delayed RIGHT MOUSE UP!!!!");
//		}
		event = _queuedEvent;
		_queuedEventTime = 0;
		// _event_queue_lock->unlock();
		// return true;
	} else if (_event_queue.empty()) {
		_event_queue_lock->unlock();
		return false;

	} else {
		event = _event_queue.pop();
	}
	_event_queue_lock->unlock();

	if (Common::isMouseEvent(event)) {
		if (_graphicsManager)
			return dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->notifyMousePosition(event.mouse);
	}

	return true;
}

void OSystem_Android::pushEvent(const Common::Event &event) {
	_event_queue_lock->lock();
	_event_queue.push(event);
	_event_queue_lock->unlock();
}

void OSystem_Android::pushEvent(const Common::Event &event1, const Common::Event &event2) {
	_event_queue_lock->lock();
	_event_queue.push(event1);
	_event_queue.push(event2);
	_event_queue_lock->unlock();
}

void OSystem_Android::setupTouchMode(int oldValue, int newValue) {
	_touch_mode = newValue;

	if (newValue == TOUCH_MODE_TOUCHPAD) {
		// Make sure we have a proper touch point if we switch to touchpad mode with finger down
		_touch_pt_down = dynamic_cast<AndroidCommonGraphics *>(_graphicsManager)->getMousePosition();
		_touch_pt_scroll.x = -1;
		_touch_pt_scroll.y = -1;
	}
}

#endif
