/*****************************************************************************/
// Copyright 2015-2023 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.
/*****************************************************************************/

#ifndef __dng_big_table__
#define __dng_big_table__

/*****************************************************************************/

#include "dng_classes.h"
#include "dng_camera_profile.h"
#include "dng_rect.h"
#include "dng_safe_arithmetic.h"
#include "dng_semantic_mask.h"
#include "dng_tag_types.h"
#include "dng_tag_values.h"

#include <map>
#include <memory>
#include <vector>

/*****************************************************************************/

void dng_big_table_cache_flush ();

void dng_big_table_cache_clear ();

/*****************************************************************************/

class dng_big_table
	{

	protected:

		enum BigTableTypeEnum
			{
			btt_LookTable				  = 0,
			btt_RGBTable				  = 1,
			btt_ImageTable				  = 2,
			btt_CompressedSettingsTable	  = 3,
			btt_UncompressedSettingsTable = 4,
			btt_PackedImageTable		  = 5,
			};

	private:

		dng_fingerprint fFingerprint;

		dng_big_table_cache * fCache;
		
		bool fIsMissing;

	protected:

		dng_big_table (dng_big_table_cache *cache);

		dng_big_table (const dng_big_table &table);

		dng_big_table & operator= (const dng_big_table &table);

	public:

		virtual ~dng_big_table ();
		
		bool IsMissing () const
			{
			return fIsMissing;
			}
		
		void SetMissing ()
			{
			fIsMissing = true;
			}
		
		bool IsEmbedNever () const
			{
			return (GetFlags () & 1) != 0;
			}
		
		void SetEmbedNever ()
			{
			SetFlags (GetFlags () | 1);
			}

		virtual bool IsValid () const = 0;

		const dng_fingerprint & Fingerprint () const;

		bool DecodeFromBinary (dng_host &host,
							   const uint8 * compressedData,
							   uint32 compressedSize,
							   AutoPtr<dng_memory_block> *uncompressedCache = nullptr);
		
		static void ASCIItoBinary (dng_memory_allocator &allocator,
								   const char *sPtr,
								   uint32 sCount,
								   AutoPtr<dng_memory_block> &dBlock,
								   uint32 &dCount);
								   
		bool DecodeFromString (dng_host &host,
							   const dng_string &block1);
		
		dng_memory_block * EncodeAsBinary (dng_memory_allocator &allocator,
										   uint32 &compressedSize) const;
		
		dng_memory_block * EncodeAsString (dng_memory_allocator &allocator) const;

		bool ExtractFromCache (const dng_fingerprint &fingerprint);

		
		#if qDNGUseXMP
		
		bool ReadTableFromXMP (const dng_xmp &xmp,
							   const char *ns,
							   const dng_fingerprint &fingerprint,
							   dng_big_table_storage *storage = nullptr,
							   dng_abort_sniffer *sniffer = nullptr);
		
		bool ReadFromXMP (const dng_xmp &xmp,
						  const char *ns,
						  const char *path,
						  dng_big_table_storage &storage,
						  dng_abort_sniffer *sniffer = NULL);

		void WriteToXMP (dng_xmp &xmp,
						 const char *ns,
						 const char *path,
						 dng_big_table_storage &storage) const;

		#endif	// qDNGUseXMP

		#if qDNGValidate

		// Write uncompressed data to stream; intended for debugging.

		void WriteUncompressedStream (dng_stream &stream) const;

		#endif	// qDNGValidate

	protected:
	
		virtual dng_fingerprint ComputeFingerprint () const;

		void RecomputeFingerprint ();
		
		virtual bool UseCompression () const
			{
			return true;
			}

		virtual bool GetStream (dng_stream &stream) = 0;

		virtual void PutStream (dng_stream &stream,
								bool forFingerprint) const = 0;
		
		virtual uint32 GetFlags () const = 0;
		
		virtual void SetFlags (uint32 flags) = 0;

	};

/*****************************************************************************/

