#ifndef __TIFF_Support_hpp__
#define __TIFF_Support_hpp__	1

// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================

#include "public/include/XMP_Environment.h"	// ! This must be the first include.

#include <map>
#include <stdlib.h>
#include <string.h>

#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"

#include "XMPFiles/source/XMPFiles_Impl.hpp"

#include "source/EndianUtils.hpp"

#include "source/Endian.h"

#if SUNOS_SPARC || XMP_IOS_ARM
        static const IEndian &IE = BigEndian::getInstance();
#else
        static const IEndian &IE = LittleEndian::getInstance();
#endif //#if SUNOS_SPARC || XMP_IOS_ARM

// =================================================================================================
/// \file TIFF_Support.hpp
/// \brief XMPFiles support for TIFF streams.
///
/// This header provides TIFF stream support specific to the needs of XMPFiles. This is not intended
/// for general purpose TIFF processing. TIFF_Manager is an abstract base class with 2 concrete
/// derived classes, TIFF_MemoryReader and TIFF_FileWriter.
///
/// TIFF_MemoryReader provides read-only support for TIFF streams that are small enough to be kept
/// entirely in memory. This allows optimizations to reduce heap usage and processing code. It is
/// sufficient for browsing access to the Exif metadata in JPEG and Photoshop files. Think of
/// TIFF_MemoryReader as "memory-based AND read-only". Since the entire TIFF stream is available,
/// GetTag will return information about any tag in the stream.
///
/// TIFF_FileWriter is for cases where updates are needed or the TIFF stream is too large to be kept
/// entirely in memory. Think of TIFF_FileWriter as "file-based OR read-write". TIFF_FileWriter only
/// maintains information for tags of interest as metadata.
///
/// The needs of XMPFiles are well defined metadata access. Only 4 IFDs are processed:
/// \li The 0th IFD, for the primary image, the first one in the outer list of IFDs.
/// \li The Exif general metadata IFD, from tag 34665 in the primary image IFD.
/// \li The Exif GPS Info metadata IFD, from tag 34853 in the primary image IFD.
/// \li The Exif Interoperability IFD, from tag 40965 in the Exif general metadata IFD.
///
/// \note These classes are for use only when directly compiled and linked. They should not be
/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection.
// =================================================================================================


// =================================================================================================
// TIFF IFD and type constants
// ===========================
//
// These aren't inside TIFF_Manager because static data members can't be initialized there.

enum {	// Constants for the recognized IFDs.
	kTIFF_PrimaryIFD    = 0,	// The primary image IFD, also called the 0th IFD.
	kTIFF_TNailIFD      = 1,	// The thumbnail image IFD also called the 1st IFD. (not used)
	kTIFF_ExifIFD       = 2,	// The Exif general metadata IFD.
	kTIFF_GPSInfoIFD    = 3,	// The Exif GPS Info IFD.
	kTIFF_InteropIFD    = 4,	// The Exif Interoperability IFD.
	kTIFF_LastRealIFD   = 4,
	kTIFF_KnownIFDCount = 5,
	kTIFF_KnownIFD      = 9	// The IFD that a tag is known to belong in.
};

enum {	// Constants for the type field of a tag, as defined by TIFF.
	kTIFF_ShortOrLongType =  0,	// ! Not part of the TIFF spec, never in a tag!
	kTIFF_ByteType        =  1,
	kTIFF_ASCIIType       =  2,
	kTIFF_ShortType       =  3,
	kTIFF_LongType        =  4,
	kTIFF_RationalType    =  5,
	kTIFF_SByteType       =  6,
	kTIFF_UndefinedType   =  7,
	kTIFF_SShortType      =  8,
	kTIFF_SLongType       =  9,
	kTIFF_SRationalType   = 10,
	kTIFF_FloatType       = 11,
	kTIFF_DoubleType      = 12,
	kTIFF_LastType        = 12
};

static const size_t kTIFF_TypeSizes[]    = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 };

static const bool kTIFF_IsIntegerType[]  = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 };
static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 };
static const bool kTIFF_IsFloatType[]    = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };

enum {	// Encodings for SetTag_EncodedString.
	kTIFF_EncodeUndefined = 0,
	kTIFF_EncodeASCII     = 1,
	kTIFF_EncodeUnicode   = 2,	// UTF-16 in the endianness of the TIFF stream.
	kTIFF_EncodeJIS       = 3,	// Exif 2.2 uses JIS X 208-1990.
	kTIFF_EncodeUnknown   = 9
};

// =================================================================================================
// Recognized TIFF tags
// ====================

// -----------------------------------------------------------------------------------------------
// An enum of IDs for all of the tags as potential interest as metadata. The numberical order does
// not matter. These are mostly listed in the order of the Exif specification tables for convenience
// of checking correspondence.

enum {

	// General 0th IFD tags. Some of these can also be in the thumbnail IFD.

