#include "jellyfish.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define ISVOWEL(a) ((a) == 'A' || (a) == 'E' || (a) == 'I' || (a) == 'O' || (a) == 'U')

char *nysiis(const char *str) {
    size_t len = strlen(str);

    char c1, c2, c3;
    char *copy = alloca((len + 1) * sizeof(char));
    memcpy(copy, str, len+1);
    if (!copy) {
        return NULL;
    }

    if (!*copy) {
        return calloc(1, sizeof(char));
    }

    char *code = calloc(len + 1, sizeof(char));
    if (!code) {
        return NULL;
    }

    char *p, *cp;

    // Step 1
    if (!strncmp(copy, "MAC", 3)) {
        copy[1] = 'C';
    } else if (!strncmp(copy, "KN", 3)) {
        copy[0] = 'N';
    } else if (copy[0] == 'K') {
        copy[0] = 'C';
    } else if (!strncmp(copy, "PH", 2)) {
        copy[0] = 'F';
        copy[1] = 'F';
    } else if (!strncmp(copy, "SCH", 3)) {
        copy[1] = 'S';
        copy[2] = 'S';
    }

    // Step 2
    c1 = copy[len - 1];
    if (c1 == 'E') {
        c2 = copy[len - 2];
        if (c2 == 'E' || c2 == 'I') {
            copy[len - 1] = ' ';
            copy[len - 2] = 'Y';
        }
    } else if (c1 == 'T') {
        c2 = copy[len - 2];
        if (c2 == 'D' || c2 == 'R' || c2 == 'N') {
            copy[len - 1] = ' ';
            copy[len - 2] = 'D';
        }
    } else if (c1 == 'D') {
        c2 = copy[len - 2];
        if (c2 == 'R' || c2 == 'N') {
            copy[len - 1] = ' ';
            copy[len - 2] = 'D';
        }
    }

    cp = code;
    p = copy;

    // Step 3
    *(cp++) = toupper(*(p++));

    while ((c1 = toupper(*p))) {
        if (c1 == ' ') {
            break;
        }

        // Step 5
        switch(c1) {
        case 'E':
            if (toupper(*(p + 1)) == 'V') {
                *cp = 'A';
                *(++cp) = 'F';
                ++p;
                break;
            }
        case 'A':
        case 'I':
        case 'O':
        case 'U':
            *cp = 'A';
            break;
        case 'Q':
            *cp = 'G';
            break;
        case 'Z':
            *cp = 'S';
            break;
        case 'M':
            *cp = 'N';
            break;
        case 'K':
            if (toupper(*(p + 1)) == 'N') {
                *cp = 'N';
            } else {
                *cp = 'C';
            }
            break;
        case 'S':
            if (toupper(*(p + 1)) == 'C' && toupper(*(p + 2)) == 'H') {
                *(cp++) = 'S';
                *(cp++) = 'S';
                *cp = 'S';
                p += 2;
            } else {
                *cp = 'S';
            }
            break;
        case 'P':
            if (toupper(*(p + 1)) == 'H') {
                *cp = 'F';
                *(++cp) = 'F';
                p++;
            } else {
                *cp = 'P';
            }
            break;
        case 'H':
            c2 = toupper(*(p + 1));
            c3 = toupper(*(p - 1));
            if (!ISVOWEL(c2) || !ISVOWEL(c3)) {
                if ISVOWEL(c3) {
                    *cp = 'A';
                } else {
                    *cp = c3;
                }
            } else {
                *cp = 'H';
            }
            break;
        case 'W':
            c2 = toupper(*(p - 1));
            if (ISVOWEL(c2)) {
                *cp = c2;
            } else {
                *cp = 'W';
            }
            break;
        default:
            *cp = toupper(c1);
        }

        // Step 6
        if (*cp != *(cp - 1)) {
            cp++;
        }

        p++;
    }

    *cp = '\0';

    // Step 7
    c1 = *(cp - 1);
    if (c1 == 'S') {
        *(--cp) = '\0';
    } else if (c1 == 'Y') {
        if (*(cp - 2) == 'A') {
            *(--cp) = '\0';
            *(--cp) = 'Y';
        }
    }

    // There is no step 8!

    // Step 9
    if (*(cp - 1) == 'A') {
        *(cp - 1) = '\0';
    }

    return code;
}
