// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
package rdpclient.adapter;

import java.awt.event.KeyEvent;

import streamer.BaseElement;
import streamer.ByteBuffer;
import streamer.Link;
import common.KeyOrder;

public class AwtRdpKeyboardAdapter extends BaseElement {

    /**
     * Absence of this flag indicates a key-down event, while its presence
     * indicates a key-release event.
     */
    public static final int FASTPATH_INPUT_KBDFLAGS_RELEASE = 0x01;

    /**
     * Keystroke message contains an extended scancode. For enhanced 101-key and
     * 102-key keyboards, extended keys include the right ALT and right CTRL keys
     * on the main section of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE
     * DOWN and ARROW keys in the clusters to the left of the numeric keypad; and
     * the Divide ("/") and ENTER keys in the numeric keypad.
     */
    public static final int FASTPATH_INPUT_KBDFLAGS_EXTENDED = 0x02;

    public static final int FASTPATH_INPUT_EVENT_SCANCODE = 0;

    public AwtRdpKeyboardAdapter(String id) {
        super(id);
    }

    @Override
    public void handleData(ByteBuffer buf, Link link) {
        if (verbose)
            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");

        KeyOrder order = (KeyOrder)buf.getOrder();
        buf.unref();

        ByteBuffer outBuf = new ByteBuffer(2, true);

        int scanCode = map_en_us(order.event);

        // eventHeader (1 byte): An 8-bit, unsigned integer. The format of this
        // field is the same as the eventHeader byte field described in section
        // 2.2.8.1.2.2. The eventCode bitfield (3 bits in size) MUST be set to
        // FASTPATH_INPUT_EVENT_SCANCODE (0). The eventFlags bitfield (5 bits in
        // size) contains flags describing the keyboard event.
        outBuf.writeByte((scanCode >> 8) | (FASTPATH_INPUT_EVENT_SCANCODE << 5) | ((order.pressed) ? 0 : FASTPATH_INPUT_KBDFLAGS_RELEASE));

        // keyCode (1 byte): An 8-bit, unsigned integer. The scancode of the key
        // which triggered the event.
        outBuf.writeByte(scanCode);

        // Push buffer to one pad only, so it can be modified without copying of
        // data
        pushDataToPad(STDOUT, outBuf);
    }