class dng_big_table_accessor
	{
	
	public:
	
		dng_big_table_accessor ()
			{
			}
			
		virtual ~dng_big_table_accessor ()
			{
			}
									   
		virtual bool HasTable (const dng_fingerprint & /* fingerprint */) const
			{
			return false;
			}
		
		virtual bool GetTable (const dng_fingerprint & /* fingerprint */,
							   dng_ref_counted_block & /* block */) const
			{
			return false;
			}
	
		virtual void AddTable (const dng_fingerprint & /* fingerprint */,
							   const dng_ref_counted_block & /* block */)
			{
			ThrowProgramError ("AddTable not implemented");
			}
			
		virtual void CopyToDictionary (dng_big_table_dictionary & /* dictionary */) const
			{
			ThrowProgramError ("CopyToDictionary not implemented");
			}
	
		virtual bool GroupToInstance (const dng_fingerprint & /* groupDigest */,
									  dng_fingerprint & /* instanceDigest */) const
			{
			return false;
			}

	};
	
/*****************************************************************************/

class dng_big_table_dictionary : public dng_big_table_accessor
	{
	
	private:
	
		std::map<dng_fingerprint, dng_ref_counted_block> fMap;
		
	public:
	
		const std::map<dng_fingerprint, dng_ref_counted_block> & Map () const
			{
			return fMap;
			}
		
		bool IsEmpty () const
			{
			return fMap.empty ();
			}
			
		virtual bool HasTable (const dng_fingerprint &fingerprint) const;
		
		virtual bool GetTable (const dng_fingerprint &fingerprint,
							   dng_ref_counted_block &block) const;
	
		virtual void AddTable (const dng_fingerprint &fingerprint,
							   const dng_ref_counted_block &block);
	
		virtual void CopyToDictionary (dng_big_table_dictionary & /* dictionary */) const;
			
	};
	
/*****************************************************************************/

class dng_big_table_index
	{
	
	public:
	
		struct IndexEntry
			{
			uint32 fTableSize;
			uint64 fTableOffset;
			};
			
	private:
	
		std::map<dng_fingerprint, IndexEntry> fMap;
		
	public:
	
		dng_big_table_index ();
			
		bool IsEmpty () const
			{
			return fMap.empty ();
			}
			
		const std::map<dng_fingerprint, IndexEntry> & Map () const
			{
			return fMap;
			}
			
		bool HasEntry (const dng_fingerprint &fingerprint) const;

		bool GetEntry (const dng_fingerprint &fingerprint,
					   uint32 &tableSize,
					   uint64 &tableOffset) const;

		void AddEntry (const dng_fingerprint &fingerprint,
					   uint32 tableSize,
					   uint64 tableOffset);
					   
	};

/*****************************************************************************/

class dng_big_table_group_index
	{
	
	private:
	
		std::map<dng_fingerprint,			 // group digest
				 dng_fingerprint> fMap;		 // instance digest
		
	public:
	
		bool IsEmpty () const
			{
			return fMap.empty ();
			}
			
		const std::map<dng_fingerprint,
					   dng_fingerprint> & Map () const
			{
			return fMap;
			}
			
		bool HasEntry (const dng_fingerprint &groupDigest) const
			{
			return fMap.find (groupDigest) != fMap.end ();
			}

		bool GetEntry (const dng_fingerprint &groupDigest,
					   dng_fingerprint &instanceDigest) const;

		void AddEntry (const dng_fingerprint &groupDigest,
					   const dng_fingerprint &instanceDigest)
			{
			if (fMap.find (groupDigest) == fMap.end ())
				fMap.insert (std::make_pair (groupDigest,
											 instanceDigest));
			}
					   
	};

/*****************************************************************************/

class dng_big_table_storage
	{
	
	public:
		
		dng_big_table_storage ();
		
		virtual ~dng_big_table_storage ();
		
		virtual bool ReadTable (dng_big_table &table,
								const dng_fingerprint &fingerprint,
								dng_memory_allocator &allocator);
	
		virtual bool WriteTable (const dng_big_table &table,
								 const dng_fingerprint &fingerprint,
								 dng_memory_allocator &allocator);
		
		virtual void MissingTable (const dng_fingerprint &fingerprint);

		virtual bool GroupToInstance (const dng_fingerprint &groupDigest,
									  dng_fingerprint &instanceDigest) const;

	};

/*****************************************************************************/

