package de.ugoe.cs.tasktree.keyboardmaps;

import java.awt.event.KeyEvent;

/**
 * <p>
 * Enumeration of virtual keys.
 * </p>
 * 
 * @version 1.0
 * @author Patrick Harms
 */
public enum VirtualKey {

    ENTER(KeyEvent.VK_ENTER),
    BACK_SPACE(KeyEvent.VK_BACK_SPACE),
    TAB(KeyEvent.VK_TAB),
    CANCEL(KeyEvent.VK_CANCEL),
    CLEAR(KeyEvent.VK_CLEAR),
    SHIFT(KeyEvent.VK_SHIFT),
    CONTROL(KeyEvent.VK_CONTROL),
    ALT(KeyEvent.VK_ALT),
    PAUSE(KeyEvent.VK_PAUSE),
    CAPS_LOCK(KeyEvent.VK_CAPS_LOCK),
    ESCAPE(KeyEvent.VK_ESCAPE),
    SPACE(KeyEvent.VK_SPACE),
    PAGE_UP(KeyEvent.VK_PAGE_UP),
    PAGE_DOWN(KeyEvent.VK_PAGE_DOWN),
    END(KeyEvent.VK_END),
    HOME(KeyEvent.VK_HOME),

    LEFT(KeyEvent.VK_LEFT),
    UP(KeyEvent.VK_UP),
    RIGHT(KeyEvent.VK_RIGHT),
    DOWN(KeyEvent.VK_DOWN),

    COMMA(KeyEvent.VK_COMMA),
    MINUS(KeyEvent.VK_MINUS),
    PERIOD(KeyEvent.VK_PERIOD),
    SLASH(KeyEvent.VK_SLASH),

    DIGIT_0(KeyEvent.VK_0),
    DIGIT_1(KeyEvent.VK_1),
    DIGIT_2(KeyEvent.VK_2),
    DIGIT_3(KeyEvent.VK_3),
    DIGIT_4(KeyEvent.VK_4),
    DIGIT_5(KeyEvent.VK_5),
    DIGIT_6(KeyEvent.VK_6),
    DIGIT_7(KeyEvent.VK_7),
    DIGIT_8(KeyEvent.VK_8),
    DIGIT_9(KeyEvent.VK_9),

    SEMICOLON(KeyEvent.VK_SEMICOLON),
    EQUALS(KeyEvent.VK_EQUALS),

    LETTER_A(KeyEvent.VK_A),
    LETTER_B(KeyEvent.VK_B),
    LETTER_C(KeyEvent.VK_C),
    LETTER_D(KeyEvent.VK_D),
    LETTER_E(KeyEvent.VK_E),
    LETTER_F(KeyEvent.VK_F),
    LETTER_G(KeyEvent.VK_G),
    LETTER_H(KeyEvent.VK_H),
    LETTER_I(KeyEvent.VK_I),
    LETTER_J(KeyEvent.VK_J),
    LETTER_K(KeyEvent.VK_K),
    LETTER_L(KeyEvent.VK_L),
    LETTER_M(KeyEvent.VK_M),
    LETTER_N(KeyEvent.VK_N),
    LETTER_O(KeyEvent.VK_O),
    LETTER_P(KeyEvent.VK_P),
    LETTER_Q(KeyEvent.VK_Q),
    LETTER_R(KeyEvent.VK_R),
    LETTER_S(KeyEvent.VK_S),
    LETTER_T(KeyEvent.VK_T),
    LETTER_U(KeyEvent.VK_U),
    LETTER_V(KeyEvent.VK_V),
    LETTER_W(KeyEvent.VK_W),
    LETTER_X(KeyEvent.VK_X),
    LETTER_Y(KeyEvent.VK_Y),
    LETTER_Z(KeyEvent.VK_Z),

    OPEN_BRACKET(KeyEvent.VK_OPEN_BRACKET),
    BACK_SLASH(KeyEvent.VK_BACK_SLASH),
    CLOSE_BRACKET(KeyEvent.VK_CLOSE_BRACKET),

