/*
 * barrier -- mouse and keyboard sharing utility
 * Copyright (C) 2016 Symless Ltd.
 *
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 *
 * This package 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/>.
 */

#include "platform/IOSXKeyResource.h"

#include <Carbon/Carbon.h>

KeyID
IOSXKeyResource::getKeyID(UInt8 c)
{
    if (c == 0) {
        return kKeyNone;
    }
    else if (c >= 32 && c < 127) {
        // ASCII
        return static_cast<KeyID>(c);
    }
    else {
        // handle special keys
        switch (c) {
        case 0x01:
            return kKeyHome;

        case 0x02:
            return kKeyKP_Enter;

        case 0x03:
            return kKeyKP_Enter;

        case 0x04:
            return kKeyEnd;

        case 0x05:
            return kKeyHelp;

        case 0x08:
            return kKeyBackSpace;

        case 0x09:
            return kKeyTab;

        case 0x0b:
            return kKeyPageUp;

        case 0x0c:
            return kKeyPageDown;

        case 0x0d:
            return kKeyReturn;

        case 0x10:
            // OS X maps all the function keys (F1, etc) to this one key.
            // we can't determine the right key here so we have to do it
            // some other way.
            return kKeyNone;

        case 0x1b:
            return kKeyEscape;

        case 0x1c:
            return kKeyLeft;

        case 0x1d:
            return kKeyRight;

        case 0x1e:
            return kKeyUp;

        case 0x1f:
            return kKeyDown;

        case 0x7f:
            return kKeyDelete;

        case 0x06:
        case 0x07:
        case 0x0a:
        case 0x0e:
        case 0x0f:
        case 0x11:
        case 0x12:
        case 0x13:
        case 0x14:
        case 0x15:
        case 0x16:
        case 0x17:
        case 0x18:
        case 0x19:
        case 0x1a:
            // discard other control characters
            return kKeyNone;

        default:
            // not special or unknown
            break;
        }

        // create string with character
        char str[2];
        str[0] = static_cast<char>(c);
        str[1] = 0;

        // get current keyboard script
        TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource();
        CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages);
        CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding(
                                        (CFStringRef)CFArrayGetValueAtIndex(langs, 0));
        // convert to unicode
        CFStringRef cfString =
            CFStringCreateWithCStringNoCopy(
                kCFAllocatorDefault, str, encoding, kCFAllocatorNull);

        // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean
        // encoding with char value 214).  if it did then make no key,
        // otherwise CFStringCreateMutableCopy() will crash.
        if (cfString == NULL) {
            return kKeyNone;
        }

        // convert to precomposed
        CFMutableStringRef mcfString =
            CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString);
        CFRelease(cfString);
        CFStringNormalize(mcfString, kCFStringNormalizationFormC);

        // check result
        int unicodeLength = CFStringGetLength(mcfString);
        if (unicodeLength == 0) {
            CFRelease(mcfString);
            return kKeyNone;
        }
        if (unicodeLength > 1) {
            // FIXME -- more than one character, we should handle this
            CFRelease(mcfString);
            return kKeyNone;
        }

        // get unicode character
        UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0);
        CFRelease(mcfString);

        // convert to KeyID
        return static_cast<KeyID>(uc);
    }
}

KeyID
IOSXKeyResource::unicharToKeyID(UniChar c)
{
    switch (c) {
    case 3:
        return kKeyKP_Enter;

    case 8:
        return kKeyBackSpace;

    case 9:
        return kKeyTab;

    case 13:
        return kKeyReturn;

    case 27:
        return kKeyEscape;

    case 127:
        return kKeyDelete;

    default:
        if (c < 32) {
            return kKeyNone;
        }
        return static_cast<KeyID>(c);
    }
}