    /**
     * Return key scan code (in lower byte) and extended flags (in second byte).
     */
    private int map_en_us(KeyEvent event) {
        // Also set extended key flag when necessary.
        // For enhanced 101-key and 102-key keyboards, extended keys include the
        // right ALT and right CTRL keys on the main section of the keyboard; the
        // INS, DEL, HOME, END, PAGE UP, PAGE DOWN and ARROW keys in the clusters to
        // the left of the numeric keypad; and the Divide ("/") and ENTER keys in
        // the numeric keypad.

        switch (event.getKeyCode()) {
        // Functional keys
        case KeyEvent.VK_ESCAPE:
            return 1;
        case KeyEvent.VK_F1:
            return 59;
        case KeyEvent.VK_F2:
            return 60;
        case KeyEvent.VK_F3:
            return 61;
        case KeyEvent.VK_F4:
            return 62;
        case KeyEvent.VK_F5:
            return 63;
        case KeyEvent.VK_F6:
            return 64;
        case KeyEvent.VK_F7:
            return 65;
        case KeyEvent.VK_F8:
            return 66;
        case KeyEvent.VK_F9:
            return 67;
        case KeyEvent.VK_F10:
            return 68;
        case KeyEvent.VK_F11:
            return 87;
        case KeyEvent.VK_F12:
            return 88;

            // Row #1
        case KeyEvent.VK_BACK_QUOTE:
            return 41;
        case KeyEvent.VK_1:
            return 2;
        case KeyEvent.VK_2:
            return 3;
        case KeyEvent.VK_3:
            return 4;
        case KeyEvent.VK_4:
            return 5;
        case KeyEvent.VK_5:
            return 6;
        case KeyEvent.VK_6:
            return 7;
        case KeyEvent.VK_7:
            return 8;
        case KeyEvent.VK_8:
            return 9;
        case KeyEvent.VK_9:
            return 10;
        case KeyEvent.VK_0:
            return 11;
        case KeyEvent.VK_MINUS:
            return 12;
        case KeyEvent.VK_EQUALS:
            return 13;
        case KeyEvent.VK_BACK_SPACE:
            return 14;

            // Row #2
        case KeyEvent.VK_TAB:
            return 15;
        case KeyEvent.VK_Q:
            return 16;
        case KeyEvent.VK_W:
            return 17;
        case KeyEvent.VK_E:
            return 18;
        case KeyEvent.VK_R:
            return 19;
        case KeyEvent.VK_T:
            return 20;
        case KeyEvent.VK_Y:
            return 21;
        case KeyEvent.VK_U:
            return 22;
        case KeyEvent.VK_I:
            return 23;
        case KeyEvent.VK_O:
            return 24;
        case KeyEvent.VK_P:
            return 25;
        case KeyEvent.VK_OPEN_BRACKET:
            return 26;
        case KeyEvent.VK_CLOSE_BRACKET:
            return 27;
        case KeyEvent.VK_ENTER:
            switch (event.getKeyLocation()) {
            default:
            case KeyEvent.KEY_LOCATION_STANDARD:
                return 28;
            case KeyEvent.KEY_LOCATION_NUMPAD:
                return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 28;
            }

            // Row #3
        case KeyEvent.VK_CAPS_LOCK:
            return 58;
        case KeyEvent.VK_A:
            return 30;
        case KeyEvent.VK_S:
            return 31;
        case KeyEvent.VK_D:
            return 32;
        case KeyEvent.VK_F:
            return 33;
        case KeyEvent.VK_G:
            return 34;
        case KeyEvent.VK_H:
            return 35;
        case KeyEvent.VK_J:
            return 36;
        case KeyEvent.VK_K:
            return 37;
        case KeyEvent.VK_L:
            return 38;
        case KeyEvent.VK_SEMICOLON:
            return 39;
        case KeyEvent.VK_QUOTE:
            return 40;

            // Row #4
        case KeyEvent.VK_SHIFT:
            switch (event.getKeyLocation()) {
            default:
            case KeyEvent.KEY_LOCATION_LEFT:
                return 42;
            case KeyEvent.KEY_LOCATION_RIGHT:
                return 54;
            }
        case KeyEvent.VK_BACK_SLASH:
            return 43;
        case KeyEvent.VK_Z:
            return 44;
        case KeyEvent.VK_X:
            return 45;
        case KeyEvent.VK_C:
            return 46;
        case KeyEvent.VK_V:
            return 47;
        case KeyEvent.VK_B:
            return 48;
        case KeyEvent.VK_N:
            return 49;
        case KeyEvent.VK_M:
            return 50;
        case KeyEvent.VK_COMMA:
            return 51;
        case KeyEvent.VK_PERIOD:
            return 52;
        case KeyEvent.VK_SLASH:
            return 53;

            //
            // Bottom row
        case KeyEvent.VK_CONTROL:
            switch (event.getKeyLocation()) {
            default:
            case KeyEvent.KEY_LOCATION_LEFT:
                return 29;
            case KeyEvent.KEY_LOCATION_RIGHT:
                return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 29;
            }
        case KeyEvent.VK_WINDOWS:
            switch (event.getKeyLocation()) {
            default:
            case KeyEvent.KEY_LOCATION_LEFT:
                return 91;
            case KeyEvent.KEY_LOCATION_RIGHT:
                return 92;
            }
        case KeyEvent.VK_ALT:
            switch (event.getKeyLocation()) {
            default:
            case KeyEvent.KEY_LOCATION_LEFT:
                return 56;
            case KeyEvent.KEY_LOCATION_RIGHT:
                return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56;
            }
        case KeyEvent.VK_ALT_GRAPH:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56;

        case KeyEvent.VK_SPACE:
            return 57;

        case KeyEvent.VK_CONTEXT_MENU:
            return 93;

            //
            // Special keys
        case KeyEvent.VK_PRINTSCREEN:
            return 55;
        case KeyEvent.VK_SCROLL_LOCK:
            return 70;
        case KeyEvent.VK_PAUSE:
            return 29;

            // Text navigation keys
        case KeyEvent.VK_INSERT:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 82;
        case KeyEvent.VK_HOME:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 71;
        case KeyEvent.VK_PAGE_UP:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 73;
        case KeyEvent.VK_DELETE:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 83;
        case KeyEvent.VK_END:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 79;
        case KeyEvent.VK_PAGE_DOWN:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 81;

            // Cursor keys
        case KeyEvent.VK_UP:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 72;
        case KeyEvent.VK_LEFT:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 75;
        case KeyEvent.VK_DOWN:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 80;
        case KeyEvent.VK_RIGHT:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 77;

            // Keypad
        case KeyEvent.VK_NUM_LOCK:
            return 69;
        case KeyEvent.VK_DIVIDE:
            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 53;
        case KeyEvent.VK_MULTIPLY:
            return 55;
        case KeyEvent.VK_SUBTRACT:
            return 74;
        case KeyEvent.VK_ADD:
            return 78;

        case KeyEvent.VK_NUMPAD7:
            return 71;
        case KeyEvent.VK_NUMPAD8:
            return 72;
        case KeyEvent.VK_NUMPAD9:
            return 73;
        case KeyEvent.VK_NUMPAD4:
            return 75;
        case KeyEvent.VK_NUMPAD5:
            return 76;
        case KeyEvent.VK_NUMPAD6:
            return 77;
        case KeyEvent.VK_NUMPAD1:
            return 79;
        case KeyEvent.VK_NUMPAD2:
            return 80;
        case KeyEvent.VK_NUMPAD3:
            return 81;
        case KeyEvent.VK_NUMPAD0:
            return 82;
        case KeyEvent.VK_DECIMAL:
            return 83;

        default:
            System.err.println("Key is not mapped: " + event + ".");
            return event.getKeyCode();
        }
    }

}
