/*
 * #%L
 * SciJava Common shared library for SciJava software.
 * %%
 * Copyright (C) 2009 - 2026 SciJava developers.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.input;

import java.util.HashMap;
import java.util.Map;

/**
 * A UI-independent enumeration of keyboard key codes.
 * 
 * @author Curtis Rueden
 */
public enum KeyCode {

	/** Enter (a.k.a. Return). */
	ENTER(0x0a),

	/** Backspace. */
	BACK_SPACE(0x08),

	/** Tab. */
	TAB(0x09),

	/** Cancel. */
	CANCEL(0x03),

	/** Clear. */
	CLEAR(0x0c),

	/** Shift (left or right). */
	SHIFT(0x10),

	/** Control (left or right). */
	CONTROL(0x11),

	/** Alt (left or right). */
	ALT(0x12),

	/** Pause. */
	PAUSE(0x13),

	/** Caps Lock. */
	CAPS_LOCK(0x14),

	/** Escape. */
	ESCAPE(0x1b),

	/** Space (' '). */
	SPACE(0x20),

	/** Page Up. */
	PAGE_UP(0x21),

	/** Page Down. */
	PAGE_DOWN(0x22),

	/** End. */
	END(0x23),

	/** Home. */
	HOME(0x24),

	/** Left arrow (non-numpad). */
	LEFT(0x25),

	/** Up arrow (non-numpad). */
	UP(0x26),

	/** Right arrow (non-numpad). */
	RIGHT(0x27),

	/** Down arrow (non-numpad). */
	DOWN(0x28),

	/** Comma (','). */
	COMMA(0x2c),

	/** Minus ('-'). */
	MINUS(0x2d),

	/** Period ('.'). */
	PERIOD(0x2e),

	/** Forward slash ('/'). */
	SLASH(0x2f),

	/** Zero ('0', non-numpad). */
	NUM0(0x30),

	/** One ('1', non-numpad). */
	NUM1(0x31),

	/** Two ('2', non-numpad). */
	NUM2(0x32),

	/** Three ('3', non-numpad). */
	NUM3(0x33),

	/** Four ('4', non-numpad). */
	NUM4(0x34),

	/** Five ('5', non-numpad). */
	NUM5(0x35),

	/** Six ('6', non-numpad). */
	NUM6(0x36),

	/** Seven ('7', non-numpad). */
	NUM7(0x37),

	/** Eight ('8', non-numpad). */
	NUM8(0x38),

	/** Nine ('9', non-numpad). */
	NUM9(0x39),

	/** Semicolon (';'). */
	SEMICOLON(0x3b),

	/** Equals ('='). */
	EQUALS(0x3d),

	/** The letter A. */
	A(0x41),

	/** The letter B. */
	B(0x42),

	/** The letter C. */
	C(0x43),

	/** The letter D. */
	D(0x44),

	/** The letter E. */
	E(0x45),

	/** The letter F. */
	F(0x46),

	/** The letter G. */
	G(0x47),

	/** The letter H. */
	H(0x48),

	/** The letter I. */
	I(0x49),

	/** The letter J. */
	J(0x4a),

	/** The letter K. */
	K(0x4b),

	/** The letter L. */
	L(0x4c),

	/** The letter M. */
	M(0x4d),

	/** The letter N. */
	N(0x4e),

	/** The letter O. */
	O(0x4f),

	/** The letter P. */
	P(0x50),

	/** The letter Q. */
	Q(0x51),

	/** The letter R. */
	R(0x52),

	/** The letter S. */
	S(0x53),

	/** The letter T. */
	T(0x54),

	/** The letter U. */
	U(0x55),

	/** The letter V. */
	V(0x56),

	/** The letter W. */
	W(0x57),

	/** The letter X. */
	X(0x58),

	/** The letter Y. */
	Y(0x59),

	/** The letter Z. */
	Z(0x5a),

	/** Left bracket ('['). */
	OPEN_BRACKET(0x5b),

	/** Backslash ('\\'). */
	BACK_SLASH(0x5c),

	/** Right bracket (']'). */
	CLOSE_BRACKET(0x5d),

	/** Zero ('0') on numeric keypad. */
	NUMPAD_0(0x60),