class dng_look_table : public dng_big_table
	{

	friend class dng_look_table_cache;

	public:

		enum
			{
			
			// Tables are are allowed to trade off resolution
			// between dimensions, but need to keep total
			// samples below this limit.  This results in
			// 216K memory footprint (12 byte per sample)
			// which is similar in size to the RGB table
			// size limit.
			
			kMaxTotalSamples = 36 * 32 * 16,
			
			// Also each must be within their own limits.
			
			kMaxHueSamples = 360,
			kMaxSatSamples = 256,
			kMaxValSamples = 256

			};

	private:

		enum
			{
			kLookTableVersion1 = 1,
			kLookTableVersion2 = 2
			};

		// Table data affecting fingerprint and caching.

		struct table_data
			{

			// 3-D hue/sat table to apply a "look".
			
			dng_hue_sat_map fMap;

			// Value (V of HSV) encoding for look table.

			uint32 fEncoding;
			
			// Minimum and maximum scale amounts supported by table.

			real64 fMinAmount;
			real64 fMaxAmount;

			// Does this table have only monochrome output (when amount is 1.0)?
			
			bool fMonochrome;
			
			// Flags.
			
			uint32 fFlags;

			// Constructor to set defaults.

			table_data ()

				:	fMap		()
				,	fEncoding	(encoding_Linear)
				,	fMinAmount	(1.0)
				,	fMaxAmount	(1.0)
				,	fMonochrome (false)
				,	fFlags		(0)

				{
				}
				
			// Compute monchrome flag.
			
			void ComputeMonochrome ()
				{
				
				fMonochrome = true;
				
				uint32 count = fMap.DeltasCount ();

				dng_hue_sat_map::HSBModify * deltas = fMap.GetDeltas ();
				
				for (uint32 index = 0; index < count; index++)
					{

					if (deltas [index] . fSatScale != 0.0f)
						{
						
						fMonochrome = false;
						
						return;
						
						}
					
					}
				
				}

			};

		table_data fData;

		// Amount to apply at runtime (does not affect fingerprint).

		real64 fAmount;

	public:

		dng_look_table ();

		dng_look_table (const dng_look_table &table);

		dng_look_table & operator= (const dng_look_table &table);

		virtual ~dng_look_table ();

		bool operator== (const dng_look_table &table) const
			{
			return Fingerprint () == table.Fingerprint () &&
				   Amount	   () == table.Amount	   () &&
				   IsMissing   () == table.IsMissing   ();
			}

		bool operator!= (const dng_look_table &table) const
			{
			return !(*this == table);
			}

		void Set (const dng_hue_sat_map &map,
				  uint32 encoding);

		virtual bool IsValid () const;

		void SetInvalid ();

		real64 MinAmount () const
			{
			return fData.fMinAmount;
			}

		real64 MaxAmount () const
			{
			return fData.fMaxAmount;
			}

		void SetAmountRange (real64 minAmount,
							 real64 maxAmount)
			{

			fData.fMinAmount = Pin_real64 (0.0,
										   Round_int32 (minAmount * 100.0) * 0.01,
										   1.0);
				
			fData.fMaxAmount = Pin_real64 (1.0,
										   Round_int32 (maxAmount * 100.0) * 0.01,
										   2.0);
			
			fAmount = Pin_real64 (fData.fMinAmount, fAmount, fData.fMaxAmount);

			RecomputeFingerprint ();

			}

		real64 Amount () const
			{
			return fAmount;
			}

		void SetAmount (real64 amount)
			{

			fAmount = Pin_real64 (fData.fMinAmount,
								  Round_int32 (amount * 100.0) * 0.01,
								  fData.fMaxAmount);

			// Not part of fingerprint.

			}

		const dng_hue_sat_map & Map () const
			{
			return fData.fMap;
			}

		uint32 Encoding () const
			{
			return fData.fEncoding;
			}
		
		bool Monochrome () const
			{
			return IsValid () && fAmount == 1.0 && fData.fMonochrome;
			}

	protected:

		virtual bool GetStream (dng_stream &stream);

		virtual void PutStream (dng_stream &stream,
								bool forFingerprint) const;

		virtual uint32 GetFlags () const
			{
			return fData.fFlags;
			}
		
		virtual void SetFlags (uint32 flags)
			{
			
			fData.fFlags = flags;
			
			RecomputeFingerprint ();

			}

	};

