//
// Tools_Histogram.h
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __cplusplus
#error Tools_Histogram.h requires C++ compilation
#endif

#ifndef _included_histgram_hpp_
#define _included_histgram_hpp_

#include <stdio.h>

#include "ImageProcessing/Tools_RGBTypes.h"

typedef RGBQuad<unsigned char,unsigned long> HHPixel;

class HHHashFunction
{
private:
	unsigned long red_hash_values [256];
	unsigned long green_hash_values [256];
	unsigned long blue_hash_values [256];
	
	void Init (unsigned long table_size);
	
protected:
	HHHashFunction (unsigned long table_size);
	
	inline unsigned long HashFunction (HHPixel const & hhp) const
		{ return red_hash_values [hhp.r & 255] ^ green_hash_values [hhp.g & 255] ^ blue_hash_values [hhp.b & 255]; }
		
	void Resize (unsigned long new_table_size);
};

class HHHashHistogram : private HHHashFunction
{
public:
	// save - returns non-zero on success
	int Save (char const * fname) const;
	// load
	HHHashHistogram (char const * fname);
	// new
	HHHashHistogram ();
	// delete
	~HHHashHistogram ();

	// copy
	HHHashHistogram (HHHashHistogram const &);
	// construct new histogram with shades for lighting
	HHHashHistogram (HHHashHistogram const &, const struct Lighting_Style *);
	// construct new histogram, passing all entries through a function
	HHHashHistogram (HHHashHistogram const &, HHPixel (*pix_manip_func)(HHPixel const &) );
	// copy over
	HHHashHistogram & operator = (HHHashHistogram const &);
	
	// add pixel
	HHHashHistogram & operator += (HHPixel const &);
	// add histogram
	HHHashHistogram & operator += (HHHashHistogram const &);
	// add histogram from file
	HHHashHistogram & operator += (char const * fname);
	
	// begin reading data; returns number of pixels that can be read
	inline unsigned long BeginRead ()
		{
			r_nodeP = entries;
			return r_num_entries_remaining = number_of_entries;
		}
	// read the current pixel
	inline HHPixel const & ReadPixel () const
		{ return *r_nodeP; }
	// read the current pixel and move to the next
	inline HHPixel const & ReadPixelAndAdvance ()
		{
			HHPixel const & rv = *r_nodeP;
			AdvanceReader ();
			return rv;
		}
	// move to the next pixel
	inline int AdvanceReader ()
		{
			if (-- r_num_entries_remaining <= 0) return 0;
			++r_nodeP;
			return 1;
		}
	// finished?
	inline int IsReadDone () const
		{
			return r_num_entries_remaining <= 0;
		}
	// finish reading data
	inline void EndRead ()
		{
			r_num_entries_remaining = 0;
			r_nodeP = 0;
		}
	// note: operators += will fail if reading is in progress
	
	inline unsigned long NumEntries () const
		{ return number_of_entries; }
		
	// note: these two functions may be removed if the implemetation changes
	inline HHPixel const & GetNthEntry(unsigned long n) const
		{ return entries[n]; }
		
	inline unsigned long GetEntryIndex(HHPixel const & hhp) const
		{
			HHNode * * nodePP = table [ HashFunction (hhp) ];
			HHNode * nodeP;
			while (0!= (nodeP = *nodePP))
			{
				if (hhp != *nodeP)
					nodePP = &nodeP->next;
				else
				{
					return nodeP - entries;
				}
			}
			// not found
			return (unsigned long)-1;
		}

private:
	class HHNode : public HHPixel
	{
	public:
		HHNode * next;
	};
	
	class HHTableEntry
	{
	private:
		HHNode * chain;
	public:
		HHTableEntry ()
			: chain (0)
			{}
		inline operator HHNode * () const
			{ return chain; }
		inline operator HHNode * * ()
			{ return &chain; }
	};
		
	void Expand (unsigned long minimum_size = 0);
	
	HHTableEntry * table;
	unsigned long table_size;
	unsigned long number_of_entries; // number of uniqie pixel colours
	HHNode * entries;
	unsigned long max_number_of_entries; // number which when reached we will expand table
	
	// for reading
	HHNode const * r_nodeP;
	unsigned long r_num_entries_remaining;
};

class HHFileReader
{
private:
	FILE * fp;
	unsigned long version;
	unsigned long num_entries;
	HHPixel hhp;

public:
	HHFileReader (char const * fname);
	~HHFileReader ();

	inline HHPixel const & GetNextEntry ()
	{
		fread (&hhp.n,4,1,fp);
		fread (&hhp.r,1,1,fp);
		fread (&hhp.g,1,1,fp);
		fread (&hhp.b,1,1,fp);
		unsigned char reservedchar;
		fread (&reservedchar,1,1,fp);
		
		-- num_entries;
		
		return hhp;
	}
	inline unsigned long Size () const
	{
		return num_entries;
	}
	inline int Done () const
	{
		return !num_entries;
	}
	inline int operator ! () const
	{
		return !fp;
	}
};

#endif // !_included_histgram_hpp_
