/**************************************************************************
 *{@C
 *	Copyright:	1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *	Module:		Utilities
 *	Filename:	UT_FileHeader.c
 *
 *	Author:		Paul Obermeier
 *
 *	Description:	Definition of the data types and declaration of the
 *			functions dealing with the file header used in poSoft
 *			files.
 *			The file header is a single line of Ascii text 
 *			consisting of the following entries:
 *
 *			MagicString FileType FileSubType ContentType
 *
 *			MagicString: "poFile"
 *			FileType:    "Model",    "Image", "Curve", 
 *                                   "Particle", "Grid"
 *			FileSubType: Model: "poModel", "irModel",
 *			                    "awModel", "nastranModel"
 *			             Image: "poImage", "rawImage"
 *			             Curve: "Spline2D", "WayPoint"
 *                                   Grid:  "Modtran", "Density"
 *			CodingType:  "Ascii", "PortableBinary",
 *			             "Intel", "Motorola"
 *
 *	Additional documentation:
 *			None.
 *
 *	Exported functions:
 *			UT_FileHeaderRead
 *			UT_FileHeaderWrite
 *			UT_FileHeaderWriteStruct
 *			UT_GetFileType
 *			UT_GetFileSubType
 *			UT_GetFileCodingType
 *			UT_GetFileTypeName
 *			UT_GetFileSubTypeName
 *			UT_GetFileCodingTypeName
 *			UT_PrintFileTypeNames
 *			UT_PrintFileSubTypeNames
 *			UT_PrintFileCodingTypeNames
 *
 **************************************************************************/

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "UT_Compat.h"
#include "UT_Error.h"

#include "UT_FileHeader.h"

#define str_InvalidHeader         "Could not parse 'poSoft' header line.\n"
#define str_InvalidTypeName       "Invalid file type name in header (%s).\n"
#define str_InvalidSubTypeName    "Invalid file subtype name in header (%s).\n"
#define str_InvalidCodingTypeName "Invalid file coding type name in header (%s).\n"
#define str_InvalidVersionStr     "Invalid version string in header (%s).\n"
#define str_InvalidMajorVersion   "Major version in file (%d) is greater than " \
                                  "version of software (%d).\n"

/* The maximum length of a string entry in the file header. */
#define UT_HDR_ENTRY_LEN 20

/* The magic string entry identifying a poSoft file. */
#define UT_HDR_MAGIC "poFile"

/* The currently valid version major and minor numbers. */
#define UT_HDR_MAJOR 1
#define UT_HDR_MINOR 0

static const char *fileTypeNames[] = {
    "Model", "Image", "Curve", "Particle", "Grid"
};

static const char *fileSubTypeNames[] = {
    "poModel", "irModel", "awModel", "poImage", "rawImage",
    "Spline2D", "WayPoint", "poParticle", "Modtran", "Density",
    "nastranModel"
};

static const char *fileCodingTypeNames[] = {
    "Ascii", "PortableBinary", "Intel", "Motorola"
};

/***************************************************************************
 *[@e
 *	Name:		UT_FileHeaderRead 
 *
 *	Usage:		Read a file header from the beginning of a file.
 *			This function should be called immediately after
 *			opening a file for reading.
 *
 *	Synopsis:	UT_Bool UT_FileHeaderRead
 *				(FILE *fp,
 *                               UT_FileHeader *hdr)
 *
 *	Description:	Tries to fill in the structure pointed to by "hdr",
 *			reading the necessary information from file "fp".
 *			The first read after UT_FileHeaderRead will start at
 *			the byte just after the file header.
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *	See also:	UT_FileHeaderWrite
 *                      UT_FileHeaderWriteStruct
 *
 ***************************************************************************/