/*****************************************************************************/

class dng_rgb_table : public dng_big_table
	{

	friend class dng_rgb_table_cache;

	public:

		enum
			{

			kMinDivisions1D = 2,
			kMaxDivisions1D = 4096,

			kMinDivisions3D = 2,
			kMaxDivisions3D = 32,

			kMaxDivisions3D_InMemory = 130

			};

		enum primaries_enum
			{

			primaries_sRGB = 0,
			primaries_Adobe,
			primaries_ProPhoto,
			primaries_P3,
			primaries_Rec2020,

			primaries_count

			};

		enum gamma_enum
			{

			gamma_Linear = 0,
			gamma_sRGB,
			gamma_1_8,
			gamma_2_2,
			gamma_Rec2020,

			gamma_count

			};

		enum gamut_enum
			{

			gamut_clip = 0,
			gamut_extend,

			gamut_count

			};

	private:

		enum
			{
			kRGBTableVersion = 1
			};

		// Table data affecting fingerprint and caching.

		struct table_data
			{

			// Number of dimensions of the table (1 or 3).

			uint32 fDimensions;

			// Number of samples per side of table.
			
			uint32 fDivisions;

			// Sample data.	 16-bit unsigned encoding.
			// Right zero padded to 64 bits per sample (i.e. RGB0).
			
			dng_ref_counted_block fSamples;

			// Color primaries for table.

			primaries_enum fPrimaries;

			// Gamma encoding for table.

			gamma_enum fGamma;

			// Gamut processing option for table.

			gamut_enum fGamut;

			// Minimum and maximum scale amounts supported by table.

			real64 fMinAmount;
			real64 fMaxAmount;

			// Does this table have only monochrome output (when amount is 1.0)?
			
			bool fMonochrome;
			
			// Flags.
			
			uint32 fFlags;

			// Constructor to set defaults.

			table_data ()

				:	fDimensions (0)
				,	fDivisions	(0)
				,	fSamples	()
				,	fPrimaries	(primaries_sRGB)
				,	fGamma		(gamma_sRGB)
				,	fGamut		(gamut_clip)
				,	fMinAmount	(0.0)
				,	fMaxAmount	(2.0)
				,	fMonochrome (false)
				,	fFlags		(0)

				{
				}

			// Compute monchrome flag.
			
			void ComputeMonochrome ()
				{
				
				if (fPrimaries != primaries_ProPhoto &&
					fGamut	   != gamut_clip)
					{
					
					fMonochrome = false;
					
					return;
					
					}
					
				if (fDimensions != 3)
					{
					
					fMonochrome = false;
					
					return;
					
					}
				
				fMonochrome = true;
				
				uint32 count = fDivisions * fDivisions * fDivisions;

				const uint16 * sample = fSamples.Buffer_uint16 ();
				
				for (uint32 index = 0; index < count; index++)
					{
					
					if (sample [0] != sample [1] ||
						sample [0] != sample [2])
						{

						fMonochrome = false;
						
						return;
						
						}
						
					sample += 4;
					
					}
					
				}

			};

		table_data fData;

		// Amount to apply at runtime (does not affect fingerprint).

		real64 fAmount;

	public:

		dng_rgb_table ();

		dng_rgb_table (const dng_rgb_table &table);

		dng_rgb_table & operator= (const dng_rgb_table &table);

		virtual ~dng_rgb_table ();

		bool operator== (const dng_rgb_table &table) const
			{
			return Fingerprint () == table.Fingerprint () &&
				   Amount	   () == table.Amount	   () &&
				   IsMissing   () == table.IsMissing   ();
			}

		bool operator!= (const dng_rgb_table &table) const
			{
			return !(*this == table);
			}

		virtual bool IsValid () const;

		void SetInvalid ();

		primaries_enum Primaries () const
			{
			return fData.fPrimaries;
			}

		void SetPrimaries (primaries_enum primaries)
			{

			fData.fPrimaries = primaries;

			fData.ComputeMonochrome ();

			RecomputeFingerprint ();

			}

		gamma_enum Gamma () const
			{
			return fData.fGamma;
			}

		void SetGamma (gamma_enum gamma)
			{

			fData.fGamma = gamma;

			RecomputeFingerprint ();

			}

		gamut_enum Gamut () const
			{
			return fData.fGamut;
			}

		void SetGamut (gamut_enum gamut)
			{

			fData.fGamut = gamut;

			fData.ComputeMonochrome ();

			RecomputeFingerprint ();

			}

		real64 MinAmount () const
			{
			return fData.fMinAmount;
			}

		real64 MaxAmount () const
			{
			return fData.fMaxAmount;
			}

		void SetAmountRange (real64 minAmount,
							 real64 maxAmount)
			{

			fData.fMinAmount = Pin_real64 (0.0,
										   Round_int32 (minAmount * 100.0) * 0.01,
										   1.0);
				
			fData.fMaxAmount = Pin_real64 (1.0,
										   Round_int32 (maxAmount * 100.0) * 0.01,
										   2.0);
			
			fAmount = Pin_real64 (fData.fMinAmount, fAmount, fData.fMaxAmount);

			RecomputeFingerprint ();

			}

		real64 Amount () const
			{
			return fAmount;
			}

		void SetAmount (real64 amount)
			{

			fAmount = Pin_real64 (fData.fMinAmount,
								  Round_int32 (amount * 100.0) * 0.01,
								  fData.fMaxAmount);

			// Not part of fingerprint.

			}

		uint32 Dimensions () const
			{
			return fData.fDimensions;
			}

		uint32 Divisions () const
			{
			return fData.fDivisions;
			}

		const uint16 * Samples () const
			{
			return fData.fSamples.Buffer_uint16 ();
			}

		bool Monochrome () const
			{
			return IsValid () && fAmount == 1.0 && fData.fMonochrome;
			}

		void Set (uint32 dimensions,
				  uint32 divisions,
				  dng_ref_counted_block samples);

	protected:

		virtual bool GetStream (dng_stream &stream);

		virtual void PutStream (dng_stream &stream,
								bool forFingerprint) const;

		virtual uint32 GetFlags () const
			{
			return fData.fFlags;
			}

		virtual void SetFlags (uint32 flags)
			{
			
			fData.fFlags = flags;
			
			RecomputeFingerprint ();

			}

	};

