/* :ts=8 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "general.h"
#include "globals.h"
#include "intui.h"
#include "spl_util.h"
#include "file.h"

#define Size_JMan_Point_Table 	1000
static int JMan_Point_Table_Idx;
static short *JMan_Point_Table;

void StrTrim(char *Out, char *In)
/************************************************************************/
/*									*/
/* Remove leading and trailing spaces from In, and copy to Out.		*/
/*									*/
/************************************************************************/
{
    int	Last, i, j;

    if (In[0] == '\0') {

        Out[0] = '\0';
	return;

    } /* if */

    for (Last = strlen(In)-1; isspace(In[Last]); Last--) ;

    for (i = 0; isspace(In[i]); i++) ;

    for (j=0; i <= Last; i++,j++) Out[j] = In[i];

    Out[j] = '\0';

} /* StrTrim */

static 
Boolean_T Read_Line(FILE *Stream, char *Line)
/************************************************************************/
/*									*/
/* Read next line from stream 'Stream'. Return it in 'Line'.		*/
/* Ignore blank lines and lines starting with '#'.			*/
/*									*/
/* The function will return TRUE in case of error or eof.		*/
/*									*/
/************************************************************************/
{

    while (fgets(Line, Buffer_Length, Stream) != NULL) {

	Line[Buffer_Length] = '\0';

	StrTrim(Line, Line);

	if (Line[0] != '#' && Line[0] != '\0') return(FALSE);

    } /* while */

    return(TRUE);

} /* Read_Line */

static 
void Display_Open_Error_Msg(char *Filename)
/************************************************************************/
/*									*/
/*									*/
/************************************************************************/
{
    sprintf(Error_Msg, "Couldn't open %s", Filename);
    Display_Message(Error_Msg);
} /* Display_Open_Error_Msg */

Boolean_T Object_File_Write(char *Filename)
/************************************************************************/
/*									*/
/*									*/
/************************************************************************/
{
    FILE	*Stream;
    Spline_T	*Spline;
    Knot_T	*Knot;
    int		i, j;

    Stream = fopen(Filename, "w");
    if (Stream == NULL) {
	Display_Open_Error_Msg(Filename);
	return(TRUE);
    }

    Display_Status("Saving object...");
    Set_Pointer(TRUE);

    fprintf(Stream, "#\n");
    fprintf(Stream, "# Patches created by ICoons\n");
    fprintf(Stream, "#\n");

    fprintf(Stream, "#\n");
    fprintf(Stream, "#--------------------------------------------------\n");
    fprintf(Stream, "# Points:\n");
    fprintf(Stream, "#--------------------------------------------------\n");
    fprintf(Stream, "#\n");

    for (i = 0; i < Max_Nbr_Points; i++) {

        if (Points[i].Reference_Count > 0) {

    	    fprintf(Stream, "P %d %d %lf %lf %lf\n",
	    			i, 
				Points[i].Reference_Count,
				Points[i].Pos[0],
				Points[i].Pos[1],
				Points[i].Pos[2]);

	} /* if */

    } /* for */

    fprintf(Stream, "#\n");
    fprintf(Stream, "#--------------------------------------------------\n");
    fprintf(Stream, "# Splines:\n");
    fprintf(Stream, "#--------------------------------------------------\n");
    fprintf(Stream, "#\n");

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

	if (Spline->Nbr_Knots > 0) {

	    fprintf(Stream, "S %d %d\n", 
	    		Spline->Nbr_Knots, 
			Spline->Loop);

	    for (j = 0, Knot = Spline->First; 
 		 j < Spline->Nbr_Knots; j++, Knot = Knot->Next) {

		fprintf(Stream, "K %d %lf %lf %lf\n",
				Knot->Point_Id,
				Knot->Tension,
				Knot->Bias,
				Knot->Continuity);

	    } /* for */

    	    fprintf(Stream, "#\n");
    	    fprintf(Stream, 
	    	"#--------------------------------------------------\n");
    	    fprintf(Stream, "#\n");

	} /* if */

    } /* for */

    fclose(Stream);
    Set_Pointer(FALSE);
    Display_Status(NULL);

    return(FALSE);

} /* Object_File_Write */