UT_Bool UT_FileHeaderRead (FILE *fp, UT_FileHeader *hdr) 
{
    int ch, major, minor;
    char versionStr[UT_HDR_ENTRY_LEN],
         fileTypeStr[UT_HDR_ENTRY_LEN],
         fileSubTypeStr[UT_HDR_ENTRY_LEN],
         fileCodingTypeStr[UT_HDR_ENTRY_LEN];

     /* OPA TODO: Should use UT_HDR_ENTRY_LEN as field width specifier */
    if (4 != fscanf (fp, UT_HDR_MAGIC "%20s %20s %20s %20s --", 
                     versionStr, fileTypeStr,
                     fileSubTypeStr, fileCodingTypeStr)) {
        UT_ErrSetNum (UT_ErrNotPoFile, str_InvalidHeader);
	return UT_False;
    }

    /* Read remaining CR and/or LF characters. */
    do {
	ch = getc (fp);
    } while (ch == '\n' || ch == '\r');
    (void) ungetc (ch, fp);

    /* Check the correctness of the file version. */
    if (2 != sscanf (versionStr, "%d.%d", &major, &minor)) {
        UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidVersionStr, versionStr);
        return UT_False;
    }
    hdr->majorVersion = major;
    hdr->minorVersion = minor;
    if (hdr->majorVersion > UT_HDR_MAJOR) {
        UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidMajorVersion, 
                      hdr->majorVersion, UT_HDR_MAJOR);
        return UT_False;
    }

    /* Check the correctness of the type names. */
    if (!UT_GetFileType       (fileTypeStr,       &hdr->fileType) ||
        !UT_GetFileSubType    (fileSubTypeStr,    &hdr->fileSubType) ||
        !UT_GetFileCodingType (fileCodingTypeStr, &hdr->fileCodingType)) {
	return UT_False;
    }
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:		UT_FileHeaderWrite
 *
 *	Usage:		Write a file header to the beginning of a file.
 *			This function should be called immediately after
 *			opening a file for writing.
 *
 *	Synopsis:	UT_Bool UT_FileHeaderWrite
 *                              (FILE              *fp, 
 *                               UT_FileType       fileType,
 *                               UT_FileSubType    fileSubType,
 *                               UT_FileCodingType fileCodingType)
 *
 *	Description:	The file header is constructed and written to file
 *			"fp". "fileType", "fileSubType" and "fileCodingType"
 *                      identify the type of information that will be written
 *                      to the file.
 *                      For a list of possible type names see the module
 *                      documentation or use the UT_PrintFile*TypeNames
 *                      functions.
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *      See also:       UT_FileHeaderWriteStruct
 *                      UT_FileHeaderRead
 *      		UT_PrintFileTypeNames
 *      		UT_PrintFileSubTypeNames
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