	// General tags from Exif 2.3 table 4:
	kTIFF_ImageWidth = 256,
	kTIFF_ImageLength = 257,
	kTIFF_BitsPerSample = 258,
	kTIFF_Compression = 259,
	kTIFF_PhotometricInterpretation = 262,
	kTIFF_Orientation = 274,
	kTIFF_SamplesPerPixel = 277,
	kTIFF_PlanarConfiguration = 284,
	kTIFF_YCbCrCoefficients = 529,
	kTIFF_YCbCrSubSampling = 530,
	kTIFF_XResolution = 282,
	kTIFF_YResolution = 283,
	kTIFF_ResolutionUnit = 296,
	kTIFF_TransferFunction = 301,
	kTIFF_WhitePoint = 318,
	kTIFF_PrimaryChromaticities = 319,
	kTIFF_YCbCrPositioning = 531,
	kTIFF_ReferenceBlackWhite = 532,
	kTIFF_DateTime = 306,
	kTIFF_ImageDescription = 270,
	kTIFF_Make = 271,
	kTIFF_Model = 272,
	kTIFF_Software = 305,
	kTIFF_Artist = 315,
	kTIFF_Copyright = 33432,
	
	// Tags defined by Adobe:
	kTIFF_XMP = 700,
	kTIFF_IPTC = 33723,
	kTIFF_PSIR = 34377,
	kTIFF_DNGVersion = 50706,
	kTIFF_DNGBackwardVersion = 50707,

	// Additional thumbnail IFD tags. We also care about 256, 257, and 259 in thumbnails.
	kTIFF_JPEGInterchangeFormat = 513,
	kTIFF_JPEGInterchangeFormatLength = 514,

	// Tags that need special handling when rewriting memory-based TIFF.
	kTIFF_StripOffsets = 273,
	kTIFF_StripByteCounts = 279,
	kTIFF_FreeOffsets = 288,
	kTIFF_FreeByteCounts = 289,
	kTIFF_TileOffsets = 324,
	kTIFF_TileByteCounts = 325,
	kTIFF_SubIFDs = 330,
	kTIFF_JPEGQTables = 519,
	kTIFF_JPEGDCTables = 520,
	kTIFF_JPEGACTables = 521,

	// Exif IFD tags defined in Exif 2.3 table 7.

	kTIFF_ExifVersion = 36864,
	kTIFF_FlashpixVersion = 40960,
	kTIFF_ColorSpace = 40961,
	kTIFF_Gamma = 42240,
	kTIFF_ComponentsConfiguration = 37121,
	kTIFF_CompressedBitsPerPixel = 37122,
	kTIFF_PixelXDimension = 40962,
	kTIFF_PixelYDimension = 40963,
	kTIFF_MakerNote = 37500, // Gets deleted when rewriting memory-based TIFF.
	kTIFF_UserComment = 37510,
	kTIFF_RelatedSoundFile = 40964,
	kTIFF_DateTimeOriginal = 36867,
	kTIFF_DateTimeDigitized = 36868,
	kTIFF_SubSecTime = 37520,
	kTIFF_SubSecTimeOriginal = 37521,
	kTIFF_SubSecTimeDigitized = 37522,
	kTIFF_ImageUniqueID = 42016,
	kTIFF_CameraOwnerName = 42032,
	kTIFF_BodySerialNumber = 42033,
	kTIFF_LensSpecification = 42034,
	kTIFF_LensMake = 42035,
	kTIFF_LensModel = 42036,
	kTIFF_LensSerialNumber = 42037,

	// Exif IFD tags defined in Exif 2.3 table 8.

	kTIFF_ExposureTime = 33434,
	kTIFF_FNumber = 33437,
	kTIFF_ExposureProgram = 34850,
	kTIFF_SpectralSensitivity = 34852,
	kTIFF_PhotographicSensitivity = 34855,	// ! Called kTIFF_ISOSpeedRatings before Exif 2.3.
	kTIFF_OECF = 34856,
	kTIFF_SensitivityType = 34864,
	kTIFF_StandardOutputSensitivity = 34865,
	kTIFF_RecommendedExposureIndex = 34866,
	kTIFF_ISOSpeed = 34867,
	kTIFF_ISOSpeedLatitudeyyy = 34868,
	kTIFF_ISOSpeedLatitudezzz = 34869,
	kTIFF_ShutterSpeedValue = 37377,
	kTIFF_ApertureValue = 37378,
	kTIFF_BrightnessValue = 37379,
	kTIFF_ExposureBiasValue = 37380,
	kTIFF_MaxApertureValue = 37381,
	kTIFF_SubjectDistance = 37382,
	kTIFF_MeteringMode = 37383,
	kTIFF_LightSource = 37384,
	kTIFF_Flash = 37385,
	kTIFF_FocalLength = 37386,
	kTIFF_SubjectArea = 37396,
	kTIFF_FlashEnergy = 41483,
	kTIFF_SpatialFrequencyResponse = 41484,
	kTIFF_FocalPlaneXResolution = 41486,
	kTIFF_FocalPlaneYResolution = 41487,
	kTIFF_FocalPlaneResolutionUnit = 41488,
	kTIFF_SubjectLocation = 41492,
	kTIFF_ExposureIndex = 41493,
	kTIFF_SensingMethod = 41495,
	kTIFF_FileSource = 41728,
	kTIFF_SceneType = 41729,
	kTIFF_CFAPattern = 41730,
	kTIFF_CustomRendered = 41985,
	kTIFF_ExposureMode = 41986,
	kTIFF_WhiteBalance = 41987,
	kTIFF_DigitalZoomRatio = 41988,
	kTIFF_FocalLengthIn35mmFilm = 41989,
	kTIFF_SceneCaptureType = 41990,
	kTIFF_GainControl = 41991,
	kTIFF_Contrast = 41992,
	kTIFF_Saturation = 41993,
	kTIFF_Sharpness = 41994,
	kTIFF_DeviceSettingDescription = 41995,
	kTIFF_SubjectDistanceRange = 41996,