    NUMPAD_0(KeyEvent.VK_NUMPAD0),
    NUMPAD_1(KeyEvent.VK_NUMPAD1),
    NUMPAD_2(KeyEvent.VK_NUMPAD2),
    NUMPAD_3(KeyEvent.VK_NUMPAD3),
    NUMPAD_4(KeyEvent.VK_NUMPAD4),
    NUMPAD_5(KeyEvent.VK_NUMPAD5),
    NUMPAD_6(KeyEvent.VK_NUMPAD6),
    NUMPAD_7(KeyEvent.VK_NUMPAD7),
    NUMPAD_8(KeyEvent.VK_NUMPAD8),
    NUMPAD_9(KeyEvent.VK_NUMPAD9),
    MULTIPLY(KeyEvent.VK_MULTIPLY),
    ADD(KeyEvent.VK_ADD),

    SEPARATOR(KeyEvent.VK_SEPARATOR),

    SUBTRACT(KeyEvent.VK_SUBTRACT),
    DECIMAL(KeyEvent.VK_DECIMAL),
    DIVIDE(KeyEvent.VK_DIVIDE),
    DELETE(KeyEvent.VK_DELETE),
    NUM_LOCK(KeyEvent.VK_NUM_LOCK),
    SCROLL_LOCK(KeyEvent.VK_SCROLL_LOCK),

    F1(KeyEvent.VK_F1),
    F2(KeyEvent.VK_F2),
    F3(KeyEvent.VK_F3),
    F4(KeyEvent.VK_F4),
    F5(KeyEvent.VK_F5),
    F6(KeyEvent.VK_F6),
    F7(KeyEvent.VK_F7),
    F8(KeyEvent.VK_F8),
    F9(KeyEvent.VK_F9),
    F10(KeyEvent.VK_F10),
    F11(KeyEvent.VK_F11),
    F12(KeyEvent.VK_F12),
    F13(KeyEvent.VK_F13),
    F14(KeyEvent.VK_F14),
    F15(KeyEvent.VK_F15),
    F16(KeyEvent.VK_F16),
    F17(KeyEvent.VK_F17),
    F18(KeyEvent.VK_F18),
    F19(KeyEvent.VK_F19),
    F20(KeyEvent.VK_F20),
    F21(KeyEvent.VK_F21),
    F22(KeyEvent.VK_F22),
    F23(KeyEvent.VK_F23),
    F24(KeyEvent.VK_F24),

    PRINTSCREEN(KeyEvent.VK_PRINTSCREEN),
    INSERT(KeyEvent.VK_INSERT),
    HELP(KeyEvent.VK_HELP),
    META(KeyEvent.VK_META),

    BACK_QUOTE(KeyEvent.VK_BACK_QUOTE),
    QUOTE(KeyEvent.VK_QUOTE),

    KP_UP(KeyEvent.VK_KP_UP),
    KP_DOWN(KeyEvent.VK_KP_DOWN),
    KP_LEFT(KeyEvent.VK_KP_LEFT),
    KP_RIGHT(KeyEvent.VK_KP_RIGHT),

    DEAD_GRAVE(KeyEvent.VK_DEAD_GRAVE),
    DEAD_ACUTE(KeyEvent.VK_DEAD_ACUTE),
    DEAD_CIRCUMFLEX(KeyEvent.VK_DEAD_CIRCUMFLEX),
    DEAD_TILDE(KeyEvent.VK_DEAD_TILDE),
    DEAD_MACRON(KeyEvent.VK_DEAD_MACRON),
    DEAD_BREVE(KeyEvent.VK_DEAD_BREVE),
    DEAD_ABOVEDOT(KeyEvent.VK_DEAD_ABOVEDOT),
    DEAD_DIAERESIS(KeyEvent.VK_DEAD_DIAERESIS),
    DEAD_ABOVERING(KeyEvent.VK_DEAD_ABOVERING),
    DEAD_DOUBLEACUTE(KeyEvent.VK_DEAD_DOUBLEACUTE),
    DEAD_CARON(KeyEvent.VK_DEAD_CARON),
    DEAD_CEDILLA(KeyEvent.VK_DEAD_CEDILLA),
    DEAD_OGONEK(KeyEvent.VK_DEAD_OGONEK),
    DEAD_IOTA(KeyEvent.VK_DEAD_IOTA),
    DEAD_VOICED_SOUND(KeyEvent.VK_DEAD_VOICED_SOUND),
    DEAD_SEMIVOICED_SOUND(KeyEvent.VK_DEAD_SEMIVOICED_SOUND),