Boolean_T Object_File_Read(char *Filename)
/************************************************************************/
/*									*/
/*									*/
/************************************************************************/
{
    FILE	*Stream;
    char	Line[Buffer_Length+1];
    Spline_T	*Spline;
    Knot_T	*Knot;
    int		Point_Id;
    int		New_Point_Id;
    int		Reference_Count;
    Vector_T	Pos;
    int		Nbr_Knots;
    int		Loop;
    double	Tension, Bias, Continuity;

    Knot   = NULL;
    Spline = NULL;

    Stream = fopen(Filename, "r");
    if (Stream == NULL) {
	Display_Open_Error_Msg(Filename);
	return(TRUE);
    }

    Display_Status("Loading object...");
    Set_Pointer(TRUE);

    for (Point_Id = 0; Point_Id < Max_Nbr_Points; Point_Id++) 
						Points[Point_Id].Work = -1;

    while (! Read_Line(Stream, Line)) {

	if (Line[0] == 'P') {	 	/* Point:		*/

    	    sscanf(Line, "P %d %d %lf %lf %lf",
	    			&Point_Id, 
				&Reference_Count,
				&Pos[0], &Pos[1], &Pos[2]);

	    if (Point_Id >= 0 && Point_Id < Max_Nbr_Points) {

		New_Point_Id = Point_New(Pos);
		if (New_Point_Id < 0) {
	       	    Display_Message("No more space for points.");
    		    Set_Pointer(FALSE);
		    fclose(Stream);
	            return(TRUE);		
		}
		Points[Point_Id].Work = New_Point_Id;

	    } /* if */

	} /* if */

	if (Line[0] == 'S') { 	/* Spline start	:		*/

	    sscanf(Line, "S %d %d",  &Nbr_Knots,  &Loop);

	    Spline = Spline_New();
	    if (Spline == NULL) {
	       	Display_Message("Couldn't allocate memory for new spline");
    		Set_Pointer(FALSE);
		fclose(Stream);
	        return(TRUE);		
	    }

	    Spline->Flags     = 0;
	    Spline->Nbr_Knots = 0;
	    Spline_Loop(Spline, Loop);
	    Knot 	      = NULL;

         } /* if */

	if (Line[0] == 'K') { 	/* Knot :		*/

	    sscanf(Line, "K %d %lf %lf %lf",
				&Point_Id,
				&Tension,
				&Bias,
				&Continuity);

	    if (Spline != NULL) {
		
		Knot = Knot_New(Spline, Knot);
		if (Knot == NULL) {
		    Display_Message("Couldn't allocate memory for new knot");
    		    Set_Pointer(FALSE);
		    fclose(Stream);
	   	    return(TRUE);
		} /* if */

		if (Point_Id < 0 || Point_Id >= Max_Nbr_Points) {
		    Display_Message("Invalid point id in file");
    		    Set_Pointer(FALSE);
		    fclose(Stream);
	   	    return(TRUE);
		}
		/* We have probably moved this point to another position */
		/* Get new point id.					 */

		New_Point_Id = Points[Point_Id].Work;
		if (New_Point_Id < 0 || New_Point_Id >= Max_Nbr_Points) {
		    Display_Message("Inconsistent point id in file");
    		    Set_Pointer(FALSE);
		    fclose(Stream);
	   	    return(TRUE);
		}
	        Knot->Point_Id   = New_Point_Id;
	        Knot->Tension    = Tension;
	        Knot->Bias   	 = Bias;
	        Knot->Continuity = Continuity;
	        Knot->Flags      = 0;

		Points[New_Point_Id].Reference_Count++;

	    } /* if */

	} /* if */

    } /* while */

    fclose(Stream);
    Display_Status(NULL);
    Set_Pointer(FALSE);

    return(FALSE);

} /* Object_File_Read */