	/** One ('1') on numeric keypad. */
	NUMPAD_1(0x61),

	/** Two ('2') on numeric keypad. */
	NUMPAD_2(0x62),

	/** Three ('3') on numeric keypad. */
	NUMPAD_3(0x63),

	/** Four ('4') on numeric keypad. */
	NUMPAD_4(0x64),

	/** Five ('5') on numeric keypad. */
	NUMPAD_5(0x65),

	/** Six ('6') on numeric keypad. */
	NUMPAD_6(0x66),

	/** Seven ('7') on numeric keypad. */
	NUMPAD_7(0x67),

	/** Eight ('8') on numeric keypad. */
	NUMPAD_8(0x68),

	/** Nine ('9') on numeric keypad. */
	NUMPAD_9(0x69),

	/** Asterisk ('*') on numeric keypad. */
	NUMPAD_ASTERISK(0x6a),

	/** Plus ('+') on numeric keypad. */
	NUMPAD_PLUS(0x6b),

	NUMPAD_SEPARATOR(0x6c),

	/** Minus ('-') on numeric keypad. */
	NUMPAD_MINUS(0x6d),

	/** Period ('.') on numeric keypad. */
	NUMPAD_PERIOD(0x6e),

	/** Slash ('/') on numeric keypad. */
	NUMPAD_SLASH(0x6f),

	/** Delete (non-numpad). */
	DELETE(0x7f),

	/** Num Lock. */
	NUM_LOCK(0x90),

	/** Scroll Lock. */
	SCROLL_LOCK(0x91),

	/** F1. */
	F1(0x70),

	/** F2. */
	F2(0x71),

	/** F3. */
	F3(0x72),

	/** F4. */
	F4(0x73),

	/** F5. */
	F5(0x74),

	/** F6. */
	F6(0x75),

	/** F7. */
	F7(0x76),

	/** F8. */
	F8(0x77),

	/** F9. */
	F9(0x78),

	/** F10. */
	F10(0x79),

	/** F11. */
	F11(0x7a),

	/** F12. */
	F12(0x7b),

	/** F13. */
	F13(0xf000),

	/** F14. */
	F14(0xf001),

	/** F15. */
	F15(0xf002),

	/** F16. */
	F16(0xf003),

	/** F17. */
	F17(0xf004),

	/** F18 */
	F18(0xf005),

	/** F19. */
	F19(0xf006),

	/** F20. */
	F20(0xf007),

	/** F21. */
	F21(0xf008),

	/** F22. */
	F22(0xf009),

	/** F23. */
	F23(0xf00a),

	/** F24. */
	F24(0xf00b),

	/** Print Screen. */
	PRINTSCREEN(0x9a),

	/** Insert. */
	INSERT(0x9b),

	/** Help. */
	HELP(0x9c),

	/** Meta. */
	META(0x9d),

	/** Backquote ('`'). */
	BACK_QUOTE(0xc0),

	/** Single quote ('\''). */
	QUOTE(0xde),

	/** Up arrow on numeric keypad. */
	KP_UP(0xe0),

	/** Down arrow on numeric keypad. */
	KP_DOWN(0xe1),

	/** Left arrow on numeric keypad. */
	KP_LEFT(0xe2),

	/** Right arrow on numeric keypad. */
	KP_RIGHT(0xe3),

	/** TODO. */
	DEAD_GRAVE(0x80),

	/** TODO. */
	DEAD_ACUTE(0x81),

	/** TODO. */
	DEAD_CIRCUMFLEX(0x82),

	/** TODO. */
	DEAD_TILDE(0x83),

	/** TODO. */
	DEAD_MACRON(0x84),

	/** TODO. */
	DEAD_BREVE(0x85),

	/** TODO. */
	DEAD_ABOVEDOT(0x86),

	/** TODO. */
	DEAD_DIAERESIS(0x87),

	/** TODO. */
	DEAD_ABOVERING(0x88),

	/** TODO. */
	DEAD_DOUBLEACUTE(0x89),

	/** TODO. */
	DEAD_CARON(0x8a),

	/** TODO. */
	DEAD_CEDILLA(0x8b),

	/** TODO. */
	DEAD_OGONEK(0x8c),