    AMPERSAND(KeyEvent.VK_AMPERSAND),
    ASTERISK(KeyEvent.VK_ASTERISK),
    QUOTEDBL(KeyEvent.VK_QUOTEDBL),
    LESS(KeyEvent.VK_LESS),
    GREATER(KeyEvent.VK_GREATER),
    BRACELEFT(KeyEvent.VK_BRACELEFT),
    BRACERIGHT(KeyEvent.VK_BRACERIGHT),

    AT(KeyEvent.VK_AT),
    COLON(KeyEvent.VK_COLON),
    CIRCUMFLEX(KeyEvent.VK_CIRCUMFLEX),
    DOLLAR(KeyEvent.VK_DOLLAR),
    EURO_SIGN(KeyEvent.VK_EURO_SIGN),
    EXCLAMATION_MARK(KeyEvent.VK_EXCLAMATION_MARK),
    INVERTED_EXCLAMATION_MARK(KeyEvent.VK_INVERTED_EXCLAMATION_MARK),
    LEFT_PARENTHESIS(KeyEvent.VK_LEFT_PARENTHESIS),
    NUMBER_SIGN(KeyEvent.VK_NUMBER_SIGN),
    PLUS(KeyEvent.VK_PLUS),
    RIGHT_PARENTHESIS(KeyEvent.VK_RIGHT_PARENTHESIS),
    UNDERSCORE(KeyEvent.VK_UNDERSCORE),

    WINDOWS(KeyEvent.VK_WINDOWS),
    CONTEXT_MENU(KeyEvent.VK_CONTEXT_MENU),

    FINAL(KeyEvent.VK_FINAL),
    CONVERT(KeyEvent.VK_CONVERT),
    NONCONVERT(KeyEvent.VK_NONCONVERT),
    ACCEPT(KeyEvent.VK_ACCEPT),
    MODECHANGE(KeyEvent.VK_MODECHANGE),
    KANA(KeyEvent.VK_KANA),
    KANJI(KeyEvent.VK_KANJI),
    ALPHANUMERIC(KeyEvent.VK_ALPHANUMERIC),
    KATAKANA(KeyEvent.VK_KATAKANA),
    HIRAGANA(KeyEvent.VK_HIRAGANA),
    FULL_WIDTH(KeyEvent.VK_FULL_WIDTH),
    HALF_WIDTH(KeyEvent.VK_HALF_WIDTH),
    ROMAN_CHARACTERS(KeyEvent.VK_ROMAN_CHARACTERS),
    ALL_CANDIDATES(KeyEvent.VK_ALL_CANDIDATES),
    PREVIOUS_CANDIDATE(KeyEvent.VK_PREVIOUS_CANDIDATE),
    CODE_INPUT(KeyEvent.VK_CODE_INPUT),
    JAPANESE_KATAKANA(KeyEvent.VK_JAPANESE_KATAKANA),
    JAPANESE_HIRAGANA(KeyEvent.VK_JAPANESE_HIRAGANA),
    JAPANESE_ROMAN(KeyEvent.VK_JAPANESE_ROMAN),
    KANA_LOCK(KeyEvent.VK_KANA_LOCK),
    INPUT_METHOD_ON_OFF(KeyEvent.VK_INPUT_METHOD_ON_OFF),

    CUT(KeyEvent.VK_CUT),
    COPY(KeyEvent.VK_COPY),
    PASTE(KeyEvent.VK_PASTE),
    UNDO(KeyEvent.VK_UNDO),
    AGAIN(KeyEvent.VK_AGAIN),
    FIND(KeyEvent.VK_FIND),
    PROPS(KeyEvent.VK_PROPS),
    STOP(KeyEvent.VK_STOP),
    COMPOSE(KeyEvent.VK_COMPOSE),
    ALT_GRAPH(KeyEvent.VK_ALT_GRAPH),
    BEGIN(KeyEvent.VK_BEGIN),

    UNDEFINED(KeyEvent.VK_UNDEFINED);

    /*
     * BAR(KeyEvent.VK_UNDEFINED),
     * APOSTROPHE(KeyEvent.VK_UNDEFINED),
     * QUESTIONMARK(KeyEvent.VK_UNDEFINED),
     * DEGREE(KeyEvent.VK_UNDEFINED),
     * HENKAN_MODE(KeyEvent.VK_UNDEFINED),
     * MUHENKAN(KeyEvent.VK_UNDEFINED),
     * EISU_TOGGLE(KeyEvent.VK_UNDEFINED),
     * HANGUL(KeyEvent.VK_UNDEFINED),
     * HANGUL_HANJA(KeyEvent.VK_UNDEFINED),
     * EXECUTE(KeyEvent.VK_UNDEFINED);
     */