	// GPS IFD tags.

	kTIFF_GPSVersionID = 0,
	kTIFF_GPSLatitudeRef = 1,
	kTIFF_GPSLatitude = 2,
	kTIFF_GPSLongitudeRef = 3,
	kTIFF_GPSLongitude = 4,
	kTIFF_GPSAltitudeRef = 5,
	kTIFF_GPSAltitude = 6,
	kTIFF_GPSTimeStamp = 7,
	kTIFF_GPSSatellites = 8,
	kTIFF_GPSStatus = 9,
	kTIFF_GPSMeasureMode = 10,
	kTIFF_GPSDOP = 11,
	kTIFF_GPSSpeedRef = 12,
	kTIFF_GPSSpeed = 13,
	kTIFF_GPSTrackRef = 14,
	kTIFF_GPSTrack = 15,
	kTIFF_GPSImgDirectionRef = 16,
	kTIFF_GPSImgDirection = 17,
	kTIFF_GPSMapDatum = 18,
	kTIFF_GPSDestLatitudeRef = 19,
	kTIFF_GPSDestLatitude = 20,
	kTIFF_GPSDestLongitudeRef = 21,
	kTIFF_GPSDestLongitude = 22,
	kTIFF_GPSDestBearingRef = 23,
	kTIFF_GPSDestBearing = 24,
	kTIFF_GPSDestDistanceRef = 25,
	kTIFF_GPSDestDistance = 26,
	kTIFF_GPSProcessingMethod = 27,
	kTIFF_GPSAreaInformation = 28,
	kTIFF_GPSDateStamp = 29,
	kTIFF_GPSDifferential = 30,
	kTIFF_GPSHPositioningError = 31,

	// Special tags that are links to other IFDs.
	
	kTIFF_ExifIFDPointer = 34665,				// Found in 0th IFD
	kTIFF_GPSInfoIFDPointer = 34853,			// Found in 0th IFD
	kTIFF_InteroperabilityIFDPointer = 40965	// Found in Exif IFD

};

// *** Temporary hack:
#define kTIFF_ISOSpeedRatings	kTIFF_PhotographicSensitivity

// ------------------------------------------------------------------
// Sorted arrays of the tags that are recognized in the various IFDs.