	/** TODO. */
	DEAD_IOTA(0x8d),

	/** TODO. */
	DEAD_VOICED_SOUND(0x8e),

	/** TODO. */
	DEAD_SEMIVOICED_SOUND(0x8f),

	/** Ampersand ('&amp;'). */
	AMPERSAND(0x96),

	/** Asterisk ('*'). */
	ASTERISK(0x97),

	/** Double quote ('"'). */
	QUOTEDBL(0x98),

	/** Less than ('&lt;'). */
	LESS(0x99),

	/** Greater than ('&gt;'). */
	GREATER(0xa0),

	/** Left curly brace ('{'). */
	BRACELEFT(0xa1),

	/** Right curly brace ('}'). */
	BRACERIGHT(0xa2),

	/** At sign ('@'). */
	AT(0x0200),

	/** Colon (':'). */
	COLON(0x0201),

	/** Caret ('^'). */
	CIRCUMFLEX(0x0202),

	/** Dollar sign ('$'). */
	DOLLAR(0x0203),

	/** Euro sign. */
	EURO_SIGN(0x0204),

	/** Bang ('!'). */
	EXCLAMATION_MARK(0x0205),

	/** Inverted bang. */
	INVERTED_EXCLAMATION_MARK(0x0206),

	/** Left parenthesis ('('). */
	LEFT_PARENTHESIS(0x0207),

	/** Pound sign ('#'). */
	NUMBER_SIGN(0x0208),

	/** Plus ('+'). */
	PLUS(0x0209),

	/** Right parenthesis (')'). */
	RIGHT_PARENTHESIS(0x020a),

	/** Underscore ('_'). */
	UNDERSCORE(0x020b),

	/** Windows key (both left and right). */
	WINDOWS(0x020c),

	/** Windows Context Menu key. */
	CONTEXT_MENU(0x020d),

	FINAL(0x0018),

	/** Convert function key. */
	CONVERT(0x001c),

	/** Don't Convert function key. */
	NONCONVERT(0x001d),

	/** Accept or Commit function key. */
	ACCEPT(0x001e),

	MODECHANGE(0x001f),

	KANA(0x0015),

	KANJI(0x0019),

	/** Alphanumeric function key. */
	ALPHANUMERIC(0x00f0),

	/** Katakana function key. */
	KATAKANA(0x00f1),

	/** Hiragana function key. */
	HIRAGANA(0x00f2),

	/** Full-Width Characters function key. */
	FULL_WIDTH(0x00f3),

	/** Half-Width Characters function key. */
	HALF_WIDTH(0x00f4),

	/** Roman Characters function key. */
	ROMAN_CHARACTERS(0x00f5),

	/** All Candidates function key. */
	ALL_CANDIDATES(0x0100),

	/** Previous Candidate function key. */
	PREVIOUS_CANDIDATE(0x0101),

	/** Code Input function key. */
	CODE_INPUT(0x0102),

	/** Japanese-Katakana function key. */
	JAPANESE_KATAKANA(0x0103),

	/** Japanese-Hiragana function key. */
	JAPANESE_HIRAGANA(0x0104),

	/** Japanese-Roman function key. */
	JAPANESE_ROMAN(0x0105),

	/** Locking Kana function key. */
	KANA_LOCK(0x0106),

	/** Input method on/off key. */
	INPUT_METHOD_ON_OFF(0x0107),

	/** Cut (Sun keyboard). */
	CUT(0xffd1),

	/** Copy (Sun keyboard). */
	COPY(0xffcd),

	/** Paste (Sun keyboard). */
	PASTE(0xffcf),

	/** Undo (Sun keyboard). */
	UNDO(0xffcb),

	/** Again (Sun keyboard). */
	AGAIN(0xffc9),

	/** Find (Sun keyboard). */
	FIND(0xffd0),

	/** Props (Sun keyboard). */
	PROPS(0xffca),

	/** Stop (Sun keyboard). */
	STOP(0xffc8),

	/** Compose function key. */
	COMPOSE(0xff20),

	/** AltGraph function key. */
	ALT_GRAPH(0xff7e),

	/** Begin key. */
	BEGIN(0xff58),

