/***********************************************************************\

    Filename: inflect.c

\***********************************************************************/

#include "thesmisc.h"
#include <string.h>

#define VMASK   0x1104111
#define isvowel(c)        ((1 << (c - 'a')) & VMASK)        /* aeiouy */

static iplural();
static ido_ly();
static inormend();
static ido_ment();
static ido_able();
static alttry();
static ido_est();

static END *Infstgptr;

inflect(source, str, posflag, sp)
char   *source;           /* string to inflect */
char   *str;              /* inflected string */
int    posflag;           /* the word to be inflected is a noun or verb */
END    *sp;               /* inflection stage pointer. */
{
char   *endptr;
char   *tmpptr;
char   *start;
char   *endstr;
int    infindex;
short  unfrel;
char   past[MAXWORD];
char   pp[MAXWORD];
char   dummy[MAXWORD];
char   rtstr[MAXWORD];

    if (!(Infstgptr = sp))
        return (FALSE);
    endstr = (char *) Infstgptr->en_end;

    /* If the string is a phrase, it should be either a verb or a noun.
       If it is a verb, inflect the first word
       if it is a noun, inflect the last word.
       */

    unfrel = Infstgptr->en_rel;
    strecpy(str, source);
    if (unfrel == TP_SP || unfrel == TP_FP)
        return (TRUE);
    endptr = start = str;
    while (*endptr && *endptr != ' ')
        ++endptr;
    *rtstr = '\0';
    if (*endptr == ' ')
    {
        if ((infindex = unfexc(source, dummy, unfrel)) != ERROR)
        {
            if (infindex == 0)
                return (FALSE);
            strecpy(str, dummy);
            return (TRUE);
        }
        tmpptr = endptr++;

        switch (unfrel)
        {
            case AJ_NN:
            case VB_NN:
            case AJ_AV:
            case VB_AJ:
                return (FALSE);
            default:
                break;
        }

        if (posflag == VERB)
        {
            endptr = tmpptr;
            strecpy(rtstr, endptr);
            *endptr = '\0';
        }
        else if (posflag == NOUN)
        {
            str = tmpptr + 1;
            while (*endptr)
            {
                if (*endptr == ' ')
                    str = endptr + 1;
                ++endptr;
            }
        }
        else
            return (FALSE);
    }

    /* See if it is an exception. If we have a pluralizing relation
       take exceptions only if noun is indicated. */

    if (posflag == NOUN || unfrel != SI_PL)
    {
        if (unfrel == PR_PS || unfrel == PR_PP)
        {
            if ((infindex = unfexc(str, str, PR_PX)) > 0)
            {
                appndstr(str, rtstr, INFCODE);
                return (TRUE);
            }
        }
        if ((infindex = unfexc(str, str, unfrel)) != ERROR)
        {
            if (infindex == 0)
                return (FALSE);
            appndstr(str, rtstr, INFCODE);
            return (TRUE);
        }
        else if (unfrel == SV_PX)
        {
            unfrel = PR_PX;
            if ((infindex = unfexc(str, str, unfrel)) > 0)
            {
                appndstr(str, rtstr, INFCODE);
                return (TRUE);
            }
        }
        else if (unfrel == SP_PL)
        {
            unfrel = SI_PL;
            if ((infindex = unfexc(str, str, unfrel)) > 0)
            {
                appndstr(str, rtstr, INFCODE);
                return (TRUE);
            }
        }
    }

    /* Take care of the ambiguous past/past participle forms */

    if (unfrel == PR_PX)
    {
        /* Find the past tense */

        endptr = strecpy(past, str);
        if ((infindex = unfexc(past, dummy, PR_PS)) != ERROR)
        {
            if (infindex != 0)
                strecpy(past, dummy);
            appndstr(past, rtstr, INFCODE);
        }
        else
        {
            if (!inormend(past, endptr, endstr))
                return (FALSE);
            endptr = strecpy(str, past);
            appndstr(str, rtstr, INFCODE);
            if ((infindex = unfexc(str, dummy, UNF_IR)) == 0)
                return (FALSE);
            return (TRUE);
        }

        /* Find the past participle */

        endptr = strecpy(pp, str);
        if ((infindex = unfexc(pp, dummy, PR_PP)) > 0)
        {
            endptr = strecpy(pp, dummy);
            appndstr(pp, rtstr, INFCODE);
        }
        else
            strecpy(pp, past);

        /* If the past and pp forms are different
           construct <past>/<pp>. */

        if (strcmp(pp, past) != 0)
        {
            endptr = strecpy(str, past);
            *endptr++ = UNFSEP;
            strecpy(endptr, pp);
            return (TRUE);
        }
        strecpy (str, past);
        return (TRUE);
    }

    switch (Infstgptr->en_class)
    {
        case 0:                 /* s */
            if (!iplural(str, endptr, posflag))
                return (FALSE);
            break;
        case 1:
            if (!ido_ly(str, endptr, endstr))
                return (FALSE);
            break;
        case 2:
        case 3:
            if (unfrel == RE_CM || unfrel == RE_SU)     /* er/est */
            {
                if (!ido_est(str, endptr, endstr))
                    return (FALSE);
            }
            else if (!inormend(str, endptr, endstr))
                return (FALSE);
            break;
        case 4:
            if (!ido_ment(str, endptr, endstr))
                return (FALSE);
            break;
        case 5:
            if (!ido_able(str,endptr, "able") || strlen((char *) str) <
              6)
                return (FALSE);
            break;
        case 6:
        case 7:
        case 8:
        case 9:
            if (!iplural(str, endptr, posflag))
                return (FALSE);
            break;
        default:
            return (FALSE);
    }
    appndstr(str, rtstr, INFCODE);
    if ((infindex = unfexc(start, dummy, UNF_IR)) == 0)
        return (FALSE);
    return (TRUE);
}

