extern void found( int , char *);
#define TCHAR char
#define LEN 256

#include <stdio.h>
#include <ctype.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>
#include "myopt.h"
#define TRUE 1
#define FALSE 0

#define MAX_NUMBER 100
#define MAX_PARMS    8

#define max(a,b) (((a) > (b)) ? (a) : (b))

struct _apitype {
    char *pchName;
    char *pchformat;
    int   cb;
    int   cParms;
    int   iOrder[MAX_PARMS];
} apilist[MAX_NUMBER];
int api_num = -1;


struct _apidump
    {
    char  pchName[256];
    char  pchformat[256];
    int   cb;
    int   cParms;
    int   iOrder[MAX_PARMS];
    } apidump;


FILE *fp_src, *fp_dst, *fp_err, *fp_dat;
char chFirstBuf[53];
int token_length = 0;
int bVerbose = FALSE;
int  bBinary  = TRUE;

#define m_iswspace(a) (isspace(a) && (a) != '\n')

char *get_token( char *pch)
{
    char *pch2;

    while (*pch)
        {
        if (isalnum(*pch))
            {
            if (strchr( chFirstBuf, *pch))
                {
                pch2 = pch;
                while(isalnum(*pch2)) pch2++;
                token_length = (int)(pch2 - pch);
                return(pch);
                }
            do
                {
                pch++;
                } while(isalnum(*pch));
            }
        while (*pch && !isalnum(*pch))
            {
            pch++;
            } 
        }
    return(NULL);
}

void print_dat()
{
    int ii, jj;

    for (ii = 0; ii < api_num; ii++)
        {
        printf( "Api Macro %s \n", apilist[ii].pchName);

        printf( "Macro Format:%s\n", apilist[ii].pchformat);
        printf( "Parms:");
        for ( jj = 0; jj < MAX_PARMS; jj++)
            printf( " %d", apilist[ii].iOrder[jj]);
        printf( "\n");

        }
    printf( "First Letters: (%s)\n", chFirstBuf);
}

void read_compiled()
    {
    int ii, jj;
    char chTmpBuf[2];

    chFirstBuf[0] = 0x00;


    fread( &api_num, sizeof(int), 1, fp_dat);

    for (ii = 0; ii < api_num; ii++)
        {
        fread( &apidump, sizeof(apidump), 1, fp_dat);

        if (strchr(chFirstBuf, apidump.pchName[0]) == NULL)
            {
            chTmpBuf[0] = apidump.pchName[0];
            chTmpBuf[1] = 0x00;
            strcat( chFirstBuf, chTmpBuf);
            }


        apilist[ii].cb         = apidump.cb;
        apilist[ii].cParms     = apidump.cParms;

        if (apidump.pchName[0] != 0x00)
            {
            apilist[ii].pchName = malloc(strlen(apidump.pchName) + sizeof(char));
            strcpy( apilist[ii].pchName, apidump.pchName);
            }
        else
            apilist[ii].pchName = NULL;

        if (apidump.pchformat[0] != 0x00)
            {
            apilist[ii].pchformat = malloc(strlen(apidump.pchformat) + sizeof(char));
            strcpy( apilist[ii].pchformat, apidump.pchformat);
            }
        else
            apilist[ii].pchformat = NULL;

        for (jj = 0; jj < MAX_PARMS; jj++)
            {
            apilist[ii].iOrder[jj] = apidump.iOrder[jj];
            }

        }

    }