	/** Unknown code. */
	UNDEFINED(0x0);

	private static final Map<Integer, KeyCode> CODES =
		new HashMap<>();

	private static final Map<String, KeyCode> NAMES =
		new HashMap<>();

	static {
		for (final KeyCode keyCode : values()) {
			CODES.put(keyCode.getCode(), keyCode);
			NAMES.put(keyCode.name(), keyCode);
		}
	}

	private int code;

	private KeyCode(final int code) {
		this.code = code;
	}

	/** Gets the key's numeric code. */
	public int getCode() {
		return code;
	}

	/**
	 * Gets the KeyCode corresponding to the given numeric code, or
	 * {@link #UNDEFINED} if no such code.
	 */
	public static KeyCode get(final int code) {
		final KeyCode keyCode = CODES.get(code);
		if (keyCode == null) return UNDEFINED;
		return keyCode;
	}

	/**
	 * Gets the KeyCode corresponding to the given character,
	 * or {@link #UNDEFINED} if no such code.
	 */
	public static KeyCode get(final char c) {
		switch (c) {
			case '\n': case '\r': return ENTER;
			case '\b': return BACK_SPACE;
			case '\t': return TAB;
			case 0x1b: return ESCAPE;
			case ' ': return SPACE;
			case ',': return COMMA;
			case '-': return MINUS;
			case '.': return PERIOD;
			case '/': return SLASH;
			case '0': return NUM0;
			case '1': return NUM1;
			case '2': return NUM2;
			case '3': return NUM3;
			case '4': return NUM4;
			case '5': return NUM5;
			case '6': return NUM6;
			case '7': return NUM7;
			case '8': return NUM8;
			case '9': return NUM9;
			case ';': return SEMICOLON;
			case '=': return EQUALS;
			case 'a': case 'A': return A;
			case 'b': case 'B': return B;
			case 'c': case 'C': return C;
			case 'd': case 'D': return D;
			case 'e': case 'E': return E;
			case 'f': case 'F': return F;
			case 'g': case 'G': return G;
			case 'h': case 'H': return H;
			case 'i': case 'I': return I;
			case 'j': case 'J': return J;
			case 'k': case 'K': return K;
			case 'l': case 'L': return L;
			case 'm': case 'M': return M;
			case 'n': case 'N': return N;
			case 'o': case 'O': return O;
			case 'p': case 'P': return P;
			case 'q': case 'Q': return Q;
			case 'r': case 'R': return R;
			case 's': case 'S': return S;
			case 't': case 'T': return T;
			case 'u': case 'U': return U;
			case 'v': case 'V': return V;
			case 'w': case 'W': return W;
			case 'x': case 'X': return X;
			case 'y': case 'Y': return Y;
			case 'z': case 'Z': return Z;
			case '[': return OPEN_BRACKET;
			case '\\': return BACK_SLASH;
			case ']': return CLOSE_BRACKET;
			case '`': return BACK_QUOTE;
			case '\'': return QUOTE;
			case '&': return AMPERSAND;
			case '*': return ASTERISK;
			case '"': return QUOTEDBL;
			case '<': return LESS;
			case '>': return GREATER;
			case '{': return BRACELEFT;
			case '}': return BRACERIGHT;
			case '@': return AT;
			case ':': return COLON;
			case '^': return CIRCUMFLEX;
			case '$': return DOLLAR;
			case '€': return EURO_SIGN;
			case '!': return EXCLAMATION_MARK;
			case 161: return INVERTED_EXCLAMATION_MARK;
			case '(': return LEFT_PARENTHESIS;
			case '#': return NUMBER_SIGN;
			case '+': return PLUS;
			case ')': return RIGHT_PARENTHESIS;
			case '_': return UNDERSCORE;
		}
		return UNDEFINED;
	}

	/**
	 * Gets the KeyCode with the given name, or {@link #UNDEFINED} if no such
	 * code.
	 */
	public static KeyCode get(final String name) {
		final KeyCode keyCode = NAMES.get(name);
		if (keyCode != null) return keyCode;
		// Not a code name, but maybe a direct character value?
		if (name.length() == 1) return KeyCode.get(name.charAt(0));
		return UNDEFINED;
	}

}