/*****************************************************************************/

// Compression info for image tables. This base class implementation uses
// Lossless JPEG for integer data and Deflate for floating-point data.

class dng_image_table_compression_info
	{

	public:

		virtual ~dng_image_table_compression_info ();
		
		virtual uint32 Type () const
			{
			return 0;
			}

		virtual void Compress (dng_host &host,
							   dng_stream &stream,
							   const dng_image &image) const;
		
	};

/*****************************************************************************/

// This subclass uses JPEG XL to compress image tables.

class dng_image_table_jxl_compression_info : public dng_image_table_compression_info
	{

	public:

		AutoPtr<dng_jxl_encode_settings> fEncodeSettings;

		std::shared_ptr<const dng_jxl_color_space_info> fColorSpaceInfo;

		// If true, then write floating-point image tables using fp16 instead
		// of fp32. This field is ignored for integer images.
		
		bool fPreferHalfFloat = true;

	public:

		dng_image_table_jxl_compression_info ();

		uint32 Type () const override
			{
			return ccJXL;
			}
		
		void Compress (dng_host &host,
					   dng_stream &stream,
					   const dng_image &image) const override;
		
	};

/*****************************************************************************/

class dng_image_table_data
	{

	public:
		
		std::shared_ptr<const dng_image> fImage;

		mutable std::shared_ptr<const dng_memory_block> fCompressedData;

		uint32 fCompressionType = 0;
		
	};

/*****************************************************************************/