static iplural(str, endptr, pos)
char *str;
char *endptr;
int     pos;
{
char   end1[MAXWORD];
char   end2[MAXWORD];

    switch (endptr[-1]) {

    case 'y':                       /* toy -> toys */
        *endptr = 's';
        endptr[1] = '\0';
        if (unftry(str)) {
            return (TRUE);
        }
        endptr[-1] = 'i';       /* candy -> candies */
        *endptr++ = 'e';
        break;

    case 'h':
        *endptr = 's';          /* bath -> baths */
        endptr[1] = '\0';
        if (unftry(str))
            return (TRUE);
        *endptr++ = 'e';        /* fish -> fishes */
        break;

    case 's':                                   /* crisis -> crises */
        if (endptr[-2] == 'i' && endptr[-3] == 's')
        {
            endptr[-2] = 'e';
            *endptr = '\0';
            if (unftry(str))
                return (TRUE);
            endptr[-2] = 'i';
        }
        if (endptr[-2] == 'u' && pos == NOUN) { /* focus -> foci */
            endptr[-2] = 'i';
            endptr[-1] = '\0';
            if (unftry(str))
                return (TRUE);
            endptr[-2] = 'u';
            endptr[-1] = 's';
        }
        strecpy(endptr, "ses");          /* gas -> gasses */
        if (unftry(str))
            return (TRUE);
        *endptr++ = 'e';
        break;

    case 'x':
        if ((endptr[-2] == 'i' || endptr[-2] == 'e') && pos == NOUN)
        {
            end1[0] = endptr[-2];
            strecpy(&end1[1], "xes");
            strecpy(end2, end1);
            end2[0] = 'i';
            end2[1] = 'c';
            return (alttry(str, endptr-2, end1, end2));

            /* vertex -> vertexes or vertices */
        }
        *endptr++ = 'e';                /* box -> boxes */
        *endptr++ = 's';
        *endptr = '\0';
        if (!unftry(str))
            *str = '\0';
        else
            return (TRUE);
    case 'u':                       /* plateau -> plateaus or plateaux*/
        end1[0] = 's';
        end2[0] = 'x';
        end1[1] = end2[1] = '\0';
        return (alttry(str, endptr, end1, end2));

    case 'z':
        strecpy(endptr, "zes");      /* quiz -> quizzes */
        if (unftry(str))
            return (TRUE);
        *endptr++ = 'e';                /* buzz -> buzzes */
        break;

    case 'f':                               /* puff -> puffs */
        if (endptr[-2] != 'f')      /* carafe -> carafes */
        {
            *endptr++ = 's';
            *endptr = '\0';
            if (unftry(str))
                return (TRUE);
            endptr -= 2;            /* loaf -> loaves */
            *endptr++ = 'v';
            *endptr++ = 'e';
        }
        break;

    case 'e':
        if (endptr[-3] == 'e' && endptr[-2] == 's')
            return (TRUE);
        *endptr = 's';
        endptr[1] = '\0';
        if (unftry(str))
            return (TRUE);
        if (endptr[-2] == 'f')      /* life -> lives */
        {
            endptr[-2] = 'v';
            if (unftry(str)) {
                return (TRUE);
            }
        }
        return (FALSE);

    case 'o':
        if (pos == VERB && !isvowel(endptr[-2]))
        {
            *endptr = 'e';
            endptr[1] = 's';
            endptr[2] = '\0';
            if (unftry(str))                /* do -> does */
                return (TRUE);
        }
        else
        {
            end1[0] = 'e';
            end1[1] = 's';
            end2[0] = 's';
            end1[2] = end2[1] = '\0';
            return (alttry(str, endptr, end1, end2));
        }
        break;

    case 'n':
        if (endptr[-2] == 'a' && endptr[-3] == 'm')
        {
            endptr[-2] = 'e';               /* man -> men */
            if (unftry(str))
                return (TRUE);
            endptr[-2] = 'a';               /* human -> humans */
        }
        break;

    case 'm':
        if (endptr[-2] == 'u' && pos == NOUN)
        {
            end1[0] = 'u';
            end1[1] = 'm';  /* medium -> mediums or media */
            end1[2] = 's';
            end2[0] = 'a';
            end1[3] = end2[1] = '\0';
            return (alttry(str, endptr-2, end1, end2));
        }
        break;

    default:
        break;
    }
    *endptr++ = 's';                        /* <word> -> <word>s */
    *endptr = '\0';
    return (unftry(str));
}

