#pragma once

#include "resource.h"
#include "chain.h"
#include "node.h"
#include "plug.h"
#include "graph2d.h"

//#######################################################################
// WarehouseObject
//#######################################################################
class WarehouseObject :
	public Plug
{
	friend class WarehouseBin;
public:
	WarehouseObject(
		const char *new_name,
		Logical keep_forever = False
	);

	WarehouseObject(
		ResourceDescription::ResourceID new_resource_id,
		Logical keep_forever = False
	);

	~WarehouseObject();

	Logical
		TestInstance() const;

	void
		SetKeepForever(Logical keep_forever)
			{
				Check(this);
				keepForever = keep_forever;
			}

	Logical
		GetKeepForever()
			{
				Check(this);
				return keepForever;
			}

	void
		Reference()
			{
				Check(this);
				++referenceCount;
			}

	Logical
		Unreference()				// returns True if no longer referenced
			{
				Check(this);
				return (Logical) ((--referenceCount) <= 0);
			}


protected:
	char
		name[32];
	ResourceDescription::ResourceID
		resourceID;
	int
		referenceCount;
	Logical
		keepForever;
};

//-----------------------------------------------------------------------
// WarehouseObjectOf
//-----------------------------------------------------------------------
template <class T> class WarehouseObjectOf:
	public WarehouseObject
{
public:
	WarehouseObjectOf(
		const char *new_name,
		T *new_pointer,
		Logical keep_forever = False
	);

	WarehouseObjectOf(
		ResourceDescription::ResourceID new_resource_id,
		T *new_pointer,
		Logical keep_forever = False
	);

	virtual ~WarehouseObjectOf();

	Logical
		TestInstance() const;

	T
		*dataPointer;
};

template <class T>
	WarehouseObjectOf<T>::WarehouseObjectOf(
		const char *new_name,
		T *new_pointer,
		Logical keep_forever
	):
		WarehouseObject(new_name, keep_forever)
			{
				Register_Pointer(new_pointer);
				dataPointer = new_pointer;
			}

template <class T>
	WarehouseObjectOf<T>::WarehouseObjectOf(
		ResourceDescription::ResourceID new_resource_id,
		T *new_pointer,
		Logical keep_forever
	):
		WarehouseObject(new_resource_id, keep_forever)
			{
				Register_Pointer(new_pointer);
				dataPointer = new_pointer;
			}                            

template <class T> WarehouseObjectOf<T>::~WarehouseObjectOf()
	{
		Check(dataPointer);
		Unregister_Pointer(dataPointer);
		delete dataPointer;
		dataPointer = NULL;		// safety check
	}

template <class T> Logical
	WarehouseObjectOf<T>::TestInstance() const
		{
			Check_Pointer(this);
			Check_Pointer(dataPointer);
			return WarehouseObject::TestInstance();
		}

//#######################################################################
// WarehouseBin
//#######################################################################
class WarehouseBin :
	public Node
{
public:
	WarehouseBin();
	~WarehouseBin();
	Logical
		TestInstance() const;
	void
		Add(WarehouseObject *new_object)
			{
				Check(this);
				Check_Pointer(new_object);
				instanceList.Add(new_object);
			}

	void
		Remove(void *old_object);
	WarehouseObject *
		Find(const char *name);
	WarehouseObject *
		Find(ResourceDescription::ResourceID new_resource_id);

protected:
	ChainOf<WarehouseObject*>
		instanceList;
};


template <class T> class WarehouseBinOf :
	public WarehouseBin
{
	friend class WarehouseBin;
public:
	WarehouseBinOf();
	~WarehouseBinOf();

	Logical
		TestInstance() const;

	T*
		Get(
			const char *name,
			Logical keep_forever = False
		);

	T*
		Get(
			ResourceDescription::ResourceID new_resource_id,
			Logical keep_forever = False
		);

	T*
		GetIfAlreadyExists(
			const char *name
		);

	T*
		GetIfAlreadyExists(
			ResourceDescription::ResourceID new_resource_id
		);

	void
		Release(
			const char *name
		);
	void
		Release(
			ResourceDescription::ResourceID new_resource_id
		);

	void
		Purge();
};

//--------------------------------------------------------------------
template <class T> Logical
	WarehouseBinOf<T>::TestInstance() const
{
	Check_Pointer(this);
	return WarehouseBin::TestInstance();
}

//--------------------------------------------------------------------
template <class T>
	WarehouseBinOf<T>::WarehouseBinOf()
	: WarehouseBin()
{
}

//--------------------------------------------------------------------
template <class T> WarehouseBinOf<T>::~WarehouseBinOf()
{
	Check(this);

	Purge();		// remove EVERYTHING unconditionally
	Check_Fpu();
}

