/************************************************************
**
** Application: TranJPEG
**
** Title:       c.exif
**
*************************************************************/

/*
*
* Copyright (c) 2015, Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */
/* from standard clib */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* headers from tasklib */
#include "TaskLib:h.os"
#include "TaskLib:h.wimp"
#include "TaskLib:h.werr"
#include "TaskLib:h.wos"
#include "TaskLib:h.err"
#include "TaskLib:h.poll"
#include "TaskLib:h.etc"
#include "TaskLib:h.flex"
#include "TaskLib:h.fsx"
#include "TaskLib:h.pane"
#include "TaskLib:h.temp"
#include "TaskLib:h.constants"
#include "TaskLib:h.conf"
#include "TaskLib:h.swis"

/* headers from app */
#include "h.reslink"
#include "h.main"
#include "h.exif"



#define INTEL 0
#define MOTOROLA 1

#define UNSIGNED_BYTE 1
#define ASCII_STRING 2
#define UNSIGNED_SHORT 3
#define UNSIGNED_LONG 4
#define UNSIGNED_RATIONAL 5
#define SIGNED_BYTE 6
#define UNDEFINED 7
#define SIGNED_SHORT 8
#define SIGNED_LONG 9
#define SIGNED_RATIONAL 10
#define SINGLE_FLOAT 11
#define DOUBLE_FLOAT 12



/* global variables */
static char * exif_data_buffer;

static   char *tiff_header_start;
static   int byte_order;
static   int offset_to_exif_subIFD;
static   int offset_to_IFD0_data;
static   int offset_to_IFD1_data;

static  int jfif_offset;

static int calculate_four_byte_value ( char *p, int byte_order )

{
  int value;


  if ( byte_order == INTEL )
  {
    value = (int)p[0] + ((int)p[1]<<8) + ((int)p[2]<<16) + ((int)p[3]<<24);
  }
  else
  {
    value = (int)p[3] + ((int)p[2]<<8) + ((int)p[1]<<16) + ((int)p[0]<<24);
  }
  return value;

}





static int calculate_two_byte_value ( char *p, int byte_order )

{
  int value;


  if ( byte_order == INTEL )
  {
    value = (int)p[0] + ((int)p[1]<<8);
  }
  else
  {
    value = (int)p[1] + ((int)p[0]<<8);
  }
  return value;

}




void write_back_normal_orientation (int offset, char *filename)
{
  FILE *handle ;

  /* write back the normal value */
  handle = fopen ( filename , "rb+" ) ;
  fseek ( handle, (long)(offset + jfif_offset), SEEK_SET);
  if ( byte_order == INTEL )
  {
    fputc (0x01, handle);
    fputc (0x00, handle);
  }
  else
  {
    fputc (0x00, handle);
    fputc (0x01, handle);
  }

  fclose ( handle ) ;
  return;
}



static int check_file_for_jpeg_and_exif ( char *filename )
/* return size of data if jpeg with exif info (must be >> 2)
 * return 2 if jpeg without exif info
 * return 1 if not jpeg
 * return 0 if error
 */

{
  int return_value = 0;
  FILE *handle ;
  int success ;
  char buffer[16];
  size_t number_read ;
  int jfif_length;

  handle = fopen ( filename , "rb" ) ;
  if ( handle == NULL )
  {
    /* error xxx */
    return 0;
  }
  /* last two bytes are 0xFFD9 for jpeg file */
  /* check the last two bytes */
  /* ok so far */
  /* load the start of the file */
  success = fseek ( handle, 0 , SEEK_SET ) ;
  if ( success == 0 )
  {
    number_read = fread ( (void *) buffer , 1, 12, handle ) ;
    /* first 2 bytes should be 0xFFD8 */
    if (( buffer[0] == 0xff ) && ( buffer[1] == 0xd8 ))
    {
      /* passed tests for jpeg file */
      /* now test for jfif header */
      if ( ( buffer[2] == 0xff ) && ( buffer[3] == 0xe0 ))
      {
        jfif_length = ((int)buffer[4]<<8) + (int)buffer[5];
        success = fseek ( handle, jfif_length + 2, SEEK_SET ) ;
        number_read = fread ( (void *) buffer , 1, 12, handle ) ;
        jfif_offset = jfif_length + 2;
      }
      else
      {
        jfif_offset = 0;
      }

      /* now test for presence of Exif data */
      if ( ( buffer[2] == 0xff )
            && ( buffer[3] == 0xe1 )
            && ( buffer[6] == 0x45 )
            && ( buffer[7] == 0x78 )
            && ( buffer[8] == 0x69 )
            && ( buffer[9] == 0x66 ) )
      {
        /* passed test for Exif data */
        /* calculate size of Exif data and return it */
        /* this data is always MOTOROLA big endian */
        return_value = ((int)buffer[4]<<8) + (int)buffer[5];
      }
      else
      {
        /* just a jpeg file */
        return_value = 2;
      }
    }
    else
    {
      /* not a jpeg */
      return_value = 1;
    }
  }

  /* close the file */
  fclose ( handle ) ;

  return (return_value);
}