static
Boolean_T JMan_Header_Read(FILE *Stream, int *Nbr_Splines)
/************************************************************************/
/*									*/
/*									*/
/************************************************************************/
{
    char	Line[Buffer_Length+1];
    if (Read_Line(Stream, Line)) goto Eof;
    if (strncmp(Line, "SCUL", 4) != 0) {
	Display_Message("Not a Journeyman .seg file");
	return(TRUE);
    } /* if */

    if (Read_Line(Stream, Line)) goto Eof;	/* A			*/
    if (Read_Line(Stream, Line)) goto Eof;	/* MinPos MaxPos	*/
    if (Read_Line(Stream, Line)) goto Eof;	/* Pos 			*/

    if (Read_Line(Stream, Line)) goto Eof;	/* Nbr_Splines		*/
    *Nbr_Splines = atoi(Line);

    if (*Nbr_Splines < 1) {

	sprintf(Error_Msg, "Number of splines (%s) out of range", Line);
	Display_Message(Error_Msg);
	return(TRUE);

    } /* if */

    return(FALSE);

Eof:
    Display_Message("Eof before expected");
    fclose(Stream);
    return(TRUE);

} /* JMan_Header_Read */

static
Boolean_T JMan_Knot_Read(FILE *Stream, Spline_T *Spline, Knot_T **Knot,
				int *Nbr_Points)
/************************************************************************/
/*									*/
/*									*/
/************************************************************************/
{
    char	Line[Buffer_Length+1];
    int		i1, i2, i3;
    int		Idx;
    double	X, Y, Z;
    double	Tension, Bias, Continuity;
    double	Alfa1, Gamma1, Magnitude1;
    double	Alfa2, Gamma2, Magnitude2;

    JMan_Point_Table_Idx++;

    if (Read_Line(Stream, Line)) goto Eof;	/* i1 i2 i3	*/
    sscanf(Line, "%d %d %d", &i1, &i2, &i3);
    
    if ((i1 != 0 && i1 != 1 && i1 != 4 && i1 != 5) ||
	(i2 != 8) ||
	(i3 != 0 && i3 != 1)) {

	sprintf(Error_Msg, "Format error in line \n'%s'", Line);
	Display_Message(Error_Msg);
	return(TRUE);

    } /* if */

    if (i1 & 4) Spline_Loop(Spline, TRUE);

    if (Read_Line(Stream, Line)) goto Eof;	/* Pos or id		*/
    if (i3 == 0) {
	sscanf(Line, "%lf %lf %lf",&X, &Y, &Z);

	if (*Nbr_Points >= Max_Nbr_Points - 1) {
	    Display_Message("Too many points");
	    return(TRUE);
	} /* if */

	Points[*Nbr_Points].Reference_Count  = 0;
	Points[*Nbr_Points].Flags            = 0;
	Points[*Nbr_Points].Pos[0]           = X;
	Points[*Nbr_Points].Pos[1]           = Y;
	Points[*Nbr_Points].Pos[2]           = Z;

	Idx = JMan_Point_Table_Idx;
	JMan_Point_Table[Idx] = *Nbr_Points;
	*Nbr_Points += 1;

    } else {

	sscanf(Line, "%d",&Idx);
	JMan_Point_Table[JMan_Point_Table_Idx] = -Idx;

	if (Idx < 1 || Idx >= Size_JMan_Point_Table) {
	    sprintf(Error_Msg, "Invalid point id %d)", Idx);
	    Display_Message(Error_Msg);
	    return(TRUE);
	} /* if */

    } /* if .. else .. */
    *Knot = Knot_New(Spline, *Knot);
    if (*Knot == NULL) {
	Display_Message("Couldn't allocate memory for new knot");
	return(TRUE);
    } /* if */

    (*Knot)->Point_Id   = Idx;
    (*Knot)->Flags      = 0;

    if (Read_Line(Stream, Line)) goto Eof;	/* Spline parm 1	*/
    sscanf(Line, "%lf %lf %lf",&Alfa1, &Gamma1, &Magnitude1);

    if (Read_Line(Stream, Line)) goto Eof;	/* Spline parm 2	*/
    sscanf(Line, "%lf %lf %lf",&Alfa2, &Gamma2, &Magnitude2);

    if (i1 & 1) Tension 	= 0.0;	/* "Smooth"	*/
    else	Tension		= 1.0;	/* "Peak"	*/
    Bias	= 0.0;
    Continuity	= 0.0;

    (*Knot)->Tension    = Tension;
    (*Knot)->Bias	     = Bias;
    (*Knot)->Continuity = Continuity;

    return(FALSE);

Eof:
    Display_Message("Eof before expected");
    fclose(Stream);
    return(TRUE);

} /* JMan_Knot_Read */