//--------------------------------------------------------------------
template <class T> T*
	WarehouseBinOf<T>::Get(
		const char *name,
		Logical keep_forever
	)
{
	Check(this);

	WarehouseObjectOf<T>
		*instance =
			(WarehouseObjectOf<T>*) WarehouseBin::Find(name);

	if (instance != NULL)
	{
		instance->Reference();
		return instance->dataPointer;
	}
	else
	{
		T *new_object = T::Make(name);

		if (new_object != NULL)
		{
			Check(new_object);
			// new_object is registered in the warehouseObject creator
			instance = new WarehouseObjectOf<T>(
				name,
				new_object,
				keep_forever
			);
			Register_Object(instance);
			WarehouseBin::Add(instance);
			return new_object;
		}
	}
	return NULL;
}

template <class T> T*
	WarehouseBinOf<T>::Get(
		ResourceDescription::ResourceID new_resource_id,
		Logical keep_forever
	)
{
	Check(this);

	WarehouseObjectOf<T>
		*instance =
			(WarehouseObjectOf<T>*) WarehouseBin::Find(new_resource_id);

	if (instance != NULL)
	{
		instance->Reference();
		return instance->dataPointer;
	}
	else
	{
		T *new_object = T::Make(new_resource_id);

		if (new_object != NULL)
		{
			Check(new_object);
			// new_object is registered in the warehouseObject creator
			instance = new WarehouseObjectOf<T>(
				new_resource_id,
				new_object,
				keep_forever
			);
			Register_Object(instance);
			WarehouseBin::Add(instance);
			return new_object;
		}
	}
	return NULL;
}

//--------------------------------------------------------------------
template <class T> T*
	WarehouseBinOf<T>::GetIfAlreadyExists(
		const char *name
	)
{
	Check(this);
	Check_Pointer(name);

	WarehouseObjectOf<T>
		*warehouse_object = (WarehouseObjectOf<T> *) WarehouseBin::Find(name);
	if (warehouse_object != NULL)
	{
		Check(warehouse_object);
		warehouse_object->Reference();

		Check(warehouse_object->dataPointer);
		return warehouse_object->dataPointer;
	}
	return NULL;
}

template <class T> T*
	WarehouseBinOf<T>::GetIfAlreadyExists(
		ResourceDescription::ResourceID id
	)
{
	Check(this);

	WarehouseObjectOf<T>
		*warehouse_object = (WarehouseObjectOf<T>*) WarehouseBin::Find(id);
	if (warehouse_object != NULL)
	{
		Check(warehouse_object);
		warehouse_object->Reference();

		Check(warehouse_object->dataPointer);
		return warehouse_object->dataPointer;
	}
	return NULL;
}

//--------------------------------------------------------------------
template <class T> void
	WarehouseBinOf<T>::Release(
		const char *name
	)
{
	Check(this);
	Check_Pointer(name);

	WarehouseObjectOf<T>
		*warehouse_object = (WarehouseObjectOf<T> *) WarehouseBin::Find(name);
	if (warehouse_object != NULL)
	{
		Check(warehouse_object);
		if (warehouse_object->Unreference())
		{
			if (warehouse_object->GetKeepForever() == False)
			{
				Unregister_Object(warehouse_object);
				// the dataPointer is deregistered & deleted in the destructor
				delete warehouse_object;
			}
		}
	}
}

template <class T> void
	WarehouseBinOf<T>::Release(
		ResourceDescription::ResourceID id
	)
{
	Check(this);

	WarehouseObjectOf<T>
		*warehouse_object = (WarehouseObjectOf<T> *) WarehouseBin::Find(id);
	if (warehouse_object != NULL)
	{
		Check(warehouse_object);
		if (warehouse_object->Unreference())
		{
			if (warehouse_object->GetKeepForever() == False)
			{
				Unregister_Object(warehouse_object);
				// the dataPointer is deregistered & deleted in the destructor
				delete warehouse_object;
			}
		}
	}
}

template <class T> void
	WarehouseBinOf<T>::Purge()
{
	Check(this);
	//------------------------------------------
	// Delete everything in the bin
	//------------------------------------------
	ChainIteratorOf<WarehouseObject*>
		i(instanceList);

	WarehouseObject
		*object_instance;

	while ((object_instance=i.ReadAndNext()) != NULL)
	{
		Check(object_instance);
		Unregister_Object(object_instance);
		delete object_instance;
	}
	Check_Fpu();
}

//#######################################################################
// Warehouse
//#######################################################################
class Warehouse SIGNATURED
{
public:
	Warehouse();
	virtual ~Warehouse();
	Logical
		TestInstance() const;
	virtual void
		Purge();

	WarehouseBinOf<BitMap>
		bitMapBin;
	WarehouseBinOf<PixelMap8>
		pixelMap8Bin;
	WarehouseBinOf<Palette8>
		palette8Bin;
};