static ido_ly(str, endptr, ending)
char *str;
char *endptr;
char *ending;
{
    switch (endptr[-1])
    {

    case 'y':                               /* ready -> readily */
        endptr[-1] = 'i';
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
        endptr[-1] = 'y';       /* gay -> gayly */
        return (unftry(str));
    case 'l':                       /* accidental -> accidentally */
        if (endptr[-2] == 'l') {
            --endptr;
        }
        break;
    case 'e':
        if (endptr[-2] == 'l' && endptr[-3] == 'b') {
            endptr[-1] = 'y';               /* able -> ably */
            *endptr = '\0';
            if (unftry(str)) {
                return (TRUE);
            }
        }
        break;
    case 'c':
        strecpy(endptr, ending);        /* impolitic -> impoliticly */
        if (unftry(str)) {
            return (TRUE);
        }
        if (endptr[-2] == 'i' || endptr[-2] == 'a') {
            strecpy(endptr, "ally");
            if (unftry(str)) {     /* athletic -> athletically */
                return (TRUE);
            }
        }
        return (FALSE);
    default:
        break;
    }

    strecpy(endptr, ending);                 /* <word> -> <word>ly */
    return (unftry(str));
}

static inormend(str, endptr, ending)
 char *str;
 char *endptr;
char   *ending;
{

    char   end1[MAXWORD];          /* the two endings used to generate */
    char   end2[MAXWORD];          /* alternative forms */

    if (Infstgptr->en_rel == AJ_NN) {
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);                  /* busy -> busyness */
        }
        if (endptr[-1] == 'y') {
            endptr[-1] = 'i';
            return (unftry(str));
        }
        return (FALSE);
    }

    switch (endptr[-1]) {

    case 'y':
        endptr[-1] = 'i';
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
        endptr[-1] = 'y';                       /* cry -> cried */
        break;

    case 'e':
        if (Infstgptr->en_rel == PR_PC) {
            if (endptr[-2] == 'i') {
                endptr -= 2;
                strecpy(strecpy(end2, endptr),ending);
                end1[0] = 'y';
                strecpy(end1 + 1, ending);
                if (alttry(str, endptr, end1, end2)) {
                    return (TRUE);
                }
                endptr += 2;
            } else if (endptr[-2] == 'y') {
                strecpy(endptr, ending); /* eye -> eyeing */
                if (unftry(str)) {
                    return (TRUE);
                }
            }
        }
        endptr--;                       /* able -> abling */
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
        *endptr++ = 'e';
        break;
    case 'a':
    case 'i':
    case 'o':
    case 'u':
        break;
    case 'h':
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
        break;
    case 'c':                               /* panic -> panicking */
        if (endptr[-2] == 'a' || endptr[-2] == 'i') {
            *endptr++ = 'k';
            strecpy(endptr, ending);
            if (unftry(str)) {
                return (TRUE);
            }
        }
        endptr--;
        break;
    case 'l':                       /* level -> leveled or levelled */
        strecpy(end1, ending);
        end2[0] = endptr[-1];
        strecpy(&end2[1], ending);
        return (alttry(str, endptr, end1, end2));
    case 's':
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
    default:
        *endptr = endptr[-1];
        endptr++;
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
        endptr--;
        break;
    }
    strecpy(endptr, ending);
    return (unftry(str));
}

