/******************************************************************************
* Bsp-Read.c - Bspline handling routines - read from file.		      *
*******************************************************************************
* Written by Gershon Elber, Aug. 90.					      *
******************************************************************************/

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

/******************************************************************************
* Reads and returns a bspline curve.					      *
* If error is found in reading the file, ErrStr is set to string describing   *
* it and Line to the line it occured in file.				      *
* If no error is detected *ErrStr = NULL.				      *
******************************************************************************/
CagdCrvStruct *BspCrvReadFromFile(char *FileName, char **ErrStr, int *ErrLine)
{
    FILE *f;
    char StringToken[LINE_LEN];
    CagdCrvStruct *Crv,
	*CrvTail = NULL,
	*CrvList = NULL;

    if ((f = fopen(FileName, "r")) == NULL) {
	*ErrStr = "File not found";
	return NULL;
    }

    while (_CagdGetToken(f, StringToken) == TOKEN_OPEN_PAREN) {
	_CagdUnGetToken(StringToken);
    	Crv = BspCrvReadFromFile2(f, FALSE, ErrStr, ErrLine);

	if (CrvList == NULL)
	    CrvList = CrvTail = Crv;
	else {
	    CrvTail -> Pnext = Crv;
	    CrvTail = Crv;
	}
    }

    fclose(f);

    return CrvList;
}

/******************************************************************************
* Reads and returns a bspline curve.					      *
* If NameWasRead is TRUE, it is assumed prefix "[CURVE BSPLINE" has already   *
* been read. This is useful for a global parser which invokes this routine.   *
* For exactly this reason, the file is NOT closed in the end.		      *
* If error is found in reading the file, ErrStr is set to string describing   *
* it and ErrLine to line it occured in file relative to begining of curve.    *
* If no error is detected *ErrStr is set to NULL.			      *
******************************************************************************/
CagdCrvStruct *BspCrvReadFromFile2(FILE *f, CagdBType NameWasRead,
						char **ErrStr, int *ErrLine)
{
    CagdPointType PType;
    TokenNumType Token;
    int i, j, Length, Order, MaxCoord;
    char StringToken[LINE_LEN];
    CagdCrvStruct *NewCrv;

    _CagdGlblLineCount = *ErrLine;

    if (!NameWasRead) {
	while ((Token = _CagdGetToken(f, StringToken)) != TOKEN_EOF &&
	       Token != TOKEN_OPEN_PAREN);

	/* We found beginning of definition - read one: */
	if (_CagdGetToken(f, StringToken) != TOKEN_CURVE ||
	    _CagdGetToken(f, StringToken) != TOKEN_BSPLINE) {
            *ErrStr = "CURVE BSPLINE key words expected";
	    *ErrLine = _CagdGlblLineCount;
	    return NULL;
	}
    }

    if ((Token = _CagdGetToken(f, StringToken)) == TOKEN_OPEN_PAREN) {
	if ((*ErrStr = _CagdGetCurveAttributes(f)) != NULL) {
            *ErrStr = "\"[\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    return NULL;
	}
    }
    else
	_CagdUnGetToken(StringToken);

    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &Length) != 1) {
	*ErrStr = "BSPLINE Number of points expected";
	*ErrLine = _CagdGlblLineCount;
	return NULL;
    }

    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &Order) != 1) {
	*ErrStr = "BSPLINE Order expected";
	*ErrLine = _CagdGlblLineCount;
	return NULL;
    }

    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	strlen(StringToken) != 2 ||
	(StringToken[0] != 'E' && StringToken[0] != 'P') ||
	!isdigit(StringToken[1]) ||
	atoi(&StringToken[1]) > CAGD_MAX_PT_COORD) {
	*ErrStr = "BSPLINE Point type expected";
	*ErrLine = _CagdGlblLineCount;
	return NULL;
    }

    PType = CAGD_MAKE_PT_TYPE(StringToken[0] == 'P', atoi(&StringToken[1]));

    NewCrv = BspCrvNew(Length, Order, PType);

    /* Read the knot vector first: */
    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OPEN_PAREN) {
	*ErrStr = "\"[\" expected";
	*ErrLine = _CagdGlblLineCount;
	CagdCrvFree(NewCrv);
	return NULL;
    }
    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_KV) {
	*ErrStr = "KV expected";
	*ErrLine = _CagdGlblLineCount;
	CagdCrvFree(NewCrv);
	return NULL;
    }
    for (i = 0; i < Order + Length; i++) {
	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	    sscanf(StringToken, CAGD_FLOAT_READ,
					     &NewCrv -> KnotVector[i]) != 1) {
	    *ErrStr = "Numeric data expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdCrvFree(NewCrv);
	    return NULL;
	}
    }
    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_CLOSE_PAREN) {
	*ErrStr = "\"]\" expected";
	*ErrLine = _CagdGlblLineCount;
	CagdCrvFree(NewCrv);
	return NULL;
    }

    /* Read the points themselves: */
    MaxCoord = CAGD_NUM_OF_PT_COORD(PType);
    for (i = 0; i < Length; i++) {
	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OPEN_PAREN) {
	    *ErrStr = "\"[\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdCrvFree(NewCrv);
	    return NULL;
	}
	if (CAGD_IS_RATIONAL_PT(PType)) {
	    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
		sscanf(StringToken, CAGD_FLOAT_READ,
					       &NewCrv -> Points[W][i]) != 1) {
		*ErrStr = "Numeric data expected";
		*ErrLine = _CagdGlblLineCount;
		CagdCrvFree(NewCrv);
		return NULL;
	    }
	}
	for (j = 1; j <= MaxCoord; j++) {
	    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
		sscanf(StringToken, CAGD_FLOAT_READ,
						&NewCrv -> Points[j][i]) != 1) {
		*ErrStr = "Numeric data expected";
		*ErrLine = _CagdGlblLineCount;
		CagdCrvFree(NewCrv);
		return NULL;
	    }
	}
	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_CLOSE_PAREN) {
	    *ErrStr = "\"]\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdCrvFree(NewCrv);
	    return NULL;
	}
    }
    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_CLOSE_PAREN) {
	*ErrStr = "\"]\" expected";
	*ErrLine = _CagdGlblLineCount;
	CagdCrvFree(NewCrv);
	return NULL;
    }

    *ErrLine = _CagdGlblLineCount;
    *ErrStr = NULL;

    return NewCrv;
}