class dng_image_table : public dng_big_table
	{
	
	friend class dng_image_table_cache;
	friend class dng_packed_image_table_cache;

	private:

		enum
			{
			kImageTableVersion = 1
			};

	protected:
	
		std::shared_ptr<const dng_image> fImage;

		// For lossy-compressed big table data, we store a copy of the
		// compressed data to avoid round-trip errors.

		mutable std::shared_ptr<const dng_memory_block> fCompressedData;

		// Compression type -- for information/debugging purposes only.
		
		uint32 fCompressionType = 0;
		
	public:
	
		dng_image_table ();

		dng_image_table (const dng_image_table &table);

		dng_image_table & operator= (const dng_image_table &table);

		virtual ~dng_image_table ();
	
		virtual bool IsValid () const;
		
		void SetInvalid ();

		bool operator== (const dng_image_table &table) const
			{
			return Fingerprint () == table.Fingerprint () &&
				   IsMissing   () == table.IsMissing   ();
			}

		bool operator!= (const dng_image_table &table) const
			{
			return !(*this == table);
			}
			
		const dng_image & Image () const
			{
			return *fImage;
			}
			
		std::shared_ptr<const dng_image> ShareImage () const
			{
			return fImage;
			};
			
		void SetImage (const dng_image *image,
					   const dng_image_table_compression_info *compressionInfo = nullptr,
					   dng_abort_sniffer *sniffer = nullptr);

		void SetImage (const std::shared_ptr<const dng_image> &image,
					   const dng_image_table_compression_info *compressionInfo = nullptr,
					   dng_abort_sniffer *sniffer = nullptr);

		uint32 CompressionType () const
			{
			return fCompressionType;
			}

		std::shared_ptr<const dng_memory_block> CompressedData () const
			{
			return fCompressedData;
			}

	private:

		void SetData (const dng_image_table_data &data);

		void GetData (dng_image_table_data &data) const;

	protected:

		virtual dng_host * MakeHost (dng_abort_sniffer *sniffer) const;

		virtual dng_fingerprint ComputeFingerprint () const;

		// dng_image_table uses its own (internal) compression method
		// optimized for image content, e.g., ccDeflate or ccJXL, and
		// therefore does not use the general, external compression method
		// provided by the base class.
		
		virtual bool UseCompression () const
			{
			return false;
			}
			
		virtual bool GetStream (dng_stream &stream);

		virtual void PutStream (dng_stream &stream,
								bool forFingerprint) const;
		
		virtual uint32 GetFlags () const
			{
			return 0;
			}

		virtual void SetFlags (uint32 /* flags */)
			{
			}

		virtual void CompressImage (const dng_image_table_compression_info &info,
									dng_abort_sniffer *sniffer);

	protected:
		
		virtual void
			PutCompressedStream (dng_stream &stream,
								 bool forFingerprint,
								 const dng_image_table_compression_info &info) const;

	};

/*****************************************************************************/

// A packed (opaque) image table. Reading the table will obtain only the
// (compressed) bytes but does not perform any decompression until Unpack is
// called. 

class dng_packed_image_table : public dng_big_table
	{

	friend class dng_packed_image_table_cache;
	
	private:

		enum
			{
			kPackedImageTableVersion = 2
			};

	protected:

		// Digest of the image table.

		dng_fingerprint fTableDigest;

		// The unpacked / concrete image table.

		std::unique_ptr<dng_image_table> fTable;

		// The packed / opaque image table.
		
		std::shared_ptr<const dng_memory_block> fBlock;

		// Image properties.

		dng_point fSize;

		uint32 fPlanes = 0;

		uint32 fPixelType = 0;

	public:

		dng_packed_image_table ();
		
		dng_packed_image_table (const dng_packed_image_table &table);

		dng_packed_image_table & operator= (const dng_packed_image_table &table);

	public:

		const dng_image_table & Table () const;

		const dng_image & Image () const
			{
			return Table ().Image ();
			}

		const_dng_image_sptr ShareImage () const
			{
			return fTable ? fTable->ShareImage () : nullptr;
			}

		std::shared_ptr<const dng_memory_block> ShareBlock () const
			{
			return fBlock;
			}

		bool IsValid () const override;

		bool HasTable () const
			{
			return fTable && fTable->IsValid ();
			}

		bool IsValidUnpacked () const
			{
			return HasTable ();
			}

		bool IsPacked () const
			{
			return fBlock != nullptr;
			}

		// The image data is already compressed internally, so we do not want
		// the overall big table logic perform any additional compression.

		bool UseCompression () const override
			{
			return false;
			}

		uint32 PackedBytes () const;

		// Info about the image. This is available even if the table is packed.

		const dng_point & Size () const
			{
			return fSize;
			}

		uint32 Planes () const
			{
			return fPlanes;
			}

		uint32 PixelType () const
			{
			return fPixelType;
			}

	public:

		bool operator== (const dng_packed_image_table &table) const
			{
			return (Fingerprint () == table.Fingerprint () &&
					IsMissing	() == table.IsMissing	());
			}

		bool operator!= (const dng_packed_image_table &table) const
			{
			return !(*this == table);
			}
			
	public:

		// Factory methods.

		virtual dng_host * MakeHost (dng_abort_sniffer *sniffer) const;

		virtual dng_image_table * MakeTable () const;

		virtual dng_image_table * CloneTable () const;

	public:

		void Clear ();

		void ClearPackedData ();

		void SetImage (const dng_image *image,
					   const dng_image_table_compression_info *compressionInfo = nullptr,
					   dng_abort_sniffer *sniffer = nullptr);

		void SetImage (const std::shared_ptr<const dng_image> &image,
					   const dng_image_table_compression_info *compressionInfo = nullptr,
					   dng_abort_sniffer *sniffer = nullptr);

		void Unpack (dng_abort_sniffer *sniffer);

		void Pack (dng_abort_sniffer *sniffer);

	protected:

		bool GetStream (dng_stream &stream) override;

		void PutStream (dng_stream &stream,
						bool forFingerprint) const override;
		
		uint32 GetFlags () const override
			{
			return 0;
			}
		
		void SetFlags (uint32 /* flags */) override
			{
			}

		dng_fingerprint ComputeFingerprint () const override
			{
			return fTableDigest;
			}
		
	};
	