static ido_ment(str, endptr, ending)
 char *str;
 char *endptr;
 char *ending;
{
    strecpy(endptr, ending);         /* <word> -> <word>ment */
    if (unftry(str)) {
        return (TRUE);
    }
    *endptr = '\0';                 /* acknowledge -> acknowledgment */
    if (strcmp(&endptr[-3], "dge") == 0) {
        strecpy(&endptr[-1], ending);
        return(unftry(str));
    }

    return (FALSE);
}

static ido_able(str, endptr, ending)
 char *str;
 char *endptr;
 char *ending;
{
    char   altend[MAXWORD];

    strecpy(altend, ending);
    altend[0] = 'i';

    if (endptr[-1] == endptr[-2]) {
        strecpy(endptr, ending);
        return (unftry(str));
    }

    switch (endptr[-1]) {
    case 'y':
        endptr[-1] = 'i';               /* try -> triable */
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
        endptr[-1] = 'y';
        break;
    case 'd':
        *endptr = endptr[-1];
        strecpy(&endptr[1], ending);
        if (unftry(str)) {
            return (TRUE);
        }
        strecpy(endptr, altend);         /* extend -> extendible */
        if (unftry(str)) {
            return (TRUE);
        }
        endptr[-1] = 's';               /* defend -> defensible */
        if (unftry(str)) {
            return (TRUE);
        }
        endptr[-1] = 'd';
        strecpy(endptr, ending);
        return (unftry(str));
    case 't':
        *endptr = endptr[-1];           /* rebut -> rebuttable */
        strecpy(&endptr[1], ending);
        if (unftry(str)) {
            return (TRUE);
        }
        if (endptr[-2] == 'i') {        /* permit -> permissible */
            endptr[-1] = 's';
            *endptr    = 's';
            strecpy(&endptr[1], altend);
            if (unftry(str)) {
                return (TRUE);
            }
            endptr[-1] = 't';
        }
        strecpy(endptr, altend);
        if (unftry(str)) {
            return (TRUE);
        }
        strecpy(endptr, ending);
        return (unftry(str));
    case 'b':                               /* recall -> recallable */
    case 'f':
    case 'g':
    case 'l':
    case 'm':
    case 'n':
    case 'p':
    case 'r':
        *endptr = endptr[-1];
        strecpy(&endptr[1], ending);
        if (unftry(str)) {
            return (TRUE);
        }
        strecpy(endptr, ending);
        return (unftry(str));
    case 'e':
        strecpy(&endptr[-1], ending);    /* repute -> reputable */
        if (unftry(str)) {
            return (TRUE);
        }
        strecpy(&endptr[-1], altend);
        if (unftry(str)) {
            return (TRUE);
        }
        if (endptr[-3] == 'a' && endptr[-2] == 't') {
            strecpy(&endptr[-3], ending);
            if (unftry(str)) {
                return (TRUE);
            }
            endptr[-2] = 't';
        }
        endptr[-1] = 'e';
        strecpy(endptr, ending);         /* change -> changeable */
        return (unftry(str));
    default:
        break;
    }
    strecpy(endptr, ending);                 /* <word> -> <word>able */
    if (unftry(str)) {
        return (TRUE);
    }
    strecpy(endptr, altend);                 /* <word> -> <word>ible */
    return (unftry(str));
}