void init_dat(int bDump)
{
    char chInBuf[256];
    char chFmtBuf[256];
    char chTmpBuf[256];
    char pchParms[MAX_PARMS][34];
    char ch;
    char *pchOut;
    char *pchIn;
    int  iParmNo, iMatch;
    int   nest = 0;
    int ii;
    long lftell = 0;
    long *lptell = NULL;
    struct _apitype *api;


    if (bBinary)
        {
        read_compiled();
        return;
        }


    chFirstBuf[0] = '\0';

    while (fgets( chInBuf, 256, fp_dat))
        {
        if (bDump)
            fputs( chInBuf, fp_dst);

        if (chInBuf[0] == '@')
            {

            api_num++;

            if (chInBuf[1] == '\n')
                return;
                
            // printf( "%s", chInBuf + 1);

            if (strchr(chFirstBuf, chInBuf[1]) == NULL)
                {
                chTmpBuf[0] = chInBuf[1];
                chTmpBuf[1] = 0x00;
                strcat( chFirstBuf, chTmpBuf);
                }

            api = &apilist[api_num];
            api->pchName = NULL;
            api->pchformat = NULL;
            api->cParms = 0;
            lftell = ftell(fp_dat);

            for (ii = 0; ii < MAX_PARMS; ii++)
                {
                api->iOrder[ii] = -1;
                pchParms[ii][0] = 0x00;
                }

            pchOut = chTmpBuf;
            pchIn  = chInBuf + 1;
            assert( isalnum(*pchIn));

            while (isalnum(*pchIn))
                *pchOut++ = *pchIn++;

            *pchOut = 0x00;

            api->cb = strlen(chTmpBuf);
            api->pchName = malloc(api->cb + sizeof(TCHAR));
            strcpy(api->pchName, chTmpBuf);
            if (bDump)
                printf("Replace::%s::\n", api->pchName);

            assert(*pchIn == '(');

            nest = 1;
            pchIn++;
            iParmNo = 0;

            while (nest)
                {
                pchOut = chTmpBuf;
                *pchOut = 0x00;

                while (m_iswspace(*pchIn)) pchIn++;

                while (isalnum(*pchIn))
                    *pchOut++ = *pchIn++;
                *pchOut = 0x00;

                // printf( "%s\n", chTmpBuf);

                strcpy(pchParms[iParmNo++], chTmpBuf);
                if (*pchIn++ == ')')
                    nest--;
                }
            api->cParms = iParmNo;
            // printf( "Parms %d\n", iParmNo);

            while (m_iswspace(*pchIn)) pchIn++;

            pchOut = chFmtBuf;

            if (*pchIn == '\n')
                {
                api->pchformat = NULL;
                if (bDump)
                    printf( "Format::::\n" );
                continue;
                }

            assert( isalnum(*pchIn));

            while (isalnum(*pchIn) || *pchIn == '_')
                *pchOut++ = *pchIn++;

            if (*pchIn == '\n')
                {
                *pchOut = 0x00;
                api->pchformat = malloc(strlen(chFmtBuf) + sizeof(TCHAR));
                strcpy(api->pchformat, chFmtBuf);
                if (bDump)
                    printf( "Format::%s::\n", api->pchformat);
                continue;
                }

            assert(*pchIn == '(');
            *pchOut++ = *pchIn++;
            *pchOut = 0x00;
            // printf("line %d::%s\n", __LINE__, chFmtBuf );

            nest = 1;
            iParmNo = 0;
            iMatch = 0;

            while (nest)
                {
                pchOut = chTmpBuf;
                *pchOut = 0x00;

                while (!isalnum(*pchIn) && nest > 0)
                    {
                    ch = *pchIn;
                    *pchOut++ = *pchIn++;

                    if (ch == '(') nest++;
                    if (ch == ')') nest--;
                    if (ch == ',' ) iParmNo++;
                    }

                *pchOut = 0x00;
                strcat( chFmtBuf, chTmpBuf);
                // printf( "Parm %d, nest %d, Fmt::%s\n", iParmNo, 
                //        nest, chFmtBuf);

                pchOut = chTmpBuf;
                *pchOut = 0x00;

                if (nest == 0)
                    continue;

                while (isalnum(*pchIn))
                    *pchOut++ = *pchIn++;
                *pchOut = 0x00;

                // printf( "Token::%s\n", chTmpBuf);

                for (ii = 0; ii < api->cParms; ii++)
                    {
                    // printf( "Compare (%s) and (%s)\n", chTmpBuf, 
                    //    pchParms[ii]);
                    if (strcmp(chTmpBuf, pchParms[ii]) == 0)
                        {
                        // printf( "Match Parm %d\n", ii);
                        assert(api->iOrder[iMatch] == -1);
                        api->iOrder[iMatch++] = ii;
                        strcpy(chTmpBuf, "%s");
                        }
                    }
                strcat(chFmtBuf, chTmpBuf);

                }
            api->pchformat = malloc(strlen(chFmtBuf) + sizeof(TCHAR));
            strcpy(api->pchformat, chFmtBuf);

            if (bDump)
                {
                printf( "Format::%s::", api->pchformat);
                for (ii = 0; ii < api->cParms + 1; ii++)
                    printf( "%d, ", api->iOrder[ii]);
                printf("\n");
                }
            }
        else
            {
            if (lptell)
                {
                *lptell = lftell;
                lptell = NULL;
                }
            }
        }
}