static const XMP_Uns16 sKnownPrimaryIFDTags[] =
{
	kTIFF_ImageWidth,					//   256
	kTIFF_ImageLength,					//   257
	kTIFF_BitsPerSample,				//   258
	kTIFF_Compression,					//   259
	kTIFF_PhotometricInterpretation,	//   262
	kTIFF_ImageDescription,				//   270
	kTIFF_Make,							//   271
	kTIFF_Model,						//   272
	kTIFF_Orientation,					//   274
	kTIFF_SamplesPerPixel,				//   277
	kTIFF_XResolution,					//   282
	kTIFF_YResolution,					//   283
	kTIFF_PlanarConfiguration,			//   284
	kTIFF_ResolutionUnit,				//   296
	kTIFF_TransferFunction,				//   301
	kTIFF_Software,						//   305
	kTIFF_DateTime,						//   306
	kTIFF_Artist,						//   315
	kTIFF_WhitePoint,					//   318
	kTIFF_PrimaryChromaticities,		//   319
	kTIFF_YCbCrCoefficients,			//   529
	kTIFF_YCbCrSubSampling,				//   530
	kTIFF_YCbCrPositioning,				//   531
	kTIFF_ReferenceBlackWhite,			//   532
	kTIFF_XMP,							//   700
	kTIFF_Copyright,					// 33432
	kTIFF_IPTC,							// 33723
	kTIFF_PSIR,							// 34377
	kTIFF_ExifIFDPointer,				// 34665
	kTIFF_GPSInfoIFDPointer,			// 34853
	kTIFF_DNGVersion,					// 50706
	kTIFF_DNGBackwardVersion,			// 50707
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownThumbnailIFDTags[] =
{
	kTIFF_ImageWidth,					// 256
	kTIFF_ImageLength,					// 257
	kTIFF_Compression,					// 259
	kTIFF_JPEGInterchangeFormat,		// 513
	kTIFF_JPEGInterchangeFormatLength,	// 514
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownExifIFDTags[] =
{
	kTIFF_ExposureTime,					// 33434
	kTIFF_FNumber,						// 33437
	kTIFF_ExposureProgram,				// 34850
	kTIFF_SpectralSensitivity,			// 34852
	kTIFF_PhotographicSensitivity,		// 34855
	kTIFF_OECF,							// 34856
	kTIFF_SensitivityType,				// 34864
	kTIFF_StandardOutputSensitivity,	// 34865
	kTIFF_RecommendedExposureIndex,		// 34866
	kTIFF_ISOSpeed,						// 34867
	kTIFF_ISOSpeedLatitudeyyy,			// 34868
	kTIFF_ISOSpeedLatitudezzz,			// 34869
	kTIFF_ExifVersion,					// 36864
	kTIFF_DateTimeOriginal,				// 36867
	kTIFF_DateTimeDigitized,			// 36868
	kTIFF_ComponentsConfiguration,		// 37121
	kTIFF_CompressedBitsPerPixel,		// 37122
	kTIFF_ShutterSpeedValue,			// 37377
	kTIFF_ApertureValue,				// 37378
	kTIFF_BrightnessValue,				// 37379
	kTIFF_ExposureBiasValue,			// 37380
	kTIFF_MaxApertureValue,				// 37381
	kTIFF_SubjectDistance,				// 37382
	kTIFF_MeteringMode,					// 37383
	kTIFF_LightSource,					// 37384
	kTIFF_Flash,						// 37385
	kTIFF_FocalLength,					// 37386
	kTIFF_SubjectArea,					// 37396
	kTIFF_UserComment,					// 37510
	kTIFF_SubSecTime,					// 37520
	kTIFF_SubSecTimeOriginal,			// 37521
	kTIFF_SubSecTimeDigitized,			// 37522
	kTIFF_FlashpixVersion,				// 40960
	kTIFF_ColorSpace,					// 40961
	kTIFF_PixelXDimension,				// 40962
	kTIFF_PixelYDimension,				// 40963
	kTIFF_RelatedSoundFile,				// 40964
	kTIFF_FlashEnergy,					// 41483
	kTIFF_SpatialFrequencyResponse,		// 41484
	kTIFF_FocalPlaneXResolution,		// 41486
	kTIFF_FocalPlaneYResolution,		// 41487
	kTIFF_FocalPlaneResolutionUnit,		// 41488
	kTIFF_SubjectLocation,				// 41492
	kTIFF_ExposureIndex,				// 41493
	kTIFF_SensingMethod,				// 41495
	kTIFF_FileSource,					// 41728
	kTIFF_SceneType,					// 41729
	kTIFF_CFAPattern,					// 41730
	kTIFF_CustomRendered,				// 41985
	kTIFF_ExposureMode,					// 41986
	kTIFF_WhiteBalance,					// 41987
	kTIFF_DigitalZoomRatio,				// 41988
	kTIFF_FocalLengthIn35mmFilm,		// 41989
	kTIFF_SceneCaptureType,				// 41990
	kTIFF_GainControl,					// 41991
	kTIFF_Contrast,						// 41992
	kTIFF_Saturation,					// 41993
	kTIFF_Sharpness,					// 41994
	kTIFF_DeviceSettingDescription,		// 41995
	kTIFF_SubjectDistanceRange,			// 41996
	kTIFF_ImageUniqueID,				// 42016
	kTIFF_CameraOwnerName,				// 42032
	kTIFF_BodySerialNumber,				// 42033
	kTIFF_LensSpecification,			// 42034
	kTIFF_LensMake,						// 42035
	kTIFF_LensModel,					// 42036
	kTIFF_LensSerialNumber,				// 42037
	kTIFF_Gamma,						// 42240
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownGPSInfoIFDTags[] =
{
	kTIFF_GPSVersionID,			//  0
	kTIFF_GPSLatitudeRef,		//  1
	kTIFF_GPSLatitude,			//  2
	kTIFF_GPSLongitudeRef,		//  3
	kTIFF_GPSLongitude,			//  4
	kTIFF_GPSAltitudeRef,		//  5
	kTIFF_GPSAltitude,			//  6
	kTIFF_GPSTimeStamp,			//  7
	kTIFF_GPSSatellites,		//  8
	kTIFF_GPSStatus,			//  9
	kTIFF_GPSMeasureMode,		// 10
	kTIFF_GPSDOP,				// 11
	kTIFF_GPSSpeedRef,			// 12
	kTIFF_GPSSpeed,				// 13
	kTIFF_GPSTrackRef,			// 14
	kTIFF_GPSTrack,				// 15
	kTIFF_GPSImgDirectionRef,	// 16
	kTIFF_GPSImgDirection,		// 17
	kTIFF_GPSMapDatum,			// 18
	kTIFF_GPSDestLatitudeRef,	// 19
	kTIFF_GPSDestLatitude,		// 20
	kTIFF_GPSDestLongitudeRef,	// 21
	kTIFF_GPSDestLongitude,		// 22
	kTIFF_GPSDestBearingRef,	// 23
	kTIFF_GPSDestBearing,		// 24
	kTIFF_GPSDestDistanceRef,	// 25
	kTIFF_GPSDestDistance,		// 26
	kTIFF_GPSProcessingMethod,	// 27
	kTIFF_GPSAreaInformation,	// 28
	kTIFF_GPSDateStamp,			// 29
	kTIFF_GPSDifferential,		// 30
	kTIFF_GPSHPositioningError,	// 31
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownInteroperabilityIFDTags[] =
{
	// ! Yes, there are none at present.
	0xFFFF	// Must be last as a sentinel.
};

// ! Make sure these are in the same order as the IFD enum!
static const XMP_Uns16* sKnownTags[kTIFF_KnownIFDCount] = { sKnownPrimaryIFDTags,
															sKnownThumbnailIFDTags,
															sKnownExifIFDTags,
															sKnownGPSInfoIFDTags,
															sKnownInteroperabilityIFDTags };


// =================================================================================================
// =================================================================================================


// =================================================================================================
// TIFF_Manager
// ============

class TIFF_Manager {	// The abstract base class.
public:

	// ---------------------------------------------------------------------------------------------
	// Types and constants

	static const XMP_Uns32 kBigEndianPrefix    = 0x4D4D002AUL;
	static const XMP_Uns32 kLittleEndianPrefix = 0x49492A00UL;

	static const size_t kEmptyTIFFLength = 8;		// Just the header.
	static const size_t kEmptyIFDLength  = 2 + 4;	// Entry count and next-IFD offset.
	static const size_t kIFDEntryLength  = 12;

	struct TagInfo {
		XMP_Uns16   id;
		XMP_Uns16   type;
		XMP_Uns32   count;
		const void* dataPtr;	// ! The data must not be changed! Stream endian format!
		XMP_Uns32   dataLen;	// The length in bytes.
		TagInfo() : id(0), type(0), count(0), dataPtr(0), dataLen(0) {};
		TagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, const void* _dataPtr, XMP_Uns32 _dataLen )
			: id(_id), type(_type), count(_count), dataPtr(_dataPtr), dataLen(_dataLen) {};
	};

	typedef std::map<XMP_Uns16,TagInfo> TagInfoMap;

	struct Rational  { XMP_Uns32 num, denom; };
	struct SRational { XMP_Int32 num, denom; };

	// ---------------------------------------------------------------------------------------------
	// The IsXyzEndian methods return the external endianness of the original parsed TIFF stream.
	// The \c GetTag methods return native endian values, the \c SetTag methods take native values.
	// The original endianness is preserved in output.

	bool IsBigEndian() const { return this->bigEndian; };
	bool IsLittleEndian() const { return (! this->bigEndian); };
	bool IsNativeEndian() const { return this->nativeEndian; };

	// ---------------------------------------------------------------------------------------------
	// The TIFF_Manager only keeps explicit knowledge of up to 4 IFDs:
	// - The primary image IFD, also known as the 0th IFD. This must be present.
	// - A possible Exif general metadata IFD, found from tag 34665 in the primary image IFD.
	// - A possible Exif GPS metadata IFD, found from tag 34853 in the primary image IFD.
	// - A possible Exif Interoperability IFD, found from tag 40965 in the Exif general metadata IFD.
	//
	// Parsing will silently forget about certain aspects of ill-formed streams. If any tags are
	// repeated in an IFD, only the last is kept. Any known tags that are in the wrong IFD are
	// removed. Parsing will sort the tags into ascending order, AppendTIFF and ComposeTIFF will
	// preserve the sorted order. These fixes do not cause IsChanged to return true, that only
	// happens if the client makes explicit changes using SetTag or DeleteTag.

	virtual bool HasExifIFD() const = 0;
	virtual bool HasGPSInfoIFD() const = 0;

	// ---------------------------------------------------------------------------------------------
	// These are the basic methods to get a map of all of the tags in an IFD, to get or set a tag,
	// or to delete a tag. The dataPtr returned by \c GetTag is consided read-only, the client must
	// not change it. The ifd parameter to \c GetIFD must be for one of the recognized actual IFDs.
	// The ifd parameter for the GetTag or SetTag methods can be a specific IFD of kTIFF_KnownIFD.
	// Using the specific IFD will be slightly faster, saving a lookup in the known tag map. An
	// exception is thrown if kTIFF_KnownIFD is passed to GetTag or SetTag and the tag is not known.
	// \c SetTag replaces an existing tag regardless of type or count. \c DeleteTag deletes a tag,
	// it is a no-op if the tag does not exist. \c GetValueOffset returns the offset within the
	// parsed stream of the tag's value. It returns 0 if the tag was not in the parsed input.

	virtual bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const = 0;

	virtual bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const = 0;

	virtual void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) = 0;

	virtual void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) = 0;

	virtual XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const = 0;

	// ---------------------------------------------------------------------------------------------
	// These methods are for tags whose type can be short or long, depending on the actual value.
	// \c GetTag_Integer returns false if an existing tag's type is not short, or long, or if the
	// count is not 1. \c SetTag_Integer replaces an existing tag regardless of type or count, the
	// new tag will have type short or long.

	virtual bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0;

	void SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data );

	// ---------------------------------------------------------------------------------------------
	// These are customized forms of GetTag that verify the type and return a typed value. False is
	// returned if the type does not match or if the count is not 1.

	// *** Should also add support for ASCII multi-strings?

	virtual bool GetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const = 0;
	virtual bool GetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const = 0;
	virtual bool GetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const = 0;
	virtual bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const = 0;
	virtual bool GetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0;
	virtual bool GetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const = 0;

	virtual bool GetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const = 0;
	virtual bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const = 0;

	virtual bool GetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const = 0;
	virtual bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const = 0;

	virtual bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const = 0;

	// ---------------------------------------------------------------------------------------------

	void SetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8 data );
	void SetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8 data );
	void SetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 data );
	void SetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16 data );
	void SetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data );
	void SetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 data );

	void SetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 num, XMP_Uns32 denom );
	void SetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 num, XMP_Int32 denom );

	void SetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float data );
	void SetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double data );

	void SetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr dataPtr );

	// ---------------------------------------------------------------------------------------------

	virtual bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const = 0;
	virtual void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) = 0;

	bool DecodeString ( const void * encodedPtr, size_t encodedLen, std::string* utf8Str ) const;
	bool EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, std::string* encodedStr );

	// ---------------------------------------------------------------------------------------------
	// \c IsChanged returns true if a read-write stream has changes that need to be saved. This is
	// only the case when a \c SetTag method has been called. It is not true for changes related to
	// parsing normalization such as sorting of tags. \c IsChanged returns false for read-only streams.

	virtual bool IsChanged() = 0;

	// ---------------------------------------------------------------------------------------------
	// \c IsLegacyChanged returns true if a read-write stream has changes that need to be saved to
	// tags other than the XMP (tag 700). This only the case when a \c SetTag method has been
	// called. It is not true for changes related to parsing normalization such as sorting of tags.
	// \c IsLegacyChanged returns false for read-only streams.

	virtual bool IsLegacyChanged() = 0;

	// ---------------------------------------------------------------------------------------------
	// \c UpdateMemoryStream is mainly applicable to memory-based read-write streams. It recomposes
	// the memory stream to incorporate all changes. The new length and data pointer are returned.
	// \c UpdateMemoryStream can be used with a read-only memory stream to get the raw stream info.
	//
	// \c UpdateFileStream updates file-based TIFF. The client must guarantee that the TIFF portion
	// of the file matches that from the parse in the file-based constructor. Offsets saved from that
	// parse must still be valid. The open file reference need not be the same, e.g. the client can
	// be doing a crash-safe update into a temporary copy.
	//
	// Both \c UpdateMemoryStream and \c UpdateFileStream use an update-by-append model. Changes are
	// written in-place where they fit, anything requiring growth is appended to the end and the old
	// space is abandoned. The end for memory-based TIFF is the end of the data block, the end for
	// file-based TIFF is the end of the file. This update-by-append model has the advantage of not
	// perturbing any hidden offsets, a common feature of proprietary MakerNotes.
	//
	// The condenseStream parameter to UpdateMemoryStream can be used to rewrite the full stream
	// instead of appending. This will discard any MakerNote tags and risks breaking offsets that
	// are hidden. This can be necessary though to try to make the TIFF fit in a JPEG file.

	virtual void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ) = 0;
	virtual void ParseFileStream   ( XMP_IO* fileRef ) = 0;

	virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0;

	virtual XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) = 0;
	virtual void      UpdateFileStream   ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) = 0;

	// ---------------------------------------------------------------------------------------------

	GetUns16_Proc  GetUns16;	// Get values from the TIFF stream.
	GetUns32_Proc  GetUns32;	// Always native endian on the outside, stream endian in the stream.
	GetFloat_Proc  GetFloat;
	GetDouble_Proc GetDouble;

	PutUns16_Proc  PutUns16;	// Put values into the TIFF stream.
	PutUns32_Proc  PutUns32;	// Always native endian on the outside, stream endian in the stream.
	PutFloat_Proc  PutFloat;
	PutDouble_Proc PutDouble;

	virtual ~TIFF_Manager() {};

	virtual void SetErrorCallback ( GenericErrorCallback * ec ) { this->errorCallbackPtr = ec; };

	virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error );

