/* LIBDGL -- a Directed Graph Library implementation
 * Copyright (C) 2002 Roberto Micarelli
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * Source best viewed with tabstop=4
 */

#include <grass/config.h>
#include <stdio.h>
#include <regex.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "opt.h"
#include "../type.h"
#include "../graph.h"

static void _regmtostring(char *pszOut, int cszOut, char *pszIn,
			  regmatch_t * pregm)
{
    int i, iout;

    for (iout = 0, i = pregm->rm_so; i < pregm->rm_eo && iout < cszOut - 1;
	 i++) {
	if (i >= 0)
	    pszOut[iout++] = pszIn[i];
    }
    pszOut[iout] = 0;
}

static int _sztoattr(unsigned char *pbNodeAttr, int cbNodeAttr, char *szw)
{
    int i, ib;

    for (ib = 0, i = 0; szw[i] && ib < cbNodeAttr; i++) {
	if (szw[i] == ' ')
	    continue;
	pbNodeAttr[ib] = (szw[i] >= '0' &&
			  szw[i] <= '9') ? (szw[i] - '0') * 16 : (szw[i] >=
								  'A' &&
								  szw[i] <=
								  'F') ? (10 +
									  (szw
									   [i]
									   -
									   'A'))
	    * 16 : (szw[i] >= 'a' &&
		    szw[i] <= 'f') ? (10 + (szw[i] - 'a')) * 16 : 0;
	i++;
	if (szw[i]) {
	    pbNodeAttr[ib] += (szw[i] >= '0' &&
			       szw[i] <= '9') ? (szw[i] - '0') : (szw[i] >=
								  'A' &&
								  szw[i] <=
								  'F') ? (10 +
									  (szw
									   [i]
									   -
									   'A'))
		: (szw[i] >= 'a' &&
		   szw[i] <= 'f') ? (10 + (szw[i] - 'a')) : 0;
	}
	ib++;
    }
    return ib;
}