/*****************************************************************************/

// Implements RGBTables tag from DNG 1.6.

class dng_masked_rgb_table: private dng_uncopyable
	{
		
	private:

		// TableSemanticName and LengthTableSemanticName fields.

		dng_string fTableSemanticName;

		// PixelType field. Note that this is NOT the list of pixel types in
		// dng_tag_types.h but is instead 0, 1, or 2 per DNG 1.6 spec for
		// RGBTables tag. Only three values are supported:
		//
		// 0 = ttByte
		// 1 = ttShort
		// 2 = ttFloat
		
		uint32 fPixelType = 0;

		// The following RGBTables tag fields are represented inside the
		// dng_rgb_table data structure, along with the table data itself.
		//
		// Divisions	  dng_rgb_table::table_data::fDivisions
		// GammaEncoding  dng_rgb_table::table_data::fGamma
		// ColorPrimaries dng_rgb_table::table_data::fPrimaries
		// GamutExtension dng_rgb_table::table_data::fGamut

		dng_rgb_table fTable;

		// fTable's internal representation is ttShort (unsigned 16-bit
		// integer). To avoid round-tripping errors, optionally store a copy
		// of the sample data in the file.

		std::shared_ptr<dng_memory_block> fStoredData;

	public:

		dng_masked_rgb_table ();

		dng_masked_rgb_table (dng_string tableSemanticName,
							  uint32 pixelType,
							  const dng_rgb_table &table);

		void Validate () const;
		
		void GetStream (dng_host &host,
						dng_stream &stream);
		
		void PutStream (dng_stream &stream) const;

		void AddDigest (dng_md5_printer &printer) const;

		const dng_string & SemanticName () const
			{
			return fTableSemanticName;
			}

		const dng_rgb_table & Table () const
			{
			return fTable;
			}

		uint32 PixelType () const
			{
			return fPixelType;
			}

		#if qDNGValidate
		void Dump (uint32 indent) const;
		#endif

	private:

		static void CheckDivisions (uint32 divisions);

		static void CheckPixelType (uint32 pixelType);

		static void CheckGammaEncoding (dng_rgb_table::gamma_enum gamma);

		static void CheckGamutExtension (dng_rgb_table::gamut_enum gamut);

		static void CheckColorPrimaries (dng_rgb_table::primaries_enum primaries);
		
	};

/*****************************************************************************/

// Implements RGBTables tag from DNG 1.6.

