/******************************************************************************
* Bzr-Read.c - Bezier curves handling routines - read from file.	      *
*******************************************************************************
* Written by Gershon Elber, Mar. 90.					      *
******************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "cagd_loc.h"
#include "irit_soc.h"

#define UNGET_STACK_SIZE	3
#define ZERO_NUM_EPSILON	1e-15

int _CagdGlblLineCount = 1;	     /* Used to locate errors in input file. */

static int
    GlblUnGetChar = 0,
    GlblReadSocket = FALSE,	              /* If TRUE - read from socket. */
    GlblTknStackSize = 0;	          /* Used by parser, to unget token. */
static char
    GlblStringToken[UNGET_STACK_SIZE][LINE_LEN],	/* Save unget tokens.*/
    CagdFloatFormat[LINE_LEN] =		  /* Floating point printing format. */
#ifdef DOUBLE
    "%-8.6lg";
#else
    "%-8.6g";
#endif /* DOUBLE */

static void InputUnGetC(char c);
static char InputGetC(FILE *f);
static int InputEOF(FILE *f);
static void GetStringToken(FILE *f, char *StringToken);

/*****************************************************************************
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
* Note the '[' was allready read.					     *
* Current supported attributes: None.					     *
* Returns NULL if O.k., otherwise string describing the error.		     *
*****************************************************************************/
char *_CagdGetCurveAttributes(FILE *f)
{
    TokenNumType i;
    char StringToken[LINE_LEN];

    do {
	switch (_CagdGetToken(f, StringToken)) {
	    default:
		while ((i = _CagdGetToken(f, StringToken)) !=
							TOKEN_CLOSE_PAREN &&
		       i != TOKEN_EOF);
		if (i == TOKEN_EOF)
		    return "EOF detected in middle of attribute.";
		break;
	}
    }
    while (_CagdGetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    _CagdUnGetToken(StringToken);

    return NULL;
}

/*****************************************************************************
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
* Note the '[' was allready read.					     *
* Current supported attributes: None.					     *
* Returns NULL if O.k., otherwise string describing the error.		     *
*****************************************************************************/
char *_CagdGetSurfaceAttributes(FILE *f)
{
    TokenNumType i;
    char StringToken[LINE_LEN];

    do {
	switch (_CagdGetToken(f, StringToken)) {
	    default:
		while ((i = _CagdGetToken(f, StringToken)) !=
							TOKEN_CLOSE_PAREN &&
		       i != TOKEN_EOF);
		if (i == TOKEN_EOF)
		    return "EOF detected in middle of attribute.";
		break;
	}
    }
    while (_CagdGetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    _CagdUnGetToken(StringToken);

    return NULL;
}

/*****************************************************************************
*   Routine to unget a single character from input stream.		     *
*****************************************************************************/
static void InputUnGetC(char c)
{
    GlblUnGetChar = c;
}

/*****************************************************************************
*   Routine to get a single character from input stream.		     *
* If input returns EOF wait until new inputs arrive (can happen if reading   *
* from a non io blocked socket).					     *
*****************************************************************************/
static char InputGetC(FILE *f)
{
    char c;

    do {
	if (GlblUnGetChar) {
	    c = GlblUnGetChar;

	    GlblUnGetChar = 0;
	}
	else if (GlblReadSocket)
	    c = SocClientReadCharNonBlock();
	else
	    c = getc(f);

	if (c == (char) EOF)
	    IritSleep(10);
    }
    while (c == (char) EOF);

    return c;
}

/*****************************************************************************
*   Routine to test for EOF condition in input stream.			     *
*****************************************************************************/
static int InputEOF(FILE *f)
{
    if (GlblReadSocket)
	return FALSE;
    else
	return feof(f);
}

/******************************************************************************
*   Routine to unget one token (on stack of UNGET_STACK_SIZE levels!)	      *
******************************************************************************/
void _CagdUnGetToken(char *StringToken)
{
    if (GlblTknStackSize >= UNGET_STACK_SIZE)
	FATAL_ERROR(CAGD_ERR_PARSER_STACK_OV);

    strcpy(GlblStringToken[GlblTknStackSize++], StringToken);
}

/******************************************************************************
*   Routine to get the next token out of the input file	f.		      *
* Returns the next token found,	as StringToken.				      *
* Note:	StringToken must be allocated before calling this routine!	      *
******************************************************************************/
static void GetStringToken(FILE *f, char *StringToken)
{
    int	Len;
    char *LocalStringToken,
	c = 0;

    if (GlblTknStackSize) {		       /* Get first the unget token. */
	strcpy(StringToken, GlblStringToken[--GlblTknStackSize]);
	return;
    }

    /* skip white spaces and comments: */
    while (!InputEOF(f) &&
	   ((c = InputGetC(f)) == ' ' || c == '\t' || c == '\n' || c == '#')) {
        /* Skip a comment if encounter one. */
	if (c == '#')
	    while (!InputEOF(f) && c != '\n')
		c = InputGetC(f);
	if (c == '\n')
	     _CagdGlblLineCount++;			 /* Count the lines. */
    }

    LocalStringToken = StringToken;
    if (c == '[')		      /* Its a token by itself so return it. */
	*LocalStringToken++ = c;	      /* Copy the token into string. */
    else {
	if (!InputEOF(f))
	    do
		*LocalStringToken++ = c;      /* Copy the token into string. */
	    while ((!InputEOF(f)) &&
		   ((c = InputGetC(f)) != ' ') && (c != '\t') && (c != '\n'));
	if (c == '\n')
	    InputUnGetC(c);		 /* Save it to be counted next time. */
    }
    *LocalStringToken =	0;					 /* Put eos. */

    /* The following handles the spacial case were we have XXXX] - we must   */
    /* split it	into two token XXXX and	], UnGetToken(']') and return XXXX:  */
    if ((StringToken[Len = strlen(StringToken)-1] == ']') && (Len > 0))	{
	/* Return CloseParen */
	_CagdUnGetToken(&StringToken[Len]);	         /* Save next token. */
	StringToken[Len] = 0;			/* Set end of string on "]". */
    }
}

/******************************************************************************
*   Routine to get the next token out of the input file	f as token number.    *
* Returns the next token number	found, with numeric result in NumericToken    *
* if TokenType is TOKEN_NUMBER.						      *
* Note:	StringToken must be allocated before calling this routine!	      *
******************************************************************************/
TokenNumType _CagdGetToken(FILE *f, char *StringToken)
{
    GetStringToken(f, StringToken);

    if (InputEOF(f))
	return TOKEN_EOF;

    if (!strcmp(StringToken, "["))
	return TOKEN_OPEN_PAREN;
    if (!strcmp(StringToken, "]"))
	return TOKEN_CLOSE_PAREN;

    if (!strcmp(StringToken, "BEZIER"))
	return TOKEN_BEZIER;
    if (!strcmp(StringToken, "BSPLINE"))
	return TOKEN_BSPLINE;
    if (!strcmp(StringToken, "POWER"))
	return TOKEN_POWER;

    if (!strcmp(StringToken, "CURVE"))
	return TOKEN_CURVE;
    if (!strcmp(StringToken, "SURFACE"))
	return TOKEN_SURFACE;

    if (!strcmp(StringToken, "PTYPE"))
	return TOKEN_PTYPE;
    if (!strcmp(StringToken, "NUMPTS"))
	return TOKEN_NUM_PTS;
    if (!strcmp(StringToken, "ORDER"))
	return TOKEN_ORDER;

    if (!strcmp(StringToken, "KV"))
	return TOKEN_KV;

    return TOKEN_OTHER;				  /* Must be number or name. */
}

/*****************************************************************************
*  Routine to request reading from a socket (if non zero) instead of a file. *
*****************************************************************************/
void CagdReadSocket(int ReadSocket)
{
    GlblReadSocket = ReadSocket;
}

/*****************************************************************************
* Set the floating point numbers printing format.			     *
*****************************************************************************/
void CagdSetFloatFormat(char *FloatFormat)
{
    strncpy(CagdFloatFormat, FloatFormat, LINE_LEN - 1);
}

/*****************************************************************************
* Convert a real number into a string.					     *
*****************************************************************************/
char *_CagdReal2Str(CagdRType R)
{
    static char Buffer[LINE_LEN];
    int j;

    if (ABS(R) < ZERO_NUM_EPSILON)
	R = 0.0;

    sprintf(Buffer, CagdFloatFormat, R);

    for (j = strlen(Buffer) - 1; Buffer[j] == ' ' && j > 0; j--);
    if (strchr(Buffer, '.') != NULL)
	for (; Buffer[j] == '0' && j > 0; j--);
    Buffer[j+1] = 0;

    return Buffer;
}