Boolean_T JMan_File_Read(char *Filename)
/************************************************************************/
/*									*/
/*									*/
/************************************************************************/
{
    FILE	*Stream;
    char	Line[Buffer_Length+1];
    Spline_T	*Spline;
    Knot_T	*Knot;
    int		Spline_Id;
    int		Knot_Id;
    int		Point_Id;
    int		Nbr_Points;
    int		Nbr_Splines;
    int		Nbr_Knots;
    int		i;
    short	Point_Table[Size_JMan_Point_Table];

    Display_Status("Loading Journeyman object...");
    Set_Pointer(TRUE);

    for (i = 0; i < Size_JMan_Point_Table; i++) Point_Table[i] = 0;
    JMan_Point_Table_Idx = 0;
    JMan_Point_Table     = Point_Table;

    Nbr_Points  = 0;
    Spline	= NULL;

    Splines_Init();

    Stream = fopen(Filename, "r");
    if (Stream == NULL) {
	Display_Open_Error_Msg(Filename);
        Set_Pointer(FALSE);
	return(TRUE);
    } /* if */

    if (JMan_Header_Read(Stream, &Nbr_Splines)) {

        Set_Pointer(FALSE);
	fclose(Stream);
        return(TRUE);

    } /* if */

    for (Spline_Id = 0; Spline_Id < Nbr_Splines; Spline_Id++) {
  
        Spline = Spline_New();
	if (Spline == NULL) {
	    Display_Message("Couldn't allocate memory for new spline");
            Set_Pointer(FALSE);
	    fclose(Stream);
	    Splines_Init();
	    return(TRUE);		
	}
	
	Spline->Flags     = 0;
	Spline->Nbr_Knots = 0;
	/* Spline->Loop      = FALSE; */

    	if (Read_Line(Stream, Line)) goto Eof;
	Nbr_Knots = atoi(Line);

	if (Nbr_Knots < 1) {

	    sprintf(Error_Msg, "Nbr of knots (%s) out of range", Line);
	    Display_Message(Error_Msg);
            Set_Pointer(FALSE);
	    fclose(Stream);
	    Splines_Init();
	    return(TRUE);

	} /* if */

        Knot = NULL;
	for (Knot_Id = 0; Knot_Id < Nbr_Knots; Knot_Id++) {

	    sprintf(Error_Msg, "Reading spline %d", Spline_Id);
            Display_Status(Error_Msg);
	
	    if (JMan_Knot_Read(Stream, Spline, &Knot, &Nbr_Points)) {

		fclose(Stream);
		Splines_Init();
                Set_Pointer(FALSE);
		return(TRUE);

	    } /* if */
	
	} /* for */
	Spline->Nbr_Knots = Nbr_Knots;
	
    } /* for */

    fclose(Stream);

	/****************************************************************/
	/*								*/
	/* Now postprocess the data.					*/
	/* Right now the knots points to the JMan_Point_Table, and not	*/
	/* to the points, furthermore point reference-counts are wrong	*/
	/* So do the following:						*/
	/*								*/
	/* For each knot:						*/
	/*								*/
	/*    Get idx into Jman point table.				*/
	/*    get idx to point from JMan point table.			*/
	/*    If this idx < 0, then this is itself a reference to the	*/
	/*    point table, so fetch this idx from the table.		*/
	/*    Continue with this while the idx is < 0.			*/
	/*    Now set real point_id in the knot and update the reference*/
	/*    count.							*/
	/*								*/
	/****************************************************************/
    Display_Status("Post Processing data");

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {
   	for (Knot_Id = 0, Knot = Spline->First; 
	     Knot_Id < Spline->Nbr_Knots; Knot_Id++, Knot = Knot->Next) {


		/* Get idx into JMan point table */
	
	    i = Knot->Point_Id;
	    if (i < 1 || i >= Size_JMan_Point_Table) {
		sprintf(Error_Msg, "Unused index (%d) in point table", i);
		Display_Message(Error_Msg);
		Splines_Init();
                Set_Pointer(FALSE);
		return(TRUE);
	    } /* if */

	    while ( (Point_Id = JMan_Point_Table[i]) < 0) {

		i = -Point_Id;

	        if (i < 1 || i >= Size_JMan_Point_Table) {
		    sprintf(Error_Msg, "Unused index (%d) in point table", i);
		    Display_Message(Error_Msg);
		    Splines_Init();
                    Set_Pointer(FALSE);
		    return(TRUE);
	        } /* if */

	    } /* while */

	    if (Point_Id < 0 || Point_Id > Max_Nbr_Points) {
		sprintf(Error_Msg, "Bad reference (%d) in point table %d", Point_Id, i);
		Display_Message(Error_Msg);
		Splines_Init();
                Set_Pointer(FALSE);
		return(TRUE);
	    } /* if */

	    Points[Point_Id].Reference_Count++;
	    Knot->Point_Id = Point_Id;

  	} /* for */ 

    } /* for */

    Display_Status(NULL);
    Set_Pointer(FALSE);

    return(FALSE);

Eof:
    Display_Status(NULL);
    Display_Message("Eof before expected");
    fclose(Stream);
    Splines_Init();
    Set_Pointer(FALSE);
    return(TRUE);
    

} /* JMan_File_Read */