/* Try alternative inflected forms. Return a string containing
   the acceptable inflection(s) separated by UNFSEP */

static alttry(str, endptr, end1, end2)
 char *str;
 char *endptr;
char   *end1;
char   *end2;
{
    char   tmpstr[MAXWORD];

    *endptr = '\0';
    strecpy(strecpy(tmpstr, str), end2);
    endptr = strecpy(endptr, end1);
    if (unftry(str)) {
        *endptr++ = UNFSEP;
    } else {
        endptr = str;
    }
    strecpy(endptr, tmpstr);
    if (!unftry(endptr)) {
        if (endptr != str) {
            endptr--;
        }
        *endptr = '\0';
    }
    return (*str != '\0');
}

static ido_est(str, endptr, ending)
 char *str;
 char *endptr;
 char *ending;
{

    char   end1[MAXWORD];
    char   end2[MAXWORD];
    char   altend[MAXWORD];
    char   prefix[MAXWORD];

    if (Infstgptr->en_rel == RE_CM) {
        strecpy(altend, "est");
        strecpy(prefix, "more ");
    } else {
        strecpy(altend, "er");
        strecpy(prefix, "most ");
    }

    switch (endptr[-1]) {

    case 'y' :
        endptr[-1] = 'i';
        strecpy(endptr, altend);
        if (unftry(str)) {
            strecpy(endptr, ending);
            if (unftry(str)) {
                return (TRUE);
            }
        }
        endptr[-1] = 'y';
        break;
    case 'e':
        endptr--;
        strecpy(endptr, altend);
        if (unftry(str)) {
            strecpy(endptr, ending);
            if (unftry(str)) {
                return (TRUE);
            }
        }
        *endptr++ = 'e';
        break;
    case 'l':
        strecpy(endptr, altend);
        if (unftry(str)) {
            strecpy(end1, ending);
            end2[0] = 'l';
            strecpy(&end2[1], ending);
            return (alttry(str, endptr, end1, end2));
        }
    default:
        *endptr = endptr[-1];
        endptr++;
        strecpy(endptr, altend);
        if (unftry(str)) {
            strecpy(endptr, ending);
            if (unftry(str)) {
                return (TRUE);
            }
        }
        *--endptr = '\0';
        break;
    }

    strecpy(endptr, altend);
    if (unftry(str)) {
        strecpy(endptr, ending);
        if (unftry(str)) {
            return (TRUE);
        }
    }
    *endptr = '\0';
    strecpy(strchr(prefix, 0), str);
    strecpy(str, prefix);
    return (TRUE);
}

/* Append rtstr to each word in str (separated by UNFSEP) */

appndstr(str, rtstr, code)
char   *str;
char   *rtstr;
int     code;
{
char *tmpptr;
char *strptr;
char   tmpstr[LONGWORD];

    strptr = str;
    tmpptr = tmpstr;
    while (*strptr != '\0')
    {
        if (*strptr == UNFSEP)
        {
            if (*rtstr != '\0')
                tmpptr = strecpy(tmpptr, rtstr);
            if (code == INFCODE)
                *strptr = INFSEP;
        }
        *tmpptr++ = *strptr++;
    }
    strecpy(tmpptr, rtstr);
    strecpy(str, tmpstr);
}
