
#include <stdio.h>
#ifdef NIO_LIB_ONLY
#include "niohlu.h"
#include "nioNresDB.h"
#else
#include <ncarg/hlu/hlu.h>
#include <ncarg/hlu/NresDB.h>
#endif
#include "defs.h"
#include "NclFile.h"
#include "NclFileInterfaces.h"
#include <ctype.h>
#include <unistd.h>

typedef struct _NclFormatList NclFormatList;

struct _NclFormatList {
NclQuark  file_extension;
NclAddFileFormat format_func;
};

NclFormatList *formats;
int format_list_size = 32;
int num_formats = 0;
int grib_version;

void _NclRegisterFormat
#if	NhlNeedProto
(NclAddFileFormat thefunc,char* file_extension)
#else
(thefunc,file_extension)
NclAddFileFormat thefunc;
char* file_extension;
#endif
{
	static int first = 1;
	NclFormatList* tmp;

	if(first) {
		formats = (NclFormatList*)NclMalloc(
				(unsigned)sizeof(NclFormatList)
				*format_list_size);
		first = 0;
	}

	if(num_formats + 1 == format_list_size) {
		tmp = (NclFormatList*)NclRealloc((void*)formats,
				(unsigned)sizeof(NclFormatList) 
				*format_list_size * 2);
		if(tmp == NULL) {
			NhlPError(NhlFATAL,NhlEUNKNOWN,"A memory allocation error occurred could not register new format");
			return;
		} else {
			formats = tmp;
			format_list_size *= 2;
		}
	}
	formats[num_formats].file_extension = NrmStringToQuark(file_extension);
	formats[num_formats].format_func = thefunc;
	num_formats++;
	return;
}

NclFormatFunctionRecPtr _NclGetFormatFuncs
#if	NhlNeedProto
(NclQuark file_extension)
#else 
(file_extension)
	NclQuark file_extension;
#endif
{
	int i; 
	char ext[16];
        char *cp;
	NclQuark lfile_ext;

	lfile_ext = _NclGetLower(file_extension);
	
	for(i = 0; i<num_formats; i++) {
		if(formats[i].file_extension == lfile_ext) {
			return((*formats[i].format_func)());
		}
	}
	return(NULL);
}

NclFormatFunctionRecPtr _NclGetFormatFuncsWithAdvancedFileStructure(NclQuark file_extension)
{
	int i, n=1; 
	NclQuark lfile_ext;

	lfile_ext = _NclGetLower(file_extension);
	
	for(i = 0; i<num_formats; i++)
	{
		if(formats[i].file_extension == lfile_ext)
		{
			n = i+1;
			break;
		}
	}

	for(i = n; i<num_formats; i++)
	{
		if(formats[i].file_extension == lfile_ext)
			return((*formats[i].format_func)());
	}

	return(NULL);
}


logical _NclFormatEqual
#if NhlNeedProto
(
	NclQuark file_ext1, 
	NclQuark file_ext2
)
#else
(file_ext1, file_ext2)
NclQuark file_ext1;
NclQuark file_ext2;
#endif
{
	NclAddFileFormat format_func1 = NULL;
	char ext[16];
        char *cp;
	NclQuark qext1,qext2;
	int i;

	qext1 = _NclGetLower(file_ext1);
	for(i = 0; i<num_formats; i++) {
		if(formats[i].file_extension == qext1) {
			format_func1 = formats[i].format_func;
			break;
		}
	}
	if (!format_func1) 
		return 0;

	qext2 = _NclGetLower(file_ext2);
	for(i = 0; i<num_formats; i++) {
		if(formats[i].file_extension == qext2) {
			if (format_func1 == formats[i].format_func)
				return 1;
			return 0;
		}
	}
	return 0;
}

int _NclGribVersion
#if NhlNeedProto
(NclQuark path)
#else
(path)
    NclQuark path;
#endif
{
    ng_size_t gbuf_size = 1024;
    unsigned char buf[4 * gbuf_size];
    int len, version = -1;
    int j;

    void *vbuf;
    FILE    *fd;

    fd = fopen(NrmQuarkToString(path), "r");
    vbuf = (void *) NclMalloc(4 * getpagesize());
    setvbuf(fd, vbuf, _IOFBF, 4 * getpagesize());

    /*
     * Read file, look for sequence 'G' 'R' 'I' 'B' ; version will follow
     */
    (void) fseek(fd, 0L, SEEK_SET);
    while ((len = fread((void *) buf, 1, 4 * gbuf_size, fd)) > 0) {
            for (j = 0; j < len - 8; j++) {
		    /* look for "GRIB" indicator */
		    if (buf[j] != 'G') {
			    continue;
		    } else {
			    if ((buf[j + 1] == 'R' && buf[j + 2] == 'I' && buf[j + 3] == 'B')) {
				    version = buf[j + 7];
				    /* we have the version; return immediately */
				    fclose(fd);
				    NclFree(vbuf);
				    return version;
			    }
		    }
            }
    }

    if (fd) {
        fclose(fd);
    }

    NclFree(vbuf);
    return version;
}

int _NclIsGrib
#if NhlNeedProto
(char *path,
 ng_size_t limit)
#else
(path,limit)
    char *path;
    ng_size_t limit;
#endif
{
    ng_size_t j,len;
    ng_size_t total;
    char *vbuf;
    FILE    *fd;
    ng_size_t vbuflen;
    struct stat statbuf;
    int version;
    int griblen;
    char end[10];
    int ret4;


    if(stat(path,&statbuf) == -1) {
	    return 0;
    }

    fd = fopen(path, "r");
    vbuflen = statbuf.st_blksize;
    vbuf = (char*)NclMalloc(vbuflen*4);
    setvbuf(fd, (void *)vbuf, _IOFBF, vbuflen*4);

    /*
     * Read file, look for sequence 'G' 'R' 'I' 'B' ; version will follow
     */
    total = 0;
    (void) fseek(fd, 0L, SEEK_SET);
    while ((len = fread((void *) vbuf, 1, vbuflen*4, fd)) > 0) {
	    if (len < 32)
		    return 0;
            for (j = 0; j < len - 16; j++) {
		    /* look for "GRIB" indicator */
		    if (vbuf[j] != 'G') {
			    continue;
		    } else {
			    if ((vbuf[j + 1] == 'R' && vbuf[j + 2] == 'I' && vbuf[j + 3] == 'B')) {
				    version = vbuf[j + 7];
				    if (version == 1) {
					    griblen = UnsignedCnvtToDecimal(3,&vbuf[j + 4]);
				    }
				    else {
					    griblen = UnsignedCnvtToDecimal(4,&vbuf[j + 12]);
				    }
				    fseek(fd,j + griblen - 4, SEEK_SET);
				    ret4 = fread((void*)end,1,4,fd);
				    if(ret4 < 4) {
					    return 0;
				    }	 
				    if(!strncmp("7777",end,4)) {
					    fclose(fd);
					    NclFree(vbuf);
					    return 1;
				    }
				    return 0;
			    }
		    }
            }
	    total += len - 16;
	    if (total > limit) 
		    return 0;
	    (void) fseek(fd, total, SEEK_SET);
    }

    if (fd) {
        fclose(fd);
    }

    NclFree(vbuf);
    return 0;
}
