#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "parse_tree.h"
#include "iec.h"
#include "token.h"

typedef struct {
    short name;			/* Function name index                  */
    short func_block;		/* Function / Function Block / Program  */
    short nin, nout;		/* Number of inlets/outlets             */
    short *in, *out;		/* Inlet / Outlet indexs                */
} FUNC_DEF;

typedef struct {
    short name;			/* Variable name index                  */
    short type;			/* Type name                            */
    short defv;			/* Default value                        */
} FUNC_VAR_DEF;

FUNC_DEF *fd = NULL;		/* Function definition array            */
FUNC_VAR_DEF *fvd = NULL;	/* Function variable definition array   */
char **fd_name = NULL;		/* Name array                           */
int nfd = 0, nfvd = 0, nfd_name = 0;

short index_name(const char *name)
{
    int i, n;

    for (i = 0; i < nfd_name; i++) {
	if (strcasecmp(fd_name[i], name) == 0)
	    return i;
    }

    n = nfd_name;
    nfd_name++;
    fd_name = realloc(fd_name, nfd_name * sizeof(char *));
    fd_name[n] = strdup(name);
    return n;
}

short index_fvd(const char *name, const char *type, const char *defv)
{
    int i, idx_name, idx_type, idx_defv;

    idx_name = index_name(name);
    idx_type = index_name(type);
    idx_defv = index_name(defv);

    for (i = 0; i < nfvd; i++) {
	if ((fvd[i].name == idx_name) &&
	    (fvd[i].type == idx_type) && (fvd[i].defv == idx_defv))
	    return i;
    }
    i = nfvd;
    fvd[i].name = idx_name;
    fvd[i].type = idx_type;
    fvd[i].defv = idx_defv;
    nfvd++;

    return i;
}

/*
 * FILE FORMAT:
 *
 *	short	Number of FUNC_DEF structures defined
 *	    short	Index to function name
 *	    short	Function / Function block / Program flag
 *	    short	Number of inlet variables
 *	    short	Number of outlet variables / local variables
 *	    short[]	Inlet indexes
 *	    short[]	Outlet indexes
 *
 *	short	Number of FUNC_VAR_DEF structures defined
 *	data	Contents of fvd
 *
 *	short	Number of '\0' terminated strings to follow
 *	data	Contents of fd_nam
 *
 * There should be a checksum so we don't crash the program due to a
 * corrupted function definition file.
 */
void flush_func_def(void)
{
    int i, j, fileno;

    fileno = open("func.def", O_CREAT);

    if (fileno < 0) {
	fprintf(stderr,
		"ERROR: Could not dump function definition table.\n");
	return;
    }

    /*
     * FUNC_DEF table
     */
    write(fileno, (char *) &nfd, sizeof(nfd));
    for (i = 0; i < nfd; i++) {
	write(fileno, (char *) &fd[i].name, sizeof(fd[i].name));
	write(fileno, (char *) &fd[i].func_block,
	      sizeof(fd[i].func_block));
	write(fileno, (char *) &fd[i].nin, sizeof(fd[i].nin));
	write(fileno, (char *) &fd[i].nout, sizeof(fd[i].nout));
	for (j = 0; j < fd[i].nin; j++)
	    write(fileno, (char *) &fd[i].in[j], sizeof(fd[i].in[j]));
	for (j = 0; j < fd[i].nout; j++)
	    write(fileno, (char *) &fd[i].out[j], sizeof(fd[i].out[j]));
    }
    /*
     * FUNC_VAR_DEF table
     */
    write(fileno, (char *) &nfvd, sizeof(nfvd));
    write(fileno, (char *) fvd, nfvd * sizeof(FUNC_VAR_DEF));
    /*
     * Name table
     */
    write(fileno, (char *) &nfd_name, sizeof(nfd_name));
    for (i = 0; i < nfd; i++)
	write(fileno, (char *) fd_name[i], strlen(fd_name[i]) + 1);
    close(fileno);
}

void load_func_def(void)
{
    int i, j, fileno;

    fileno = open("func.def", O_RDONLY);
    /*
     * FUNC_DEF table
     */
    read(fileno, (char *) &nfd, sizeof(nfd));
    fd = realloc(fd, nfd * sizeof(FUNC_DEF));
    for (i = 0; i < nfd; i++) {
	read(fileno, (char *) &fd[i].name, sizeof(fd[i].name));
	read(fileno, (char *) &fd[i].func_block, sizeof(fd[i].func_block));
	read(fileno, (char *) &fd[i].nin, sizeof(fd[i].nin));
	read(fileno, (char *) &fd[i].nout, sizeof(fd[i].nout));
	fd[i].in = realloc(fd[i].out, fd[i].nin * sizeof(short));
	fd[i].out = realloc(fd[i].out, fd[i].nout * sizeof(short));
	for (j = 0; j < fd[i].nin; j++)
	    read(fileno, (char *) &fd[i].in[j], sizeof(fd[i].in[j]));
	for (j = 0; j < fd[i].nout; j++)
	    read(fileno, (char *) &fd[i].out[j], sizeof(fd[i].out[j]));
    }
    /*
     * FUNC_VAR_DEF table
     */
    read(fileno, (char *) &nfvd, sizeof(nfvd));
    fvd = realloc(fvd, nfvd * sizeof(FUNC_VAR_DEF));
    read(fileno, (char *) fvd, nfvd * sizeof(FUNC_VAR_DEF));
    close(fileno);
}

void init_func_def(void)
{
    /*
     * The following are "standard" functions
     */
    /*
       ANY_NUM      ABS(ANY_NUM)
       ANY_REAL     SQRT(ANY_REAL)

       ANY_REAL     LN(ANY_REAL)
       ANY_REAL     LOG(ANY_REAL)
       ANY_REAL     EXP(ANY_REAL)

       ANY_REAL     SIN(ANY_REAL)
       ANY_REAL     COS(ANY_REAL)
       ANY_REAL     TAN(ANY_REAL)
       ANY_REAL     ASIN(ANY_REAL)
       ANY_REAL     ACOS(ANY_REAL)
       ANY_REAL     ATAN(ANY_REAL)

       ANY_BIT      SHL(ANY_BIT, UINT)
       ANY_BIT      SHR(ANY_BIT, UINT)
       ANY_BIT      ROL(ANY_BIT, UINT)
       ANY_BIT      ROR(ANY_BIT, UINT)

       ANY_NUM      SEL(BOOL, ANY_NUM, ANY_NUM)
       ANY_###      MAX(ANY_NUM, ANY_NUM, ...)
       ANY_###      MIN(ANY_NUM, ANY_NUM, ...)
       ANY_###      LIMIT(ANY_NUM, ANY_NUM, ANY_NUM)
       ANY_NUM      MUX(ANY_INT, ANY_NUM, ANY_NUM, ...)

       INT          LEN(STRING)
       STRING       LEFT(STRING, UINT)
       STRING       RIGHT(STRING, UINT)
       STRING       MID(STRING, UINT, UINT)
       STRING       CONCAT(STRING, STRING, ...)
       STRING       INSERT(STRING, STRING, UINT)
       STRING       DELETE(STRING, UINT, UINT)
       STRING       REPLACE(STRING, STRING, UINT, UINT)
       INT          FIND(STRING, STRING)
     */
}