void add_buff(char *pch)
{
    char chExtraLine[256];
    fgets( chExtraLine, 256, fp_src);
    if (bVerbose)
        fputs( chExtraLine, fp_err);

    strcat( pch, chExtraLine);
}

void found( int index, char *pch)
{
    char chApiBuf[2048];
    char chRestBuf[256];
    char *pchIn = pch;
    char *pchOut = chApiBuf;
    char *pchEnd;
    char pchParms[MAX_PARMS][256];
    char *parmlist[MAX_PARMS];
    struct _apitype *api = &apilist[index];
    int nest = 0;
    int iParmNo = 0;
    int string = 0;
    int ii;
    char ch;

    for (ii = 0; ii < MAX_PARMS; ii++)
        {
        pchParms[ii][0] = 0x00;
        parmlist[ii]    = NULL;
        }

    strncpy( chApiBuf, pch, api->cb);
    pchIn += api->cb;
    pchOut = chApiBuf + api->cb;
    while (iswspace(*pchIn)) pchIn++;
    assert( *pchIn == '(');
   *pchOut++ = *pchIn++;
    nest = 1;

    while (nest > 0)
        {
        ch = *pchOut++ = *pchIn++;
        if (ch == '(') nest++;
        if (ch == ')') nest--;
        if (ch == '\'' || ch == '\"')
            {
            char chEnd = ch;

            do
                {
                ch = *pchOut++ = *pchIn++;
                if (ch == '\\')
                    ch = *pchOut++ = *pchIn++;
                if (ch == 0x00)
                    {
                    pchIn--;
                    pchOut--;
                    add_buff(pchIn);
                    }
                }
            while (ch != chEnd);
            }
        if (ch == 0x00)
            {
            pchIn--;
            pchOut--;
            add_buff(pchIn);
            }
        }

    *pchOut = 0x00;
    strcpy( chRestBuf, pchIn);
    pchIn = chApiBuf + api->cb + 1;
//    printf( "Api (%s) Rest (%s)\n", chApiBuf, chRestBuf);
    if (api->pchformat == NULL)
        {
        strcpy( pch, chRestBuf);
        return;
        }

    nest = 1;
    iParmNo = 0;
    pchEnd = pchIn;

    while (nest > 0)
        {
        int  cb;
        char *pchBeginLoop = pchEnd;

        ch = *pchEnd;
        while (isspace(ch) || isalnum(ch)
            || strchr("&*.[]-></+|?!%=^_:", ch) ) ch = *++pchEnd;

        if ( ! (strchr("\"\'(),", ch)))
            printf("Invalid character %c\n", ch);

        assert( ch != 0x00);

        if (ch == '\"' || ch == '\'')
            {
            char chEnd = ch;
            do
                {
                ch = *++pchEnd;
                if (ch == '\\')
                    ch = *++pchEnd;
                }
            while (ch != chEnd);
            ch = *++pchEnd;
            }
        if (ch == '(') 
            {
            nest++;
            ++pchEnd;
            }
        if (ch == ')') 
            {
            nest--;
            if (nest == 0)
                {
                cb = pchEnd - pchIn;
                strncpy(pchParms[iParmNo], pchIn, cb);
                pchParms[iParmNo++][cb] = 0x00;
                pchIn += cb + 1;
                pchEnd = pchIn;
                }
            else
                ++pchEnd;
            }
        if (ch == ',' )
            {
            if (nest == 1)
                {
                cb = pchEnd - pchIn;
                strncpy(pchParms[iParmNo], pchIn, cb);
                pchParms[iParmNo++][cb] = 0x00;
                pchIn += cb + 1;
                pchEnd = pchIn;
                }
            else
                ++pchEnd;
            }
        if(pchBeginLoop == pchEnd)
            pchEnd++;
        }

    for (ii = 0; ii < MAX_PARMS; ii++)
        {
        if (api->iOrder[ii] != -1)
            parmlist[ii] = pchParms[api->iOrder[ii]];
        else
            parmlist[ii] = NULL;
        }

    sprintf( pch, api->pchformat, parmlist[0], parmlist[1],
        parmlist[2], parmlist[3], parmlist[4], parmlist[5],
        parmlist[6], parmlist[7]);
    strcat(pch, chRestBuf);
}