    /**
     * <p>
     * Virtual key code of the virtual key.
     * </p>
     */
    private int virtualKeyCode = -1;

    /**
     * <p>
     * Description of the virtual key.
     * </p>
     */
    private String description;

    /**
     * <p>
     * Constructor. Creates a new VirtualKey.
     * </p>
     * 
     * @param virtualKeyCode
     *            key code of the virtual key
     */
    private VirtualKey(int virtualKeyCode) {
        this.virtualKeyCode = virtualKeyCode;
        this.description = KeyEvent.getKeyText(this.virtualKeyCode);
    }

    /**
     * <p>
     * Returns the description of the virtual key.
     * </p>
     * 
     * @return the description.
     */
    String getDescription() {
        return description;
    }

    /**
     * <p>
     * Returns whether the key is a combination key (e.g., shift, alt) or not.
     * </p>
     * 
     * @return true, if the key is a combiniation key; false otherwise
     */
    public boolean isCombinationKey() {
        switch (this)
        {
            case SHIFT:
            case CONTROL:
            case ALT:
            case ALT_GRAPH:
            case WINDOWS:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is a lock key (e.g., caps lock, num lock) or not.
     * </p>
     * 
     * @return true, if the key is a lock key; false otherwise
     */
    public boolean isLockKey() {
        switch (this)
        {
            case CAPS_LOCK:
            case NUM_LOCK:
            case SCROLL_LOCK:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is shift.
     * </p>
     * 
     * @return true, if the key is shift; false otherwise
     */
    public boolean isShiftKey() {
        switch (this)
        {
            case SHIFT:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the is an alt key.
     * </p>
     * 
     * @return true, if the key is alt or altgr; false otherwise
     */
    public boolean isAltKey() {
        switch (this)
        {
            case ALT:
            case ALT_GRAPH:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is control.
     * </p>
     * 
     * @return true, if the key is control; false otherwise
     */
    public boolean isControlKey() {
        switch (this)
        {
            case CONTROL:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is the windows key.
     * </p>
     * 
     * @return true, if the key is the windows key; false otherwise
     */
    public boolean isWindowsKey() {
        switch (this)
        {
            case WINDOWS:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is the meta key.
     * </p>
     * 
     * @return true, if the key is the meta key; false otherwise
     */
    public boolean isMetaKey() {
        switch (this)
        {
            case META:
                return true;

            default:
                return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is a letter.
     * </p>
     * 
     * @return true, if the key is a letter; false otherwise
     */
    public boolean isLetter() {
        if (virtualKeyCode > -1) {
            return Character.isLetter((char) virtualKeyCode);
        }
        else {
            return false;
        }
    }

    /**
     * <p>
     * Returns whether the key is a digit.
     * </p>
     * 
     * @return true, if the key is a digit; false otherwise
     */
    public boolean isDigit() {
        if (virtualKeyCode > -1) {
            return Character.isDigit(virtualKeyCode);
        }
        else {
            return false;
        }
    }

    /**
     * <p>
     * Parses an {@link String} and returns the respective VirtualKey if possible.
     * </p>
     * 
     * @param numberString
     *            String representation of the virtual key
     * @return created VirtualKey
     * @throws IllegalArgumentException
     *             thrown if there is no VirtualKey that correlates to string
     */
    public static VirtualKey parseVirtualKey(String string) {
        int virtualKeyCode = Integer.parseInt(string);
        for (VirtualKey key1 : VirtualKey.values()) {
            if (key1.virtualKeyCode == virtualKeyCode) {
                return key1;
            }
        }

        throw new IllegalArgumentException("there is no virtual key with id " + string);
    }

    /**
     * <p>
     * Returns the VirtualKey associated with an integer.
     * </p>
     * 
     * @param number
     *            integer to which the according VirtualKey is returned
     * @return the VirtualKey
     * @throws IllegalArgumentException
     *             thrown if there is no VirtualKey that correlates to number
     */
    public static VirtualKey valueOf(int number) {
        for (VirtualKey virtualKey : VirtualKey.values()) {
            if (virtualKey.virtualKeyCode == number) {
                return virtualKey;
            }
        }

        throw new IllegalArgumentException("there is no virtual key with number " + number);
    }

}
