/**
 *  Routines to get initial list of
 *  matching dictionary words for
 *  cipherwords
 *  
 *  Author: Ramkumar Natarajan (nrk@cs.purdue.edu)
 **/

#include "legend.h"

LOCAL int makeMapForWord(char *word, int len, int *patbuf, int *patpos, int **patmap) {
    int alpha_seen = 0, i;
    *patpos = 0;

    for ( i = 0; i < len; ++i) {
        int curchar = word[i];
        if ( !isalpha((int) curchar) )
          continue;
        else {
            int aleph_code = aleph_codes[CHAR_TO_INDEX(curchar)];
            curchar = CHAR_TO_INDEX(curchar);
            if ( alpha_seen & aleph_code ) {
                patbuf[curchar] |= (1 << i);
            }
            else {
                alpha_seen |= aleph_code;
                patbuf[curchar] = (1 << i);
                *patpos |= patbuf[curchar];
                patmap[i] = patbuf + curchar;
            }
        }
    }

    return 0;
}

int getMatchingWordsFromDict(CipherWord *cipherwordptr, Dictionary *dictptr, CipherChar *ciphermap) {
    char  *cptr;
    int    i, j;
    int    rejected;
    int    newmap[ALPHA_SZ_];
    char **wordlist = NULL;
    int    nwords = 0;
    int    wordlistsz = 0;

    int    patmapbuf[ALPHA_SZ_];
    int   *patmap[ALPHA_SZ_];
    int    patmappos;

    char  *cipherword   = cipherwordptr->word;
    int    cipherpatmapbuf[ALPHA_SZ_];
    int   *cipherpatmap[ALPHA_SZ_];
    int    ciphermappos;
    int    nonalphapos  = cipherwordptr->nonalphapos;

    readDict(dictptr);

    if ( dictptr->nwords <= 0 ) {
        cipherwordptr->npossibles = 0;
        return 0;
    }

    makeMapForWord(cipherword, cipherwordptr->len, cipherpatmapbuf, &ciphermappos, cipherpatmap);
    cptr = dictptr->words;
    memset(newmap, 0, sizeof newmap);
    for ( i = 0; i < dictptr->nwords; ++i ) {
        rejected = FALSE;
        if ( nonalphapos >= 0 &&
             cptr[nonalphapos] != cipherword[nonalphapos] ) {
            rejected = TRUE;
            cptr += dictptr->wordlen;
            continue;
        }
        makeMapForWord(cptr, cipherwordptr->len, patmapbuf, &patmappos, patmap);
        if ( patmappos != ciphermappos ) {
            rejected = TRUE;
        }
        else {
            for ( j = 0; j < cipherwordptr->len; ++j ) {
                CipherChar *ccptr;
                int cipherchar;
                int plainchar;
                
                if ( j == nonalphapos )
                    continue;
                if ( (patmappos & (1 << j)) &&
                     *patmap[j] != *cipherpatmap[j] ) {
                    rejected = TRUE;
                    break;
                }
                plainchar  = CHAR_TO_INDEX(cptr[j]);
                cipherchar = CHAR_TO_INDEX(cipherword[j]);
                ccptr = ciphermap + cipherchar;
                if ( ! (ccptr->plainmap & aleph_codes[plainchar]) ) {
                    rejected = TRUE;
                    break;
                }
            }
        }

        if ( !rejected ) {
            int   tempmap[ALPHA_SZ_];

            memset(tempmap, 0, sizeof tempmap);
            for ( j = 0; j < cipherwordptr->len; ++j ) {
                int cipherchar;
                int plainchar;
                CipherChar *ccptr;

                if ( j == nonalphapos )
                    continue;
                cipherchar = CHAR_TO_INDEX(cipherword[j]);
                plainchar  = CHAR_TO_INDEX(cptr[j]);
                ccptr      = ciphermap + cipherchar;
                tempmap[cipherchar] |= aleph_codes[plainchar];
            }

            if ( wordlistsz == nwords ) {
                wordlist = my_realloc(wordlist, sizeof *wordlist * (wordlistsz + WORDLIST_SZ_));
                wordlistsz += WORDLIST_SZ_;
            }
            wordlist[nwords++] = cptr;
            for ( j = 0; j < ALPHA_SZ_; ++j ) {
                newmap[j] |= tempmap[j];
            }
        }
        cptr += dictptr->wordlen;
    }

    if ( nwords ) {
        for ( i = 0; i < ALPHA_SZ_; ++i ) {
            if ( newmap[i] )
                ciphermap[i].plainmap &= newmap[i];
        }
        cipherwordptr->possiblewords = my_realloc(wordlist, sizeof *wordlist * nwords);
    }
    else
        MY_FREE(wordlist);
    cipherwordptr->npossibles = nwords;
    
    return 0;
}