protected:

	bool bigEndian, nativeEndian;

	XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length );
		// The pointer is to a buffer of the first 8 bytes. The length is the overall length, used
		// to check the primary IFD offset.

	TIFF_Manager();	// Force clients to use the reader or writer derived classes.

	struct RawIFDEntry {
		XMP_Uns16 id;
		XMP_Uns16 type;
		XMP_Uns32 count;
		XMP_Uns32 dataOrOffset;
	};

	GenericErrorCallback *errorCallbackPtr;

};	// TIFF_Manager


// =================================================================================================
// =================================================================================================


// =================================================================================================
// TIFF_MemoryReader
// =================

class TIFF_MemoryReader : public TIFF_Manager {	// The derived class for memory-based read-only access.
public:

	bool HasExifIFD() const { return (containedIFDs[kTIFF_ExifIFD].count != 0); };
	bool HasGPSInfoIFD() const { return (containedIFDs[kTIFF_GPSInfoIFD].count != 0); };

	bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const;

	bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const;

	void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) { NotAppropriate(); };

	void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) { NotAppropriate(); };

	XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;

	bool GetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const;
	bool GetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const;
	bool GetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const;
	bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const;
	bool GetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;
	bool GetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const;

	bool GetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const;
	bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const;

	bool GetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const;
	bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const;

	bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const;

	bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;

	void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) { NotAppropriate(); };

	bool IsChanged() { return false; };
	bool IsLegacyChanged() { return false; };

	void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true );
	void ParseFileStream   ( XMP_IO* fileRef ) { NotAppropriate(); };

	void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) { NotAppropriate(); };

	XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; };
	void      UpdateFileStream   ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) { NotAppropriate(); };

	TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {};

	virtual ~TIFF_MemoryReader() { if ( this->ownedStream ) free ( this->tiffStream ); };

