/*
 * The RISC OS Latin-1 alphabet against Unicode, both ways.
 *   Copyright RISC OS Developments 2019+, credited to the RISC OS One Project.
 */

#include "alphabet.h"

/*
    &80 to &9F.  Below that the alphabet is ASCII and above it it is
    Latin-1, both of which are already the code point; only this block
    differs, and it is the block Latin-1 spends on control codes.

    U+FFFD marks the six RISC OS leaves undefined.  It is deliberately not
    reversible: a server sending U+FFFD means a character it could not
    represent either, not one of these.
*/
static const unsigned short top[32] =
{
    0x20AC, 0x0174, 0x0175, 0xFFFD, 0xFFFD, 0x0176, 0x0177, 0xFFFD,
    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2026, 0x2122, 0x2030, 0x2022,
    0x2018, 0x2019, 0x2039, 0x203A, 0x201C, 0x201D, 0x201E, 0x2013,
    0x2014, 0x2212, 0x0152, 0x0153, 0x2020, 0x2021, 0xFB01, 0xFB02
};

unsigned int AlphabetToUnicode(unsigned int ch)
{
    ch &= 0xFF;
    if((ch >= 0x80) && (ch <= 0x9F))
        return top[ch - 0x80];
    return ch;
}

unsigned int AlphabetFromUnicode(unsigned int u)
{
    int i;

    if(u < 0x80)
        return u;
    /*
        U+0080-U+009F are the C1 controls.  They are NOT the RISC OS
        characters that share those byte values, and mapping them back
        would turn a control code into a Euro sign.
    */
    if(u <= 0x9F)
        return 0;
    if(u <= 0xFF)
        return u;
    for(i = 0; i < 32; i++)
        if((top[i] == u) && (top[i] != 0xFFFD))
            return (unsigned int) (0x80 + i);
    return 0;
}

/*
    Characters the alphabet has no room for.

    Most of Unicode is not in the alphabet and never can be, and a name
    carrying one of those used to arrive as an underscore: visible in a
    directory, and no longer the name the server knows, so the file could
    be seen and not opened.  Such a character is now written out as its
    UTF-16 value in hexadecimal, behind a marker, and read back the same
    way - so the name still says what it is, and still opens.

    The marker is a dollar, which is not an arbitrary choice.  The name at
    this level is in the form the server uses, and the layer above swaps a
    server's dollar for a RISC OS "<" and back again - it has done since
    long before any of this.  Writing the marker as a dollar here therefore
    shows up as "<ABCD" on the RISC OS side, which is the notation asked
    for, and it round-trips through that layer untouched rather than
    needing it changed.

    A character outside the basic plane arrives as two units, and each is
    written out separately: reading them back gives the pair again, so an
    emoji survives the trip even though no single escape could describe it.
*/
static const char hex[] = "0123456789ABCDEF";

int AlphabetEscape(unsigned int u, char *out)
{
    out[0] = '$';
    out[1] = hex[(u >> 12) & 0xF];
    out[2] = hex[(u >> 8) & 0xF];
    out[3] = hex[(u >> 4) & 0xF];
    out[4] = hex[u & 0xF];
    return ALPHABET_ESCAPE_LEN;
}

static int hex_digit(int c)
{
    if(c >= '0' && c <= '9') return c - '0';
    if(c >= 'A' && c <= 'F') return c - 'A' + 10;
    if(c >= 'a' && c <= 'f') return c - 'a' + 10;
    return -1;
}

int AlphabetIsHex(int c)
{
    return hex_digit(c) >= 0;
}

int AlphabetUnescape(const char *s, unsigned int *u)
{
    int a, b, c, d;

    if(s[0] != '$')
        return 0;
    if((a = hex_digit((unsigned char) s[1])) < 0) return 0;
    if((b = hex_digit((unsigned char) s[2])) < 0) return 0;
    if((c = hex_digit((unsigned char) s[3])) < 0) return 0;
    if((d = hex_digit((unsigned char) s[4])) < 0) return 0;
    *u = (unsigned int) ((a << 12) | (b << 8) | (c << 4) | d);
    return ALPHABET_ESCAPE_LEN;
}
