/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package malik.emulator.application;

import java.util.*;

public final class InputDevice extends Object
{
    public static final int NUM_LOCK    = 0x01;
    public static final int CAPS_LOCK   = 0x02;
    public static final int SCROLL_LOCK = 0x04;
    public static final int KEYBOARD_TYPE_NONE           = 0;
    public static final int KEYBOARD_TYPE_ALPHABETIC     = 1;
    public static final int KEYBOARD_TYPE_NON_ALPHABETIC = 2;
    public static final int SOURCE_CLASS_NONE      = 0x00;
    public static final int SOURCE_CLASS_BUTTON    = 0x01;
    public static final int SOURCE_CLASS_POINTER   = 0x02;
    public static final int SOURCE_CLASS_TRACKBALL = 0x04;
    public static final int SOURCE_CLASS_POSITION  = 0x08;
    public static final int SOURCE_CLASS_JOYSTICK  = 0x10;
    public static final int SOURCE_CLASS_MASK      = 0xff;
    public static final int SOURCE_UNKNOWN          = 0x00000000;
    public static final int SOURCE_ANY              = 0xffffff00;
    public static final int SOURCE_KEYBOARD         = 0x00000101;
    public static final int SOURCE_DPAD             = 0x00000201;
    public static final int SOURCE_GAMEPAD          = 0x00000401;
    public static final int SOURCE_TOUCHSCREEN      = 0x00001002;
    public static final int SOURCE_MOUSE            = 0x00002002;
    public static final int SOURCE_STYLUS           = 0x00004002;
    public static final int SOURCE_BLUETOOTH_STYLUS = 0x0000c002;
    public static final int SOURCE_TRACKBALL        = 0x00010004;
    public static final int SOURCE_MOUSE_RELATIVE   = 0x00020004;
    public static final int SOURCE_TOUCHPAD         = 0x00100008;
    public static final int SOURCE_JOYSTICK         = 0x01000010;
    public static final int SOURCE_HDMI             = 0x02000001;
    static final int ID_POINTING_STANDARD = 0x000d9038;
    static final int ID_KEYBOARD_STANDARD = 0x00080ae6;
    static final int ID_KEYBOARD_VIRTUAL  = 0x00096bac;

    private static int COUNT;
    private static InputDevice[] DEVICES;
    private static final Random ID_GENERATOR;

    static {
        long[] skeys = new long[] { 0x03ffc1ff081f2300L, 0x0fffefff27fffffeL, 0xfc00001f00030000L, 0x0000000078000001L };
        long[] vkeys = new long[] { 0x03ffc1ff081f2300L, 0xffffefff27fffffeL, 0xfc00003f000300ffL, 0x0000000078000001L };
        COUNT = 3;
        DEVICES = new InputDevice[] {
            new InputDevice(ID_POINTING_STANDARD, false, SOURCE_TOUCHSCREEN | SOURCE_MOUSE | SOURCE_STYLUS, KEYBOARD_TYPE_NONE, null, 0x0073, "Стандартное указывающее устройство"),
            new InputDevice(ID_KEYBOARD_STANDARD, false, SOURCE_KEYBOARD, KEYBOARD_TYPE_ALPHABETIC, skeys, 0, "Стандартная клавиатура"),
            new InputDevice(ID_KEYBOARD_VIRTUAL, true, SOURCE_KEYBOARD, KEYBOARD_TYPE_ALPHABETIC, vkeys, 0, "Виртуальная клавиатура")
        };
        ID_GENERATOR = new Random();
    }

    public static int getKeyboardLightStatus() {
        return (int) MalikSystem.syscall(0L, 0x0051);
    }

    public static int[] getIDs() {
        int len;
        int[] result = new int[len = COUNT];
        for(int i = len; i-- > 0; result[i] = DEVICES[i].id);
        return result;
    }

    public static InputDevice get(int id) {
        for(int i = COUNT; i-- > 0; )
        {
            InputDevice device;
            if(id == (device = DEVICES[i]).id) return device;
        }
        return null;
    }

    public static InputDevice createVirtual(int source, int keyboardType, long[] keys, int buttons, String name) {
        int id;
        InputDevice result;
        if(keyboardType < KEYBOARD_TYPE_NONE || keyboardType > KEYBOARD_TYPE_NON_ALPHABETIC)
        {
            throw new IllegalArgumentException("InputDevice.createVirtual: аргумент keyboardType имеет недопустимое значение.");
        }
        synchronized(ID_GENERATOR)
        {
            int len;
            InputDevice[] devices;
            do
            {
                id = ID_GENERATOR.nextInt(0x000fffff) + 1;
            } while(get(id) != null);
            result = new InputDevice(id, true, source, keyboardType, keys, buttons, name);
            if((len = COUNT) == (devices = DEVICES).length)
            {
                Array.copy(devices, 0, devices = new InputDevice[(len << 1) + 1], 0, len);
                DEVICES = devices;
            }
            devices[len++] = result;
            COUNT = len;
        }
        return result;
    }

    private final boolean virtual;
    private final int id;
    private final int source;
    private final int keyboardType;
    private final int buttons;
    private final long[] keys;
    private final String name;

    private InputDevice(int id, boolean virtual, int source, int keyboardType, long[] keys, int buttons, String name) {
        long[] thisKeys;
        this.virtual = virtual;
        this.id = id;
        this.source = source;
        this.keyboardType = keyboardType;
        this.buttons = buttons & 0x0000ffff;
        this.keys = thisKeys = new long[4];
        this.name = name == null || name.length() <= 0 ? "Безымянное виртуальное устройство" : name;
        if(keys != null)
        {
            int keysLength;
            Array.copy(keys, 0, thisKeys, 0, (keysLength = keys.length) < 4 ? keysLength : 4);
        }
    }

    public String toString() {
        return name;
    }

    public boolean isVirtual() {
        return virtual;
    }

    public boolean isEnabled() {
        return true;
    }

    public boolean hasMicrophone() {
        return false;
    }

    public boolean hasKey(int key) {
        return key >= 0x00 && key <= 0xff && (keys[key >> 6] & (1L << key)) != 0L;
    }

    public boolean hasButton(int button) {
        return button >= 0x00 && button <= 0x0f && (buttons & (1 << button)) != 0;
    }

    public boolean sourceSupported(int source) {
        return (this.source & source) == source;
    }

    public int getKeyboardType() {
        return keyboardType;
    }

    public int getSource() {
        return source;
    }

    public int getID() {
        return id;
    }

    public String getName() {
        return name;
    }
}