UT_Bool UT_FileHeaderWrite (FILE *fp, 
                         UT_FileType fileType,
			 UT_FileSubType fileSubType,
                         UT_FileCodingType fileCodingType) 
{
    if (0 > fprintf (fp, "%s %d.%d %s %s %s --\n", 
                     UT_HDR_MAGIC,
                     UT_HDR_MAJOR, UT_HDR_MINOR,
		     UT_GetFileTypeName(fileType),
                     UT_GetFileSubTypeName(fileSubType),
                     UT_GetFileCodingTypeName(fileCodingType))) {
	UT_ErrSetNum (UT_ErrFromOs(), "");
	return UT_False;
    }
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:		UT_FileHeaderWriteStruct
 *
 *	Usage:		Write a file header to the beginning of a file.
 *			This function should be called immediately after
 *			opening a file for writing.
 *
 *	Synopsis:	UT_Bool UT_FileHeaderWriteStruct
 *                              (FILE              *fp,
 *                               const UT_FileHeader *hdr) 
 *
 *	Description:	The file header is constructed and written to file
 *			"fp". "hdr" identifies the type of information that
 *			will be written to the file.
 *                      For a list of possible type names see the module 
 *                      documentation or use the UT_PrintFile*TypeNames
 *                      functions.
 *
 *	Return value:	UT_True if successful, otherwise UT_False.
 *
 *      See also:       UT_FileHeaderWrite
 *                      UT_FileHeaderRead
 *      		UT_PrintFileTypeNames
 *      		UT_PrintFileSubTypeNames
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

UT_Bool UT_FileHeaderWriteStruct (FILE *fp, const UT_FileHeader *hdr) 
{
    return UT_FileHeaderWrite (fp, hdr->fileType, hdr->fileSubType,
                               hdr->fileCodingType);
}

/***************************************************************************
 *[@e
 *	Name:		UT_GetFileTypeName
 *
 *	Usage:		Get the name of a file type number.
 *
 *	Synopsis:	const char *UT_GetFileTypeName
 *                               (UT_FileType fileType)
 *
 *	Description:	Possible file type names:
 *			    "Model", "Image", "Curve", "Particle", "Grid"
 *			    "UNKNOWN" if file type is invalid.
 *
 *	Return value:	A pointer to the name of the file type.
 *
 *      See also:	UT_GetFileSubTypeName
 *      		UT_GetFileCodingTypeName
 *      		UT_PrintFileTypeNames
 *
 ***************************************************************************/

const char *UT_GetFileTypeName (UT_FileType fileType)
{
    if (fileType < UT_FileTypeModel ||
	fileType >= UT_NumFileTypes) {
	return "UNKNOWN";
    }
    return fileTypeNames[(UInt16)fileType];
}

/***************************************************************************
 *[@e
 *	Name:		UT_GetFileSubTypeName
 *
 *	Usage:		Get the name of a file subtype number.
 *
 *	Synopsis:	const char *UT_GetFileSubTypeName
 *                               (UT_FileSubType fileSubType)
 *
 *	Description:	Possible file subtype names:
 *			    Model:    "poModel", "irModel", 
 *			              "awModel", "nastranModel"
 *			    Image:    "poImage", "rawImage"
 *			    Curve:    "poCurve"
 *			    Particle: "poParticle"
 *			    Grid:     "Modtran" "Density"
 *			    "UNKNOWN" if file subtype is invalid.
 *
 *	Return value:	A pointer to the name of the file subtype.
 *
 *      See also:	UT_GetFileTypeName
 *      		UT_GetFileCodingTypeName
 *      		UT_PrintFileSubTypeNames
 *
 ***************************************************************************/

const char *UT_GetFileSubTypeName (UT_FileSubType fileSubType)
{
    if (fileSubType < UT_FileSubTypePoModel ||
	fileSubType >= UT_NumFileSubTypes) {
	return "UNKNOWN";
    }
    return fileSubTypeNames[(UInt16)fileSubType];
}

/***************************************************************************
 *[@e
 *	Name:		UT_GetFileCodingTypeName
 *
 *	Usage:		Get the name of a file coding number.
 *
 *	Synopsis:	const char *UT_GetFileCodingTypeName
 *                               (UT_FileCodingType fileCodingType)
 *
 *	Description:	Possible coding type names:
 *			    "Ascii", "PortableBinary", "Intel", "Motorola"
 *			    "UNKNOWN" if file coding type is invalid.
 *
 *	Return value:	A pointer to the name of the file coding type.
 *
 *      See also:	UT_GetFileTypeName
 *      		UT_GetFileSubTypeName
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

const char *UT_GetFileCodingTypeName (UT_FileCodingType fileCodingType)
{
    if (fileCodingType < UT_FileCodingTypeAscii ||
	fileCodingType >= UT_NumFileCodingTypes) {
	return "UNKNOWN";
    }
    return fileCodingTypeNames[(UInt16)fileCodingType];
}

/***************************************************************************
 *[@e
 *	Name:		UT_GetFileType
 *
 *	Usage:		Get the type number of a file type name.
 *
 *	Synopsis:	UT_Bool UT_GetFileType
 *                               (const char *typeName, UT_FileType *type)
 *
 *	Description:    Get the type number of a file type name.
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *      See also:	UT_GetFileTypeName
 *      		UT_PrintFileTypeNames
 *
 ***************************************************************************/

UT_Bool UT_GetFileType (const char *typeName, UT_FileType *type)
{
    UInt16 i;

    assert (UT_NumFileTypes == sizeof(fileTypeNames)/sizeof(fileTypeNames[0]));

    for (i=0; i<sizeof(fileTypeNames)/sizeof(fileTypeNames[0]); i++) {
	if (strcmp (typeName, fileTypeNames[i]) == 0) {
            *type = (UT_FileType) i;
            return UT_True;
	}
    }
    UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidTypeName, typeName);
    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		UT_GetFileSubType
 *
 *	Usage:		Get the type number of a file subtype name.
 *
 *	Synopsis:	UT_Bool UT_GetFileSubType
 *                               (const char *typeName, UT_FileSubType *subType)
 *
 *	Description:    Get the type number of a file subtype name.
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *      See also:	UT_GetFileSubTypeName
 *      		UT_PrintFileSubTypeNames
 *
 ***************************************************************************/

UT_Bool UT_GetFileSubType (const char *typeName, UT_FileSubType *subType)
{
    UInt16 i;

    assert (UT_NumFileSubTypes ==
            sizeof(fileSubTypeNames)/sizeof(fileSubTypeNames[0]));

    for (i=0; i<sizeof(fileSubTypeNames)/sizeof(fileSubTypeNames[0]); i++) {
	if (strcmp (typeName, fileSubTypeNames[i]) == 0) {
            *subType = (UT_FileSubType) i;
            return UT_True;
	}
    }
    UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidSubTypeName, typeName);
    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		UT_GetFileCodingType
 *
 *	Usage:		Get the type number of a file coding type name.
 *
 *	Synopsis:	UT_Bool UT_GetFileCodingType
 *                              (const char *typeName, 
 *                               UT_FileCodingType *codingType)
 *
 *	Description:    Get the type number of a file coding type name.
 *
 *	Return value:	UT_True, if successful, otherwise UT_False.
 *
 *      See also:	UT_GetFileCodingTypeName
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

UT_Bool UT_GetFileCodingType (const char *typeName, UT_FileCodingType *codingType)
{
    UInt16 i;

    assert (UT_NumFileCodingTypes == 
            sizeof(fileCodingTypeNames)/sizeof(fileCodingTypeNames[0]));

    for (i=0; i<sizeof(fileCodingTypeNames)/sizeof(fileCodingTypeNames[0]); i++) {
	if (strcmp (typeName, fileCodingTypeNames[i]) == 0) {
            *codingType = (UT_FileCodingType) i;
            return UT_True;
	}
    }
    UT_ErrSetNum (UT_ErrParamInvalid, str_InvalidCodingTypeName, typeName);
    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		UT_PrintFileTypeNames
 *
 *	Usage:		Print all allowed file type names.
 *
 *	Synopsis:	void UT_PrintFileTypeNames (void)
 *
 *	Description:	Print all allowed file type names onto stdout.
 *
 *	Return value:	None.
 *
 *      See also:	UT_PrintFileSubTypeNames
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

void UT_PrintFileTypeNames (void)
{
    UInt16 i;

    assert (UT_NumFileTypes == sizeof(fileTypeNames)/sizeof(fileTypeNames[0]));

    printf ("Valid file type names are:\n  ");
    for (i=0; i<sizeof(fileTypeNames)/sizeof(fileTypeNames[0]); i++) {
	printf ("%s ", fileTypeNames[i]);
    }
    printf ("\n");
    return;
}

/***************************************************************************
 *[@e
 *	Name:		UT_PrintFileSubTypeNames
 *
 *	Usage:		Print all allowed file subtype names.
 *
 *	Synopsis:	void UT_PrintFileSubTypeNames (void)
 *
 *	Description:	Print all allowed file subtype names onto stdout.
 *
 *	Return value:	None.
 *
 *      See also:	UT_PrintFileSubTypeNames
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

void UT_PrintFileSubTypeNames (void)
{
    UInt16 i;

    assert (UT_NumFileSubTypes == 
            sizeof(fileSubTypeNames)/sizeof(fileSubTypeNames[0]));

    printf ("Valid file subtype names are:\n  ");
    for (i=0; i<sizeof(fileSubTypeNames)/sizeof(fileSubTypeNames[0]); i++) {
	printf ("%s ", fileSubTypeNames[i]);
    }
    printf ("\n");
    return;
}

/***************************************************************************
 *[@e
 *	Name:		UT_PrintFileCodingTypeNames
 *
 *	Usage:		Print all allowed file coding type names.
 *
 *	Synopsis:	void UT_PrintFileCodingTypeNames (void)
 *
 *	Description:	Print all allowed file coding type names onto stdout.
 *
 *	Return value:	None.
 *
 *      See also:	UT_PrintFileTypeNames
 *      		UT_PrintFileSubTypeNames
 *
 ***************************************************************************/

void UT_PrintFileCodingTypeNames (void)
{
    UInt16 i;

    assert (UT_NumFileCodingTypes ==
            sizeof(fileCodingTypeNames)/sizeof(fileCodingTypeNames[0]));

    printf ("Valid file coding type names are:\n  ");
    for (i=0; i<sizeof(fileCodingTypeNames)/sizeof(fileCodingTypeNames[0]); i++) {
	printf ("%s ", fileCodingTypeNames[i]);
    }
    printf ("\n");
    return;
}

/***************************************************************************
 *[@e
 *	Name:		UT_PrintFileVersion
 *
 *	Usage:		Print software file version.
 *
 *	Synopsis:	void UT_PrintFileVersion (void)
 *
 *	Description:	Print software file version.
 *
 *	Return value:	None.
 *
 *      See also:	UT_PrintFileTypeNames
 *      		UT_PrintFileSubTypeNames
 *      		UT_PrintFileCodingTypeNames
 *
 ***************************************************************************/

void UT_PrintFileVersion (void)
{
    printf ("Version of file parsing software:\n  ");
    printf ("%d.%d\n", UT_HDR_MAJOR, UT_HDR_MINOR);
    return;
}