private:

	bool ownedStream;

	XMP_Uns8* tiffStream;
	XMP_Uns32 tiffLength;

	// Memory usage notes: TIFF_MemoryReader is for memory-based read-only usage (both apply). There
	// is no need to ever allocate separate blocks of memory, everything is used directly from the
	// TIFF stream. Data pointers are computed on the fly, the offset field is 4 bytes and pointers
	// will be 8 bytes for 64-bit platforms.

	struct TweakedIFDEntry {	// ! Most fields are in native byte order, dataOrPos is for offsets only.
		XMP_Uns16 id;
		XMP_Uns16 type;
		XMP_Uns32 bytes;
		XMP_Uns32 dataOrPos;
		TweakedIFDEntry() : id(0), type(0), bytes(0), dataOrPos(0) {};
	};

	struct TweakedIFDInfo {
		XMP_Uns16 count;
		TweakedIFDEntry* entries;
		TweakedIFDInfo() : count(0), entries(0) {};
	};

	TweakedIFDInfo containedIFDs[kTIFF_KnownIFDCount];

	static void SortIFD ( TweakedIFDInfo* thisIFD );

	XMP_Uns32 ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd );

	const TweakedIFDEntry* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	const inline void* GetDataPtr ( const TweakedIFDEntry* tifdEntry ) const
		{ if ( GetUns32AsIs(&tifdEntry->bytes) <= 4 ) {
		  	return &tifdEntry->dataOrPos;
		  } else {
			XMP_Uns32 pos = GetUns32AsIs(&tifdEntry->dataOrPos);
			if (pos + GetUns32AsIs (&tifdEntry->bytes) > this->tiffLength) {
				// Invalid file.
				// The data is past the length of the TIFF.
				return NULL;
			}
			return (this->tiffStream + pos);
		  }
		}

	static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for TIFF_Reader", kXMPErr_InternalFailure ); };

};	// TIFF_MemoryReader