char *FindApi( char *pch)
{
    int ii;
    char *ptr;

    for (ii = 0; ii < api_num; ii++)
        {
        if ( token_length == apilist[ii].cb &&
                *pch == apilist[ii].pchName[0] &&
                (strncmp( pch, apilist[ii].pchName, apilist[ii].cb) == 0))
            {
            ptr = pch + apilist[ii].cb;
            while (iswspace(*ptr)) ptr++;
            if (*ptr == '(')
                {
                found(ii, pch);
                return( pch + 1);
                }
            }
        }

    return( pch + 1);
}

void useage()
{
    printf( "Useage: ms_ssed [-v -d ] file_name [ file_result ] \n");
    printf( "        -d -- dump data file. \n");
    printf( "        -v -- Verbose, echo input to screen. \n");
}

main( argc, argv)
	int argc;
	char *argv[];
    {
    int ii;
    char src[30];
    char dst[30];
    char buffer[2560];
    char *ptr;
    int  bDump = FALSE;
    char chBinFile[34] = "ms_ssed.bin";
    char *pchBinFile = chBinFile;

    int  bCompile = FALSE;

    ii = 1;

    src[0] = 0x00;
    dst[0] = 0x00;

    while (ii = my_getopt(argc, argv, "vd"))
        {
        switch (ii)
            {
            case ((int) 'd'):
                bDump = TRUE;
                break;
            case ((int) 'v'):
                bVerbose = TRUE;
                break;

            case -1:
                if (src[0] == 0x00)
                    {
                    strcpy(src, p_optstr);
                    break;
                    }
                if (dst[0] == 0x00)
                    {
                    strcpy(dst, p_optstr);
                    break;
                    }

                // FALL THRU

            default:
                useage();
                return(0);
            }
        }
    
    if (src[0] == 0x00 && bDump == FALSE )
        {
        useage();
        return(0);
        }

    fp_src = stdin;
	fp_dst = stdout;
	fp_err = stderr;

    fp_dat = fopen( pchBinFile, "rb");

    if (fp_dat == NULL)
        {
        printf( "Failed to open DataFile %s\n", pchBinFile);
        return(0);
        }

    if (bDump)
        {
        init_dat(TRUE);
        print_dat();

        fclose(fp_dat);
        return(0);
        }

    if (dst[0] == 0x00)
        {
        strcpy( dst, src);
        
        if ((ptr = strchr( dst, '.')))
            {
            *ptr = '\0';
            }
        strcat( dst, ".win");   
        }

    printf( "src: (%s) dst (%s)\n", src, dst);

    fp_src = freopen( src, "rt", stdin);
	fp_dst = freopen( dst, "w", stdout);

    init_dat(FALSE);

    while (fgets( buffer, 256, fp_src))
        {
        if (bVerbose)
            fputs( buffer, fp_err);

        ptr = buffer;
        while (ptr = get_token( ptr))
            {
            ptr = FindApi(ptr);
            }
        fputs( buffer, fp_dst);
        }
    fclose(fp_dat);
    fclose(fp_src);
    fclose(fp_dst);
    }

