// 
// This code snippet is made available under LGPL 
// Author: Joakim Larsson 2007-02-25
//

const char leth[]  = {0x49, 0x49, 0x2a, 0x00}; // Little endian TIFF header
const char beth[]  = {0x4d, 0x4d, 0x00, 0x2a}; // Big endian TIFF header
const char types[] = {0x00, 0x01, 0x01, 0x02, 0x04, 0x08, 0x00, 0x08, 0x00, 0x04, 0x08}; // size in bytes for EXIF types

#define DE_ENDIAN16(val) endian == G_BIG_ENDIAN ? GUINT16_FROM_BE(val) : GUINT16_FROM_LE(val)
#define DE_ENDIAN32(val) endian == G_BIG_ENDIAN ? GUINT32_FROM_BE(val) : GUINT32_FROM_LE(val)

#define ENDIAN32_IT(val) endian == G_BIG_ENDIAN ? GUINT32_TO_BE(val) : GUINT32_TO_LE(val)

unsigned int
patch_exif_tag( const char    *filename,
	        ExifTag        etag,
		unsigned char *data,
		int            size)
{
	FILE *jf;
	char buf[1024 * 1];
	int  i, ifd, offset, tags, rest, type, count, tiff;
        int endian = 0;

	printf("patch_exif_tag(%s, %04x, %s, %d)\n", filename, etag, data, size);

        // Open file
	if ((filename = get_file_path_from_uri (filename)) == NULL )
		return 0;

	if ((jf = fopen(filename, "r+")) == NULL)
		return 0;

        // Fill buffer
        fread(buf, 1, sizeof(buf), jf);


        // Check for TIFF header and catch endianess
	i = 0;
	while (i < sizeof(buf)){
		if (bcmp(&buf[i], leth, 4) == 0){ // Little endian TIFF header
		   tiff   = i;
		   endian = G_LITTLE_ENDIAN;
		   ifd    = GINT16_TO_LE(etag);
                   offset = GINT32_FROM_LE(*((unsigned long*)(&buf[i] + 4)));
		   i      = i + offset;
		   tags   = GINT16_FROM_LE(*((unsigned short*)(&buf[i])));
		   i      = i + 2;
		   break;
                }
		else if (bcmp(&buf[i], beth, 4) == 0){ // Big endian TIFF header
		   tiff   = i;
                   endian = G_BIG_ENDIAN;
		   ifd    = GINT16_TO_BE(etag);
                   offset = GINT32_FROM_BE(*((unsigned long*)(&buf[i] + 4)));
		   i      = i + offset;
		   tags   = GINT16_FROM_BE(*((unsigned short*)(&buf[i])));
		   i      = i + 2;
		   break;
                }
                i++;
        }

	// Check the tags for our tag
	while (tags--){
		// Our tag?
		if (bcmp(&buf[i], &ifd, 2) == 0){ 
		   type   = DE_ENDIAN16(*((unsigned short*)(&buf[i + 2])));
		   count  = DE_ENDIAN32(*((unsigned long*)(&buf[i + 4])));
  	           offset = DE_ENDIAN32(*((unsigned long*)(&buf[i + 8])));

		   // Direct value that can be patched directly in TAG table 
		   if ((types[type] * count) <= 4){
		       printf("In value tag\n");
		       *((unsigned long*)(&buf[i + 8])) = ENDIAN32_IT(*data);
                   }
		   // Offseted value of same or larger size that we can patch
		   else if (types[type] * count >= size){
		       memcpy((char *)(buf + tiff + offset), data, size);
		   }
		   // Otherwise we are not able to patch the new value, at least not here
		   else {
		       printf("New TAG value does not fit, no patch applied\n");
		       continue;
                   }
                }
                i += 12;
        }

        // Save changes
	rewind(jf);
	fwrite(buf, 1, sizeof(buf), jf);
	fclose(jf);
}