class dng_masked_rgb_tables: private dng_uncopyable
	{

	public:
		
		static const uint32 kMaxTables = 20;

		enum composite_method
			{
			kWeightedSum = 0,
			kSequential	 = 1,
			};

	private:

		composite_method fCompositeMethod = kWeightedSum;

		std::vector<std::shared_ptr<dng_masked_rgb_table> > fTables;

	public:

		dng_masked_rgb_tables ();

		dng_masked_rgb_tables
			(const std::vector<std::shared_ptr<dng_masked_rgb_table> > &tables,
			 composite_method compositeMethod);

		const std::vector<std::shared_ptr<dng_masked_rgb_table> > & Tables () const
			{
			return fTables;
			}
		
		bool IsNOP () const;

		void Validate () const;

		void AddDigest (dng_md5_printer &printer) const;

		void PutStream (dng_stream &stream) const;

		static dng_masked_rgb_tables * GetStream (dng_host &host,
												  dng_stream &stream,
												  bool isDraft);

		composite_method CompositeMethod () const
			{
			return fCompositeMethod;
			}

		bool UseSequentialMethod () const
			{
			return (fCompositeMethod == kSequential);
			}

		bool UseWeightedSumMethod () const
			{
			return !UseSequentialMethod ();
			}

		void SetCompositeMethod (composite_method method)
			{
			fCompositeMethod = method;
			}

		#if qDNGValidate
		void Dump () const;
		#endif
		
	};

/*****************************************************************************/

class dng_rgb_to_rgb_table_data
	{

	public:

		dng_rgb_table fTable;

		bool fNeedMatrix;

		dng_matrix fEncodeMatrix;
		dng_matrix fDecodeMatrix;
		
		AutoPtr<dng_1d_table> fEncodeTable;
		AutoPtr<dng_1d_table> fDecodeTable;

		AutoPtr<dng_1d_table> fTable1D [3];

	public:

		dng_rgb_to_rgb_table_data (dng_host &host,
								   const dng_rgb_table &table);

		virtual ~dng_rgb_to_rgb_table_data ();

		virtual void Process_32 (dng_pixel_buffer &buffer,
								 dng_pixel_buffer *optMaskBuffer, // may be nullptr
								 uint32 optMaskPlane,
								 const dng_rect &dstArea,
								 uint32 bufferStartPlane,
								 bool needOverrange);

		void AddDigest (dng_md5_printer &printer) const;

	};

/*****************************************************************************/

class dng_masked_rgb_table_render_data
	{

	public:

		bool fUseSequentialMethod = false;
		
		// A vector of paired RGB tables & masks. A given RGB table is meant
		// to be applied with the corresponding mask.

		std::vector<std::pair<dng_masked_rgb_table_sptr,
							  dng_semantic_mask> > fMaskedTables;

		// A vector of RGB transform data that is precomputed. This vector has
		// a one-to-one correspondence with fMaskedTables, above.

		std::vector<dng_rgb_to_rgb_table_data_sptr> fMaskedTableData;

		// Background RGB table and data. Will be left as nullptr if there is
		// no background table.
		
		dng_masked_rgb_table_sptr fBackgroundTable;

		AutoPtr<dng_rgb_to_rgb_table_data> fBackgroundTableData;

		// In the sequential method case, if there is a background table then
		// this is its index in the fMaskedTables vector. Otherwise this gets
		// set to fMaskedTables.size () as it may be used in ways that require
		// to be larger than all other table indices when there is no
		// background table.

		uint32 fBackgroundTableIndex = 0;

	public:

		void Initialize (const dng_negative &negative,
						 const dng_camera_profile &profile);

		bool IsNOP () const
			{
			return (fMaskedTables.empty () && !fBackgroundTable);
			}

		void PrepareRGBtoRGBTableData (dng_host &host);
		
	};

/*****************************************************************************/

// Big tables in the XMP_NS_CRS and XMP_NS_CRSS are removed from the
// xmp and added to the big table dictionary.

void MoveBigTablesToDictionary (dng_xmp &xmp,
								dng_big_table_dictionary &dictionary);

/******************************************************************************/

// Parses an XMP block into decoded xmp and a big table dictionary.
// Big tables in the XMP_NS_CRS and XMP_NS_CRSS are removed from the decoded
// xmp and added to the big table dictionary.

void DualParseXMP (dng_host &host,
				   dng_xmp &xmp,
				   dng_big_table_dictionary &dictionary,
				   const void *blockData,
				   uint32 blockSize);

/*****************************************************************************/

#endif	// __dng_big_table__
	
/*****************************************************************************/