Boolean_T Config_File_Write(char *Filename)
/************************************************************************/
/*									*/
/* Write the config file with the name 'Filename'.			*/
/*									*/
/* Return TRUE in case of error.					*/
/*									*/
/************************************************************************/
{
    FILE	*Stream;
    char	Line[Buffer_Length+1];
    double	D1, D2, D3;

    Stream = fopen(Filename, "w");
    if (Stream == NULL)  {
	Display_Open_Error_Msg(Filename);
	return(TRUE);
    }
    D1 = Delay_Draw_Seconds;
    D2 = Delay_Draw_Micros;
    D3 = D1 + D2 / 1000000.0;

    fprintf(Stream, "#\n");
    fprintf(Stream, "#Configuration file for ICoons version 1.0\n");
    fprintf(Stream, "#\n");
    fprintf(Stream, "Delay_Draw        = %lf\n", D3);

    fprintf(Stream, "Spline_Resolution = %d\n", Spline_Resolution);
    fprintf(Stream, "Patch_Resolution  = %d\n", Patch_Resolution);
    fprintf(Stream, "Backface_Culling  = %d\n", Backface_Culling ? 'Y' : 'N');

    fprintf(Stream, "Grid_Size         = %lf\n", Grid_Size);
    fprintf(Stream, "Grid_Active       = %c\n", Grid_Active ? 'Y' : 'N');
    fprintf(Stream, "Grid_Snap_Active  = %c\n", Grid_Snap_Active ? 'Y' : 'N');

    fprintf(Stream, "MQ_Size_KnotInfo  = %d\n", MQ_Size_KnotInfo);
    fprintf(Stream, "MQ_Size_Rotate_P  = %d\n", MQ_Size_Rotate_P);
    fprintf(Stream, "MQ_Size_Rotate_G  = %d\n", MQ_Size_Rotate_G);
    fprintf(Stream, "MQ_Size_Scale_G   = %d\n", MQ_Size_Scale_G);
    fprintf(Stream, "MQ_Size_Move_G    = %d\n", MQ_Size_Move_G);
    fprintf(Stream, "MQ_Size_Move      = %d\n", MQ_Size_Move);

    fclose(Stream);

    return(FALSE);

} /* Config_File_Write */