/* look for the orientation tag */

static int report_IFD0_tag_data ( int tag,
                                  int data_value,
                                  char *p1,
                                  int components,
                                  int format )

{


  if ( tag == 0x112 )
  {
    /* orientation */
    return (calculate_two_byte_value ( p1, byte_order ));
  }
  else
  {
    return (0);
  }
}




int exif_get_orientation (char *filename, int * orientation, int *or_offset)
{
  os_error   *err = NULL;

  int exif_size;
  FILE *handle ;
  int success ;
  size_t number_read ;
  int or = 0;
  char *p, *p1;
  int entries;
  int i;
  int tag;
  int format;
  int components;
  int data_value;

  if (or_offset) *or_offset = 0;

  exif_size = check_file_for_jpeg_and_exif ( filename );
  if ( exif_size <= 2 )
  {
    /* report err */
    return 0;
  }

  /* Reserve some memory for the buffer for exif data */
  err = flex_alloc ((flex_ptr) &exif_data_buffer, exif_size + 10);
  if (err)
  {
    main_issuewarning (WFLEXALFAIL);
    return (0);
  }

  handle = fopen ( filename , "rb" ) ;
  if ( handle == NULL )
  {
    /* error xxx */
    main_issuewarning (XIFNOOPEN);
    return 0;
  }

  /* move to start of file and load data */
  success = fseek ( handle, jfif_offset , SEEK_SET ) ;
  if ( success == 0 )
  {
    number_read = fread ( (void *) exif_data_buffer , 1, exif_size+2, handle ) ;
    /* close the file */
    success = fclose ( handle ) ;
  }
  else
  {
    main_issuewarning (XIFNOSEEK);
    /* close the file */
    success = fclose ( handle ) ;
    return 0;
  }

  /* Get the byte order */
  tiff_header_start = exif_data_buffer + 12;
  if ( (tiff_header_start[0] == 0x49) && (tiff_header_start[1] == 0x49))
  {
    byte_order = INTEL;
  }
  else
  {
    if ( (tiff_header_start[0] == 0x4D) && (tiff_header_start[1] == 0x4D))
    {
      byte_order = MOTOROLA;
    }
    else
    {
      main_issuewarning (XIFBYTEORDER);
      return 0;
    }
  }

  /* check for further TIFF marker bytes */
  if ( calculate_two_byte_value ( tiff_header_start+2, byte_order ) != 0x2a )
  {
    main_issuewarning (XIFTIFMARK);
    return (0);
  }

  /* now we are in business */
  /* next four bytes are offset to IFD0 (Main image data) */
  p = tiff_header_start + 4;
  offset_to_IFD0_data = calculate_four_byte_value ( p, byte_order );

    p = tiff_header_start + offset_to_IFD0_data;
    entries = calculate_two_byte_value ( p, byte_order );

    /* now parse each of the entries */
    p+=2;
    for ( i=0; i<entries; i++ )
    {
      tag = calculate_two_byte_value ( p, byte_order );
      p1 = p + 2;
      format = calculate_two_byte_value ( p1, byte_order );
      p1 = p + 4;
      components = calculate_four_byte_value ( p1, byte_order );
      p1 += 4;
      data_value = calculate_four_byte_value ( p1, byte_order );

      /* now decode each tag */

      or = report_IFD0_tag_data ( tag, data_value,  p1, components, format );
      if (or > 0)
      {
        if (or_offset) *or_offset = (int)(p1 - exif_data_buffer);
        break;
      }
      p += 12;
    }

  *orientation = or;

  err = flex_free ((flex_ptr) &exif_data_buffer);

  return (1);
}