/******************************************************************************
* Reads and returns a bspline surface.					      *
* If error is found in reading the file, ErrStr is set to string describing   *
* it and Line to the line it occured in file.				      *
* If no error is detected *ErrStr = NULL.				      *
******************************************************************************/
CagdSrfStruct *BspSrfReadFromFile(char *FileName, char **ErrStr, int *ErrLine)
{
    FILE *f;
    char StringToken[LINE_LEN];
    CagdSrfStruct *Srf,
	*SrfTail = NULL,
	*SrfList = NULL;

    if ((f = fopen(FileName, "r")) == NULL) {
	*ErrStr = "File not found";
	return NULL;
    }

    while (_CagdGetToken(f, StringToken) == TOKEN_OPEN_PAREN) {
	_CagdUnGetToken(StringToken);
    	Srf = BspSrfReadFromFile2(f, FALSE, ErrStr, ErrLine);

	if (SrfList == NULL)
	    SrfList = SrfTail = Srf;
	else {
	    SrfTail -> Pnext = Srf;
	    SrfTail = Srf;
	}
    }

    fclose(f);

    return SrfList;
}

/******************************************************************************
* Reads and returns a bspline surface.					      *
* If NameWasRead is TRUE, it is assumed prefix "[SURFACE BSPLINE" has already *
* been read. This is useful for a global parser which invokes this routine.   *
* For exactly this reason, the file is NOT closed in the end.		      *
* If error is found in reading the file, ErrStr is set to string describing   *
* it and ErrLine to the line it occured in file.		  	      *
* If no error is detected *ErrStr = NULL.				      *
******************************************************************************/
CagdSrfStruct *BspSrfReadFromFile2(FILE *f, CagdBType NameWasRead,
						char **ErrStr, int *ErrLine)
{
    int i, j, k, Len, ULength, VLength, UOrder, VOrder, MaxCoord;
    char StringToken[LINE_LEN];
    CagdRType *KnotVector;
    CagdPointType PType;
    TokenNumType Token;
    CagdSrfStruct *NewSrf;

    _CagdGlblLineCount = *ErrLine;

    if (!NameWasRead) {
	while ((Token = _CagdGetToken(f, StringToken)) != TOKEN_EOF &&
	       Token != TOKEN_OPEN_PAREN);

	/* We found beginning of definition - read one: */
	if (_CagdGetToken(f, StringToken) != TOKEN_SURFACE ||
	    _CagdGetToken(f, StringToken) != TOKEN_BSPLINE) {
	    *ErrStr = "SURFACE BSPLINE key words expected";
	    *ErrLine = _CagdGlblLineCount;
	    return NULL;
        }
    }

    if ((Token = _CagdGetToken(f, StringToken)) == TOKEN_OPEN_PAREN) {
	if ((*ErrStr = _CagdGetSurfaceAttributes(f)) != NULL) {
	    *ErrStr = "\"[\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    return NULL;
	}
    }
    else
	_CagdUnGetToken(StringToken);

    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
        sscanf(StringToken, "%d", &ULength) != 1 ||
	(Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &VLength) != 1) {
	*ErrStr = "BSPLINE Number of points expected";
	*ErrLine = _CagdGlblLineCount;
	return NULL;
    }
    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &UOrder) != 1 ||
	(Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &VOrder) != 1) {
	*ErrStr = "BSPLINE Order expected";
	*ErrLine = _CagdGlblLineCount;
	return NULL;
    }

    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
        strlen(StringToken) != 2 ||
	(StringToken[0] != 'E' && StringToken[0] != 'P') ||
	!isdigit(StringToken[1]) ||
	atoi(&StringToken[1]) > CAGD_MAX_PT_COORD) {
	*ErrStr = "BSPLINE Point type expected";
	*ErrLine = _CagdGlblLineCount;
	return NULL;
    }
    PType = CAGD_MAKE_PT_TYPE(StringToken[0] == 'P', atoi(&StringToken[1]));

    NewSrf = BspSrfNew(ULength, VLength, UOrder, VOrder, PType);

    /* Read the knot vectors first: */
    for (k = 0; k < 2; k++) {
	if (k == 0) {
	    KnotVector = NewSrf -> UKnotVector;
	    Len = NewSrf -> UOrder + NewSrf -> ULength;
	}
	else {
	    KnotVector = NewSrf -> VKnotVector;
	    Len = NewSrf -> VOrder + NewSrf -> VLength;
	}

    	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OPEN_PAREN) {
    	    *ErrStr = "\"[\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdSrfFree(NewSrf);
	    return NULL;
    	}
    	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_KV) {
	    *ErrStr = "KV expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdSrfFree(NewSrf);
	    return NULL;
	}

	for (i = 0; i < Len; i++) {
	    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
    		sscanf(StringToken, CAGD_FLOAT_READ, &KnotVector[i]) != 1) {
    		*ErrStr = "Numeric data expected";
		*ErrLine = _CagdGlblLineCount;
		CagdSrfFree(NewSrf);
		return NULL;
	    }
	}

	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_CLOSE_PAREN) {
	    *ErrStr = "\"]\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdSrfFree(NewSrf);
	    return NULL;
	}
    }

    /* Read the points themselves: */
    MaxCoord = CAGD_NUM_OF_PT_COORD(PType);
    for (i = 0; i < ULength * VLength; i++) {
	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OPEN_PAREN) {
	    *ErrStr = "\"[\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdSrfFree(NewSrf);
	    return NULL;
	}
	if (CAGD_IS_RATIONAL_PT(PType)) {
	    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	        sscanf(StringToken, CAGD_FLOAT_READ,
						&NewSrf -> Points[W][i]) != 1) {
		*ErrStr = "Numeric data expected";
		*ErrLine = _CagdGlblLineCount;
		CagdSrfFree(NewSrf);
		return NULL;
	    }
	}
	for (j = 1; j <= MaxCoord; j++) {
	    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_OTHER ||
	        sscanf(StringToken, CAGD_FLOAT_READ,
						&NewSrf -> Points[j][i]) != 1) {
		*ErrStr = "Numeric data expected";
		*ErrLine = _CagdGlblLineCount;
		CagdSrfFree(NewSrf);
		return NULL;
	    }
	}
	if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_CLOSE_PAREN) {
	    *ErrStr = "\"]\" expected";
	    *ErrLine = _CagdGlblLineCount;
	    CagdSrfFree(NewSrf);
	    return NULL;
	}
    }
    if ((Token = _CagdGetToken(f, StringToken)) != TOKEN_CLOSE_PAREN) {
        *ErrStr = "\"]\" expected";
	*ErrLine = _CagdGlblLineCount;
	CagdSrfFree(NewSrf);
	return NULL;
    }

    *ErrStr = NULL;
    *ErrLine = _CagdGlblLineCount;

    return NewSrf;
}