Boolean_T Config_File_Read(char *Filename)
/************************************************************************/
/*									*/
/* Read the config file with the name 'Filename'.			*/
/*									*/
/* Return TRUE in case of error.					*/
/*									*/
/************************************************************************/
{
    FILE	*Stream;
    char	Line[Buffer_Length+1];
    char	*Name, *Value;
    double	D1, D2, D3;
    long	L;

    Stream = fopen(Filename, "r");
    if (Stream == NULL) {

	sprintf(Line, "s:%s", Filename);

        Stream = fopen(Line, "r");
        if (Stream == NULL)  {
	    Display_Open_Error_Msg(Filename);
	    return(TRUE);
	}

    } /* if */

    while (! Read_Line(Stream, Line)) {

	Name = strtok(Line, "=");
	if (Name == NULL) continue;
	Value = strtok(NULL, "\n");
	if (Value == NULL) continue;
	StrTrim(Name, Name);

	if (strcmp(Name, "Delay_Draw") == 0) {

	    D1 = atof(Value);
	    if (D1 <= 0.0) continue;
	    D3 = modf(D1, &D2);
	    Delay_Draw_Seconds = D2;
	    Delay_Draw_Micros  = D3 * 1000000.0;

	} else if (strcmp(Name, "Spline_Resolution") == 0) {

	    L = atoi(Value);
	    if (L > 0 && L <= Max_Spline_Resolution) Spline_Resolution = L;

	} else if (strcmp(Name, "Patch_Resolution") == 0) {

	    L = atoi(Value);
	    if (L > 0 && L <= Max_Patch_Resolution) Patch_Resolution = L;

	} else if (strcmp(Name, "Backface_Culling") == 0) {

	    StrTrim(Value, Value);
	    Backface_Culling = (*Value == 'y' || *Value == 'Y');

	} else if (strcmp(Name, "Grid_Size") == 0) {

	    D1 = atof(Value);
	    if (D1 > 0.5 && D1 < 500.0) Grid_Size = D1;

	} else if (strcmp(Name, "Grid_Active") == 0) {

	    StrTrim(Value, Value);
	    Grid_Active = (*Value == 'y' || *Value == 'Y');

	} else if (strcmp(Name, "Grid_Snap_Active") == 0) {

	    StrTrim(Value, Value);
	    Grid_Snap_Active = (*Value == 'y' || *Value == 'Y');

	} else if (strcmp(Value, "MQ_Size_KnotInfo") == 0) {

	    L = atoi(Value);
	    if (L >= 0 && L <= 5) MQ_Size_KnotInfo = L;

	} else if (strcmp(Value, "MQ_Size_Rotate_P") == 0) {

	    L = atoi(Value);
	    if (L >= 0 && L <= 5) MQ_Size_Rotate_P = L;

	} else if (strcmp(Value, "MQ_Size_Rotate_G") == 0) {

	    L = atoi(Value);
	    if (L >= 0 && L <= 5) MQ_Size_Rotate_G = L;

	} else if (strcmp(Value, "MQ_Size_Scale_G") == 0) {

	    L = atoi(Value);
	    if (L >= 0 && L <= 5) MQ_Size_Scale_G = L;

	} else if (strcmp(Value, "MQ_Size_Move_G") == 0) {

	    L = atoi(Value);
	    if (L >= 0 && L <= 5) MQ_Size_Move_G = L;

	} else if (strcmp(Value, "MQ_Size_Move") == 0) {

	    L = atoi(Value);
	    if (L >= 0 && L <= 5) MQ_Size_Move = L;

	} /* if .. else .. else .. */
	
    } /* while */

    fclose(Stream);

    return(FALSE);

} /* Config_File_Read */
