content: /*
   * Copyright 2002-2015 Drew Noakes
   *
   * More information about this project is available at:
   *
   *    https://drewnoakes.com/code/exif/
   *    https://github.com/drewnoakes/metadata-extractor
   */
  package tim.prune.jpeg.drew;
  
  import tim.prune.jpeg.JpegData;
  
  /**
   * Implementation of TiffHandler used for handling TIFF tags according to the Exif standard.
   *
   * @author Drew Noakes https://drewnoakes.com
   */
  public class ExifTiffHandler
  {
  	private JpegData _jpegData = null;
  	private long _thumbnailOffset = -1L, _thumbnailLength = -1L;
  
  	/** This tag is a pointer to the Exif SubIFD. */
  	private static final int DIR_EXIF_SUB_IFD_OFFSET = 0x8769;
  	/** This tag is a pointer to the Exif GPS IFD. */
  	private static final int DIR_GPS_INFO_OFFSET = 0x8825;
  
  	private static final int TAG_GPS_LATITUDE_REF  = 0x0001;
  	private static final int TAG_GPS_LATITUDE      = 0x0002;
  	private static final int TAG_GPS_LONGITUDE_REF = 0x0003;
  	private static final int TAG_GPS_LONGITUDE     = 0x0004;
  	private static final int TAG_GPS_ALTITUDE      = 0x0006;
  	private static final int TAG_GPS_BEARING       = 0x0011;
  
  	private static final int TAG_ORIENTATION       = 0x0112;
  	private static final int TAG_THUMBNAIL_OFFSET  = 0x0201;
  	private static final int TAG_THUMBNAIL_LENGTH  = 0x0202;
  
  	private static final int TAG_SUB_ORITIME       = 0x9003;
  	private static final int TAG_SUB_DIGITIME      = 0x9004;
  
  
  	/**
  	 * Constructor
  	 * @param jpegData data object to populate with received results
  	 */
  	public ExifTiffHandler(JpegData jpegData)
  	{
  		_jpegData = jpegData;
  		_thumbnailOffset = _thumbnailLength = -1L;
  	}
  
  	public boolean isTagIfdPointer(int tagType)
  	{
  		if (tagType == DIR_EXIF_SUB_IFD_OFFSET) {
  			return true;
  		} else if (tagType == DIR_GPS_INFO_OFFSET) {
  			return true;
  		}
  
  		return false;
  	}
  
  	public void completed(final ByteArrayReader reader, final int tiffHeaderOffset)
  	{
  		// after the extraction process, if we have the correct tags, we may be able to store thumbnail information
  		if (_thumbnailOffset >= 0L && _thumbnailLength > 0L)
  		{
  			try {
  				byte[] thumbData = reader.getBytes(tiffHeaderOffset + (int) _thumbnailOffset, (int) _thumbnailLength);
  				if (thumbData != null)
  				{
  					byte[] thumbCopy = new byte[thumbData.length];
  					System.arraycopy(thumbData, 0, thumbCopy, 0, thumbData.length);
  					_jpegData.setThumbnailImage(thumbCopy);
  				}
  			} catch (ExifException ex) {}
  		}
  	}
  
  	public void setRationalArray(int tagId, Rational[] array)
  	{
  		switch (tagId)
  		{
  			case TAG_GPS_LATITUDE:
  				_jpegData.setLatitude(new double[] {array[0].doubleValue(), array[1].doubleValue(),
  					array[2].convertToPositiveValue()});
  				break;
  			case TAG_GPS_LONGITUDE:
  				_jpegData.setLongitude(new double[] {array[0].doubleValue(), array[1].doubleValue(),
  					array[2].convertToPositiveValue()});
  				break;
  		}
  	}
  
  	public void setRational(int tagId, Rational rational)
  	{
  		switch (tagId)
  		{
  			case TAG_GPS_ALTITUDE:
  				_jpegData.setAltitude(rational.intValue());
  				return;
  			case TAG_GPS_BEARING:
  				_jpegData.setBearing(rational.doubleValue());
  				return;
  		}
  		// maybe it was an integer passed as a rational?
  		if (rational.getDenominator() == 1L) {
  			setIntegerValue(tagId, rational.intValue());
  		}
  	}
  
  	public void setString(int tagId, String string)
  	{
  		switch (tagId)
  		{
  			case TAG_SUB_ORITIME:
  				_jpegData.setOriginalTimestamp(string);
  				break;
  			case TAG_SUB_DIGITIME:
  				_jpegData.setDigitizedTimestamp(string);
  				break;
  			case TAG_GPS_LATITUDE_REF:
  				_jpegData.setLatitudeRef(string);
  				break;
  			case TAG_GPS_LONGITUDE_REF:
  				_jpegData.setLongitudeRef(string);
  				break;
  		}
  	}
  
  	public void setIntegerValue(int tagId, int intVal)
  	{
  		switch (tagId)
  		{
  			case TAG_ORIENTATION:
  				_jpegData.setOrientationCode(intVal);
  				break;
  			case TAG_THUMBNAIL_OFFSET:
  				_thumbnailOffset = intVal;
  				break;
  			case TAG_THUMBNAIL_LENGTH:
  				_thumbnailLength = intVal;
  				break;
  			case TAG_GPS_BEARING:
  				_jpegData.setBearing(intVal);
  				break;
  		}
  	}
  
  
  	/**
  	 * Decide, based on the directory id and the tag id, if we want to parse and process it
  	 * @param inDirectoryId
  	 * @param childTagId
  	 * @return true if the tag should be parsed
  	 */
  	public boolean isInterestingTag(int inDirectoryId, int childTagId)
  	{
  		switch (inDirectoryId)
  		{
  			case DIR_GPS_INFO_OFFSET:
  				return true;
  			case DIR_EXIF_SUB_IFD_OFFSET:
  				return childTagId == TAG_SUB_ORITIME
  					|| childTagId == TAG_SUB_DIGITIME;
  			case 0:
  				return childTagId == TAG_THUMBNAIL_OFFSET
  					|| childTagId == TAG_THUMBNAIL_LENGTH
  					|| childTagId == TAG_ORIENTATION;
  		}
  		return false;
  	}
  }
  