// =================================================================================================
// =================================================================================================


// =================================================================================================
// TIFF_FileWriter
// ===============

class TIFF_FileWriter : public TIFF_Manager {	// The derived class for file-based or read-write access.
public:

	bool HasExifIFD() const { return this->containedIFDs[kTIFF_ExifIFD].tagMap.size() != 0; };
	bool HasGPSInfoIFD() const { return this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size() != 0; };

	bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const;

	bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const;

	void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr );

	void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id );

	XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;

	bool GetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const;
	bool GetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const;
	bool GetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const;
	bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const;
	bool GetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;
	bool GetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const;

	bool GetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const;
	bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const;

	bool GetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const;
	bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const;

	bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const;

	bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;

	void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding );

	bool IsChanged() { return this->changed; };

	bool IsLegacyChanged();

	enum { kDoNotCopyData = false };

	void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true );
	void ParseFileStream   ( XMP_IO* fileRef );

	void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen );

	XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false );
	void      UpdateFileStream   ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker );

	TIFF_FileWriter();

	virtual ~TIFF_FileWriter();

private:

	bool changed, legacyDeleted;
	bool memParsed, fileParsed;
	bool ownedStream;

	XMP_Uns8* memStream;
	XMP_Uns32 tiffLength;

	// Memory usage notes: TIFF_FileWriter is for file-based OR read/write usage. For memory-based
	// streams the dataPtr is initially into the stream, regardless of size. For file-based streams
	// the dataPtr is initially a separate allocation for large values (over 4 bytes), and points to
	// the smallValue field for small values. This is also the usage when a tag is changed (for both
	// memory and file cases), the dataPtr is a separate allocation for large values (over 4 bytes),
	// and points to the smallValue field for small values.

	// ! The working data values are always stream endian, no matter where stored. They are flipped
	// ! as necessary by GetTag and SetTag.

	static const bool kIsFileBased   = true;	// For use in the InternalTagInfo constructor.
	static const bool kIsMemoryBased = false;

	class InternalTagInfo {
	public:

		XMP_Uns16 id;
		XMP_Uns16 type;
		XMP_Uns32 count;
		XMP_Uns32 dataLen;
		XMP_Uns32 smallValue;		// Small value in stream endianness, but "left" justified.
		XMP_Uns8* dataPtr;			// Parsing captures all small values, only large ones that we care about.
		XMP_Uns32 origDataLen;		// The original (parse time) data length in bytes.
		XMP_Uns32 origDataOffset;	// The original data offset, regardless of length.
		bool      changed;
		bool      fileBased;

		inline void FreeData() {
			if ( this->fileBased || this->changed ) {
				if ( (this->dataLen > 4) && (this->dataPtr != 0) ) { free ( this->dataPtr ); this->dataPtr = 0; }
			}
		}

		InternalTagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, bool _fileBased )
			: id(_id), type(_type), count(_count), dataLen(0), smallValue(0), dataPtr(0),
			  origDataLen(0), origDataOffset(0), changed(false), fileBased(_fileBased) {};
		~InternalTagInfo() { this->FreeData(); };

		void operator=  ( const InternalTagInfo & in )
		{
			// ! Gag! Transfer ownership of the dataPtr!
			this->FreeData();
			memcpy ( this, &in, sizeof(*this) );	// AUDIT: Use of sizeof(InternalTagInfo) is safe.
			if ( this->dataLen <= 4 ) {
				this->dataPtr = (XMP_Uns8*) &this->smallValue;	// Don't use the copied pointer.
			} else {
				*((XMP_Uns8**)&in.dataPtr) = 0;	// The pointer is now owned by "this".
			}
		};

	private:

		InternalTagInfo()	// Hidden on purpose, fileBased must be properly set.
			: id(0), type(0), count(0), dataLen(0), smallValue(0), dataPtr(0),
			  origDataLen(0), origDataOffset(0), changed(false), fileBased(false) {};

	};

	typedef std::map<XMP_Uns16,InternalTagInfo> InternalTagMap;

	struct InternalIFDInfo {
		bool changed;
		XMP_Uns16 origCount;		// Original number of IFD entries.
		XMP_Uns32 origIFDOffset;	// Original stream offset of the IFD.
		XMP_Uns32 origNextIFD;		// Original stream offset of the following IFD.
		InternalTagMap tagMap;
		InternalIFDInfo() : changed(false), origCount(0), origIFDOffset(0), origNextIFD(0) {};
		inline void clear()
		{
			this->changed = false;
			this->origCount = 0;
			this->origIFDOffset = this->origNextIFD = 0;
			this->tagMap.clear();
		};
	};

	InternalIFDInfo containedIFDs[kTIFF_KnownIFDCount];

	static XMP_Uns8 PickIFD ( XMP_Uns8 ifd, XMP_Uns16 id );
	const InternalTagInfo* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	void DeleteExistingInfo();

	XMP_Uns32 ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd );
	XMP_Uns32 ProcessFileIFD   ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef );

	void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd );

	void* CopyTagToMasterIFD ( const TagInfo& ps6Tag, InternalIFDInfo* masterIFD );

	void PreflightIFDLinkage();

	XMP_Uns32 DetermineVisibleLength();

	XMP_Uns32 DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
									bool      appendedIFDs[kTIFF_KnownIFDCount],
									XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount],
									bool      appendAll = false );

	void UpdateMemByAppend  ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out,
							  bool appendAll = false, XMP_Uns32 extraSpace = 0 );
	void UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out );

	void WriteFileIFD ( XMP_IO* fileRef, InternalIFDInfo & thisIFD );

};	// TIFF_FileWriter

XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound );

// =================================================================================================

#endif	// __TIFF_Support_hpp__