int main(int argc, char **argv)
{
    FILE *fp;
    char sz[1024];
    char szw[1024];
    int nret;
    regmatch_t aregm[64];
    dglInt32_t nVersion;
    dglInt32_t nNodeAttrSize;
    dglInt32_t nEdgeAttrSize;
    dglInt32_t anOpaque[16];
    int i, fd, cOut;

    regex_t reVersion;
    regex_t reByteOrder;
    regex_t reNodeAttrSize;
    regex_t reEdgeAttrSize;
    regex_t reCounters;
    regex_t reOpaque;
    regex_t reNodeFrom;
    regex_t reNodeAttr;
    regex_t reEdge;
    regex_t reToNodeAttr;
    regex_t reEdgeAttr;


    dglInt32_t nNodeFrom, nNodeTo, nUser, nCost;

    int fInOpaque;
    int fInBody;

    unsigned char *pbNodeAttr, *pbEdgeAttr, *pbToNodeAttr;

    STRUCT_STAT statdata;

    dglGraph_s graphOut;

    /* program options
     */
    char *pszFilein;
    char *pszGraphout;

    GNO_BEGIN			/* short  long        default     variable        help */
	GNO_OPTION("i", "input", NULL, &pszFilein, "Input text file")
	GNO_OPTION("o", "output", NULL, &pszGraphout, "Output graph file")
	GNO_END if (GNO_PARSE(argc, argv) < 0) {
	return 1;
    }
    /*
     * options parsed
     */

    if (pszFilein == NULL) {
	GNO_HELP("... usage");
	return 1;
    }

    /*
     * compile header expressions
     */
    printf("Compile header expressions...");
    fflush(stdout);
    i = 0;
    if (regcomp(&reVersion, "^Version:[ ]+([0-9]+)", REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp(&reByteOrder, "^Byte Order:[ ]+(.+)", REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp
	(&reNodeAttrSize, "^Node Attribute Size:[ ]+([0-9]+)",
	 REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp
	(&reEdgeAttrSize, "^Edge Attribute Size:[ ]+([0-9]+)",
	 REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp(&reCounters, "^Counters:[ ]+.*", REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp(&reOpaque, "^Opaque Settings:", REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    printf("done.\n");

    /*
     * compile body expressions
     */

    printf("Compile body expressions...");
    fflush(stdout);
    if (regcomp(&reNodeFrom, "^HEAD ([0-9]+)[ ]*- [HT/']+", REG_EXTENDED) !=
	0)
	goto regc_error;
    i++;
    if (regcomp(&reNodeAttr, ".*HEAD ATTR [[]([0-9a-fA-F ]+)]", REG_EXTENDED)
	!= 0)
	goto regc_error;
    i++;

    if (regcomp
	(&reEdge,
	 "^EDGE #([0-9]+)[ ]*: TAIL ([0-9]+)[ ]*- [HT/']+[ ]+- COST ([0-9]+)[ ]*- ID ([0-9]+)",
	 REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp
	(&reToNodeAttr, ".*TAIL ATTR [[]([0-9a-fA-F ]+)]", REG_EXTENDED) != 0)
	goto regc_error;
    i++;
    if (regcomp(&reEdgeAttr, ".*EDGE ATTR [[]([0-9a-fA-F ]+)]", REG_EXTENDED)
	!= 0)
	goto regc_error;
    i++;

    printf("done.\n");

    goto regc_ok;




  regc_error:
    fprintf(stderr, "regex compilation error %d\n", i);
    exit(1);





  regc_ok:

    if ((fp = fopen(pszFilein, "r")) == NULL) {
	perror("fopen");
	return 1;
    }

    fstat(fileno(fp), &statdata);

    fInOpaque = 0;
    fInBody = 0;

    nNodeAttrSize = 0;
    nEdgeAttrSize = 0;
    pbNodeAttr = NULL;
    pbToNodeAttr = NULL;
    pbEdgeAttr = NULL;

    cOut = 0;

    while (fgets(sz, sizeof(sz), fp)) {
#ifndef VERBOSE
	if (!(cOut++ % 512) || G_ftell(fp) == statdata.st_size)
	    printf("Parse input file ... status: %ld/%ld\r", G_ftell(fp),
		   statdata.st_size);
	fflush(stdout);
#endif

#ifdef VERYVERBOSE
	printf("<<<%s>>>\n", sz);
#endif
	if (fInOpaque == 0 && fInBody == 0) {
	    if (regexec(&reVersion, sz, 64, aregm, 0) == 0) {
		_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
		nVersion = atoi(szw);
#ifdef VERYVERBOSE
		printf("-- version %d\n", nVersion);
#endif
	    }
	    else if (regexec(&reByteOrder, sz, 64, aregm, 0) == 0) {
	    }
	    else if (regexec(&reNodeAttrSize, sz, 64, aregm, 0) == 0) {
		_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
		nNodeAttrSize = atoi(szw);
		if (nNodeAttrSize) {
		    pbNodeAttr = (unsigned char *)malloc(nNodeAttrSize);
		    if (pbNodeAttr == NULL) {
			fprintf(stderr, "Memory Exhausted\n");
			exit(1);
		    }
		    pbToNodeAttr = (unsigned char *)malloc(nNodeAttrSize);
		    if (pbToNodeAttr == NULL) {
			fprintf(stderr, "Memory Exhausted\n");
			exit(1);
		    }
		}
#ifdef VERYVERBOSE
		printf("-- node attr size %d\n", nNodeAttrSize);
#endif
	    }
	    else if (regexec(&reEdgeAttrSize, sz, 64, aregm, 0) == 0) {
		_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
		nEdgeAttrSize = atoi(szw);
		if (nEdgeAttrSize > 0) {
		    pbEdgeAttr = (unsigned char *)malloc(nEdgeAttrSize);
		    if (pbEdgeAttr == NULL) {
			fprintf(stderr, "Memory Exhausted\n");
			exit(1);
		    }
		}
#ifdef VERYVERBOSE
		printf("-- edge attr size %d\n", nEdgeAttrSize);
#endif
	    }
	    else if (regexec(&reOpaque, sz, 64, aregm, 0) == 0) {
#ifdef VERYVERBOSE
		printf("-- opaque...\n");
#endif
		fInOpaque = 1;
	    }
	    else if (strncmp(sz, "--", 2) == 0) {
		nret = dglInitialize(&graphOut,
				     nVersion,
				     nNodeAttrSize, nEdgeAttrSize, anOpaque);
		if (nret < 0) {
		    fprintf(stderr, "dglInitialize error %s\n",
			    dglStrerror(&graphOut));
		    exit(1);
		}
#ifdef VERBOSE
		printf("Initialize: Version=%ld NodeAttr=%ld EdgeAttr=%ld\n",
		       nVersion, nNodeAttrSize, nEdgeAttrSize);
#endif
		fInBody = 1;
	    }
	}
	else if (fInOpaque > 0 && fInBody == 0) {
	    if (fInOpaque == 1) {
		sscanf(sz, "%ld %ld %ld %ld",
		       &anOpaque[0],
		       &anOpaque[1], &anOpaque[2], &anOpaque[3]);
		fInOpaque++;
#ifdef VERYVERBOSE
		printf("opaque 1: %ld %ld %ld %ld\n",
		       anOpaque[0], anOpaque[1], anOpaque[2], anOpaque[3]);
#endif

	    }
	    else if (fInOpaque == 2) {
		sscanf(sz, "%ld %ld %ld %ld",
		       &anOpaque[4],
		       &anOpaque[5], &anOpaque[6], &anOpaque[7]);
#ifdef VERYVERBOSE
		printf("opaque 2: %ld %ld %ld %ld\n",
		       anOpaque[4], anOpaque[5], anOpaque[6], anOpaque[7]);
#endif
		fInOpaque++;
	    }
	    else if (fInOpaque == 3) {
		sscanf(sz, "%ld %ld %ld %ld",
		       &anOpaque[8],
		       &anOpaque[9], &anOpaque[10], &anOpaque[11]);
#ifdef VERYVERBOSE
		printf("opaque 3: %ld %ld %ld %ld\n",
		       anOpaque[8], anOpaque[9], anOpaque[10], anOpaque[11]);
#endif
		fInOpaque++;
	    }
	    else if (fInOpaque == 4) {
		sscanf(sz, "%ld %ld %ld %ld",
		       &anOpaque[12],
		       &anOpaque[13], &anOpaque[14], &anOpaque[15]);
#ifdef VERYVERBOSE
		printf("opaque 4: %ld %ld %ld %ld\n",
		       anOpaque[12],
		       anOpaque[13], anOpaque[14], anOpaque[15]);
#endif
		fInOpaque = 0;
	    }
	}
	else if (fInBody == 1) {
	    if (regexec(&reNodeFrom, sz, 64, aregm, 0) == 0) {
		_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
#ifdef VERYVERBOSE
		printf("node from snippet = %s\n", szw);
#endif
		nNodeFrom = atol(szw);
		if (nNodeAttrSize > 0) {
		    if (regexec(&reNodeAttr, sz, 64, aregm, 0) == 0) {
			_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
			if (_sztoattr(pbNodeAttr, nNodeAttrSize, szw) !=
			    nNodeAttrSize) {
			    fprintf(stderr, "node attr size mismatch\n");
			}
#ifdef VERYVERBOSE
			{
			    int k;

			    for (k = 0; k < nNodeAttrSize; k++) {
				printf("%02x", pbNodeAttr[k]);
			    }
			    printf("\n");
			}
#endif
		    }
		}
	    }
	    else if (regexec(&reEdge, sz, 64, aregm, 0) == 0) {
		_regmtostring(szw, sizeof(szw), sz, &aregm[2]);
		nNodeTo = atol(szw);
		_regmtostring(szw, sizeof(szw), sz, &aregm[3]);
		nCost = atol(szw);
		_regmtostring(szw, sizeof(szw), sz, &aregm[4]);
		nUser = atol(szw);
		if (nEdgeAttrSize > 0) {
		    if (regexec(&reEdgeAttr, sz, 64, aregm, 0) == 0) {
			_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
			if (_sztoattr(pbEdgeAttr, nEdgeAttrSize, szw) !=
			    nEdgeAttrSize) {
			    fprintf(stderr, "edge attr size mismatch\n");
			}
#ifdef VERYVERBOSE
			{
			    int k;

			    for (k = 0; k < nEdgeAttrSize; k++) {
				printf("%02x", pbEdgeAttr[k]);
			    }
			    printf("\n");
			}
#endif
		    }
		}
		if (nNodeAttrSize > 0) {
		    if (regexec(&reToNodeAttr, sz, 64, aregm, 0) == 0) {
			_regmtostring(szw, sizeof(szw), sz, &aregm[1]);
			if (_sztoattr(pbToNodeAttr, nNodeAttrSize, szw) !=
			    nNodeAttrSize) {
			    fprintf(stderr, "to node attr size mismatch\n");
			}
#ifdef VERYVERBOSE
			{
			    int k;

			    for (k = 0; k < nNodeAttrSize; k++) {
				printf("%02x", pbToNodeAttr[k]);
			    }
			    printf("\n");
			}
#endif
		    }
		}
		nret = dglAddEdgeX(&graphOut,
				   nNodeFrom,
				   nNodeTo,
				   nCost,
				   nUser,
				   pbNodeAttr, pbToNodeAttr, pbEdgeAttr, 0);

		if (nret < 0) {
		    fprintf(stderr, "dglAddEdge error %s\n",
			    dglStrerror(&graphOut));
		    exit(1);
		}
#ifdef VERBOSE
		printf("AddEdge: from=%ld to=%ld cost=%ld user=%ld\n",
		       nNodeFrom, nNodeTo, nCost, nUser);
#endif
	    }
	}
    }
#ifndef VERBOSE
    printf("\ndone.\n");
#endif

    fclose(fp);

    regfree(&reVersion);
    regfree(&reByteOrder);
    regfree(&reNodeAttrSize);
    regfree(&reEdgeAttrSize);
    regfree(&reCounters);
    regfree(&reOpaque);
    regfree(&reNodeFrom);
    regfree(&reNodeAttr);
    regfree(&reEdge);
    regfree(&reToNodeAttr);
    regfree(&reEdgeAttr);

    if (pbNodeAttr)
	free(pbNodeAttr);
    if (pbToNodeAttr)
	free(pbToNodeAttr);
    if (pbEdgeAttr)
	free(pbEdgeAttr);


    printf("Flatten...");
    fflush(stdout);
    nret = dglFlatten(&graphOut);
    if (nret < 0) {
	fprintf(stderr, "dglFlatten error %s\n", dglStrerror(&graphOut));
	exit(1);
    }
    printf("done.\n");

    if (pszGraphout) {
	fd = open(pszGraphout, O_WRONLY | O_CREAT | O_TRUNC, 0666);
	if (fd < 0) {
	    perror("open");
	    exit(1);
	}

	printf("Write <%s>...", pszGraphout);
	fflush(stdout);
	nret = dglWrite(&graphOut, fd);
	if (nret < 0) {
	    fprintf(stderr, "dglWrite error %s\n", dglStrerror(&graphOut));
	    exit(1);
	}
	printf("done.\n");
	close(fd);
    }

    printf("Release...");
    fflush(stdout);
    dglRelease(&graphOut);
    printf("done.\n");

    return 0;
}
