#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "exif.h"
#include "jpeg.h"

static char error[256];

struct exiftags *et = NULL;
struct exifprop *ep = NULL;
unsigned short dumplvl = 0;

static int
read_data(char *fname)
{
	static char _file_name[1024] = "";

        int mark, first = 0;
        unsigned int len, rlen;
	unsigned char *exifbuf = NULL;

	FILE *fpn;
	char *mode;

#ifdef WIN32
        mode = "rb";
#else
        mode = "r";
#endif

	if(strcmp(fname, _file_name)){
		fpn = fopen(fname, mode);
		if (fpn) 
			strcpy(_file_name, fname);
		else
			_file_name[0] = '\0';
	} else {
		return 0;
	}

	if (fpn == NULL){
		exifdie((const char *)strerror(errno));
		return 2;
	}

	while (jpegscan(fpn, &mark, &len, !(first++))) {

                if (mark != JPEG_M_APP1) {
                        if (fseek(fpn, len, SEEK_CUR)){
                                exifdie((const char *)strerror(errno));
				free (exifbuf);
				fclose (fpn);
				return 2;
			}
                        continue;
                }
		
                exifbuf = (unsigned char *)malloc(len);
                if (!exifbuf){
                        exifdie((const char *)strerror(errno));
			free (exifbuf);
			fclose (fpn);
			return 2;
		}

                rlen = fread(exifbuf, 1, len, fpn);
                if (rlen != len) {
                        exifwarn("error reading JPEG (length mismatch)");
                        free(exifbuf);
			fclose (fpn);
                        return (1);
                }

                et = exifparse(exifbuf, len);

		if (et && et->props){
			break;
		} else {
	                exifwarn("couldn't find Exif data");
			free (exifbuf);
			fclose (fpn);
        	        return (1);
		}
	}

	free (exifbuf);
	fclose (fpn);
        return (0);
}

static long
get_props(char *field, char *value)
{
	int pas = TRUE;

	if (ep && dumplvl) {

		/* Take care of point-and-shoot values. */

		if (ep->lvl == ED_PAS)
			ep->lvl = pas ? ED_CAM : ED_IMG;

		/* For now, just treat overridden & bad values as verbose. */

		if (ep->lvl == ED_OVR || ep->lvl == ED_BAD)
			ep->lvl = ED_VRB;

		if (ep->lvl == dumplvl) {
			strcpy(field, ep->descr ? ep->descr : ep->name);
			if (!ep->str)
				sprintf(value, "%d", ep->value);
			else
				strcpy(value, ep->str);
		}

		ep = ep->next;
	}

	return (long)ep;
}

static int
close_application()
{
	if (et)
	{
		exiffree(et);
		et = NULL;
	}
}

static int
not_here(char *s)
{
    croak("%s not implemented on this architecture", s);
    return -1;
}

static double
constant(char *name, int len, int arg)
{
    errno = EINVAL;
    return 0;
}

MODULE = Image::EXIF		PACKAGE = Image::EXIF		


double
constant(sv,arg)
    PREINIT:
	STRLEN		len;
    INPUT:
	SV *		sv
	char *		s = SvPV(sv, len);
	int		arg
    CODE:
	RETVAL = constant(s,len,arg);
    OUTPUT:
	RETVAL

int
c_read_file(fname)
	char *fname
CODE:
	error[0] = '\0';
	RETVAL = 0;

	RETVAL = read_data(fname);
OUTPUT:
	RETVAL

int
c_get_camera_info()
CODE:
	dumplvl = ED_CAM;
	if (et)
		ep = et->props;

int
c_get_image_info()
CODE:
	dumplvl = ED_IMG;
	if (et)
		ep = et->props;

int
c_get_other_info()
CODE:
	dumplvl = ED_VRB;
	if (et)
		ep = et->props;

int
c_get_unknown_info()
CODE:
	dumplvl = ED_UNK;
	if (et)
		ep = et->props;

int
c_fetch()
PPCODE:
	char field[256] = "";
	char value[256] = "";

	int rc = get_props(field, value);
	if (rc){
		EXTEND(sp, 2);
		PUSHs(sv_2mortal(newSVpv((char*)field, 0)));
		PUSHs(sv_2mortal(newSVpv((char*)value, 0)));
	}

int
c_errstr()
PPCODE:
	if (strlen(error)){
		EXTEND(sp, 1);
		PUSHs(sv_2mortal(newSVpv((char*)error, 0)));
	}

int
c_close_all()
PPCODE:
	close_application();