#pragma once

#include "memstrm.h"
#include "node.h"
#include "hash.h"
#include "tree.h"
#include "cstr.h"

class NotationFile;
class NameList;
class CString;
class ResourceFile;
class ResourceDescription;

//##########################################################################
//#########################    ObjectStream    #############################
//##########################################################################

class ObjectStream:
	public DynamicMemoryStream
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction and testing
//
public:
	ObjectStream();
	ObjectStream(
		void *stream_start,
		size_t stream_size
	);
	ObjectStream(ResourceDescription *resource_description);
	~ObjectStream();

	Logical
  	TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Object creation
//
public:
  virtual void
  	CreateObjects();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notation file parsing
//
public:
	virtual void
		BuildFromNotationFile(
			NotationFile *notation_file,
			ResourceFile *resource_file
		);

	ResourceFile*
		GetResourceFile()
     	{return resourceFile;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Object ID generation
//
protected:
	static ObjectID
  	MakeUniqueObjectID();	

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Implementations
//
protected:
	virtual RegisteredClass*
  	MakeObjectImplementation(Enumeration class_ID);

	virtual void
		CreatedObjectImplementation(
			RegisteredClass *object,
        ObjectID object_ID
     );

	virtual void
  	BuildFromPageImplementation(
     	NameList *name_list,
        Enumeration class_ID,
        ObjectID object_ID
     );

	virtual void
  	BuiltFromPageImplementation(
     	Enumeration class_ID,
        ObjectID object_ID,
        const CString &object_name_string
     );

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private methods
//
private:
	void
  	ParseNotationFile(NotationFile *notation_file);
	void
		ReadIncludedFiles(NameList *entry_list);
	void
		ReadMacros(NameList *entry_list);

	void
		PerformMacroReplacement(NameList *entry_list);
	CString
		DeriveMacroValue(const CString &token_string);
	void
		CleanupMacroReplacement(NameList *entry_list);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	typedef PlugOf<CString>
		MacroValue;
	typedef TreeOf<MacroValue*, CString>
		MacroSocket;
	typedef TreeIteratorOf<MacroValue*, CString>
		MacroIterator;

	static ObjectID
		lastObjectID;
	ResourceFile
		*resourceFile;
	MacroSocket
  	*macroSocket;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ObjectIDPlug ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

typedef PlugOf<ObjectID>
	ObjectIDPlug;

//##########################################################################
//######################    PlugStreamManager    ###########################
//##########################################################################

class PlugStreamManager:
public Node
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Plug stream types
//
public:
	typedef HashOf<Plug*, ObjectID>
		PlugIndex;

	typedef TreeOf<ObjectIDPlug*, CString>
		ObjectIDIndex;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction and testing
//
public:
PlugStreamManager();
  ~PlugStreamManager();

	Logical
  	TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Program termination
//
public:
void
		Cleanup();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Manager Accessors
//
public:
	static void
		AddPlug(
			Plug *plug,
			ObjectID object_ID
		);

	static Plug*
		FindPlug(ObjectID object_ID);

	static void
		AddPlugName(
			ObjectID object_ID,
			const CString &object_name_string
		);

	static ObjectID
		FindPlugName(const CString &object_name_string);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	PlugIndex
		plugIndex;
	ObjectIDIndex
		objectIDIndex;

	static PlugStreamManager
  	plugStreamManager;
};

//##########################################################################
//##########################    PlugStream    ##############################
//##########################################################################

class PlugStream:
public ObjectStream
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Plug stream types
//
public:
	typedef VChainOf<Plug*, ObjectID>
     LocalPlugIndex;

	typedef VChainOf<ObjectIDPlug*, CString>
     LocalObjectIDIndex;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction and testing
//
public:
	PlugStream();
	PlugStream(
		void *stream_start,
		size_t stream_size
	);
	PlugStream(ResourceDescription *resource_description);
	~PlugStream();

  Logical
		TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Object creation and utilities
//
public:
  void
  	CreateObjects();

	void
  	AddLocalPlug(
     	Plug *plug,
        ObjectID object_ID
     );

	void
  	AddGlobalPlug(
     	Plug *plug,
        ObjectID object_ID
     );

	Plug*
  	FindPlug(ObjectID object_ID);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notation file parsing
//
public:
	void
		BuildFromNotationFile(
			NotationFile *notation_file,
			ResourceFile *resource_file
		);

	void
  	AddLocalObjectID(
     	ObjectID object_ID,
			const CString &object_name_string
     );

	void
		AddGlobalObjectID(
     	ObjectID object_ID,
        const CString &object_name_string
     );

	ObjectID
  	FindObjectID(const CString &object_name_string);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Implementations
//
protected:
	void
     CreatedObjectImplementation(
     	RegisteredClass *object,
        ObjectID object_ID
		);

	void
  	BuiltFromPageImplementation(
			Enumeration class_ID,
        ObjectID object_ID,
			const CString &object_name_string
     );

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	LocalPlugIndex
		*localPlugIndex;
	LocalObjectIDIndex
  	*localObjectIDIndex;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PlugStream functions ~~~~~~~~~~~~~~~~~~~~~~~~

template <class T> void
PlugStream_ReadObjectIDAndFindPlug(
  	PlugStream *stream,
		T** type_pointer
  )
{
Check(stream);
  Check_Pointer(type_pointer);

  ObjectID object_ID;
  Plug *plug;

	MemoryStream_Read(stream, &object_ID);
	plug = stream->FindPlug(object_ID);
	*type_pointer = Cast_Object(T*, plug);
}

void
PlugStream_FindEntryAndWriteObjectID(
		PlugStream *stream,
     NameList *name_list,
     const CString &entry_name
  );
