#include "AdeptHeaders.hpp"

#include "GUIObject.hpp"
#include <ElementRenderer\StateChange.hpp>
#include <ElementRenderer\CameraElement.hpp>
#include "Tool.hpp"
#include <MLR\MLRTexture.hpp>
#include <MLR\MLRTexturePool.hpp>

using MidLevelRenderer::MLRTexturePool;
using MidLevelRenderer::MLRTexture;

//#############################################################################
//###########################    ScreenQuadObject   ###########################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ScreenQuadObject::ScreenQuadObject(int size) :
	Plug(Plug::DefaultData),
	availableQuadStack(size, size + 20, "Available Quad Stack"),
	quadElement(NULL)
{
	Verify(size > 0);
	ElementRenderer::ScreenQuadsElement *quad_element;

	gos_PushCurrentHeap(ElementRenderer::ScreenQuadsElement::s_Heap);
	quad_element = new ElementRenderer::ScreenQuadsElement(size);
	Check_Object(quad_element);
	gos_PopCurrentHeap();

	quadElement.Add(quad_element);
	int i;
	for(i=size-1; i>=0; i--)
	{
		QuadIndexObject empty_object(i);
		availableQuadStack.Push(&empty_object);
#if 0
		QuadIndexObject *index = new (availableQuadStack.Peek())QuadIndexObject(i);
		SPEW(("daberger", "BERGER!!! %s(%d) leaks memory!", __FILE__, __LINE__-1));
		Check_Object(index);
		availableQuadStack.Push(index);
#endif
		
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ScreenQuadObject::~ScreenQuadObject()
{	
	ElementRenderer::ScreenQuadsElement *quad_element;
	quad_element = quadElement.GetCurrent();
	if(quad_element)
	{
		Check_Object(quad_element);
		delete quad_element;
	}

	QuadIndexObject *index;
	while((index = availableQuadStack.Peek()) != NULL)
	{
		Check_Object(index);
		Check_Object(index);
		index->QuadIndexObject::~QuadIndexObject();
		availableQuadStack.Pop();
	}
}

//#############################################################################
//###########################    GUIObject   ##################################
//#############################################################################

GUIObject::ClassData*
	GUIObject::DefaultData = NULL;

ChainOf<ScreenQuadObject *>
	*GUIObject::ScreenQuadGroups = NULL;

ElementRenderer::CameraElement
	*GUIObject::Camera = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			GUIObjectClassID,
			"Adept::GUIObject",
			Receiver::DefaultData,
			0,
			NULL
		);
	Check_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GUIObject::GUIObject(ClassData *class_data):
	Receiver(class_data)
{
	Check_Pointer(this);
	quadState = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GUIObject::GUIObject(
	ClassData *class_data,
	MemoryStream *stream
):
	Receiver(class_data)
{
	Check_Pointer(this);
	Check_Object(stream);

	*stream >> guiObjectName;
	quadState = new ElementRenderer::StateChange(
		stream,
		ElementRenderer::CurrentERFVersion
	);

	Check_Object(quadState);
	Check_Object(quadState);

	//
	//-------------------------------------------------------------------------
	//Using the State Change Check to see if there is alread a ScreenQuadObject
	//-------------------------------------------------------------------------
	//
	ChainIteratorOf<ScreenQuadObject *> iterator(ScreenQuadGroups);
	ScreenQuadObject *quad_group;
	bool found = false;
	while(((quad_group = iterator.ReadAndNext()) != NULL)
		&& (!found))
	{
		Check_Object(quad_group);
		found = quad_group->quadElement.GetCurrent()->GetStateChange() == quadState;
	}
	if(!found)
	{
		//------------------------------------
		//Create and Initialize new ScreedQuad
		//------------------------------------
		quad_group = new ScreenQuadObject(10);
		Check_Object(quad_group);
		ScreenQuadGroups->Add(quad_group);
		quad_group->quadElement.GetCurrent()->AdoptStateChange(quadState);
	//	quad_group->quadElement.GetCurrent()->SetName(state_change_name);
	}

	//
	//--------------------------------------------
	//Get the next available index for a new quad!
	//--------------------------------------------
	//
	QuadIndexObject *new_quad_index = quad_group->availableQuadStack.Peek();
	quad_group->availableQuadStack.Pop();

	quad_group->quadElement.GetCurrent()->GetQuadData(
		new_quad_index->quadIndex, 
		&polyCorners, 
		&polyColors,
		&polyUVs,
		&polyStatus
	);
	stream->ReadBytes(polyCorners, 4 * sizeof(Vector4D));
	stream->ReadBytes(polyColors, 4 * sizeof(RGBAColor));
	stream->ReadBytes(polyUVs, 4 * sizeof(Vector2DOf<Scalar>));
	*stream >> *polyStatus;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GUIObject::GUIObject(
	ClassData *class_data,
	Page *instance_page
):
	Receiver(class_data)
{
	Check_Pointer(this);
	Check_Object(instance_page);

	guiObjectName = instance_page->GetName();

	//
	//-----------------------------------------
	//Create the State Change for the GUIObject
	//-----------------------------------------
	//
	{
		NotationFile state_file;
		instance_page->GetEntry("ModelFile", &state_file, true);

		NotationFile::PageIterator *pages = state_file.MakePageIterator();
		Check_Object(pages);
		Page *page = pages->GetCurrent();
		Check_Object(page);
		MString state_change_name = page->GetName();

		quadState = new ElementRenderer::StateChange(page, *MString::s_Empty);
		Check_Object(quadState);

		//---------------------------
		//Temp Way to set the Texture
		//---------------------------
		const char *texture_name;
		page->GetEntry("Texture", &texture_name, true);
		Check_Object(MLRTexturePool::Instance);
		MLRTexture *quad_texture = (*MLRTexturePool::Instance)(texture_name, 0);
		if(!quad_texture)
		{
			quad_texture = MLRTexturePool::Instance->Add(texture_name, 0);
		}
		Check_Object(quad_texture);
		quadState->SetTextureHandle(quad_texture->GetTextureHandle());

		Check_Object(pages);
		delete pages;	
//		Tool::Instance->PopFilePath();

		//
		//-------------------------------------------------------------------------
		//Using the State Change Check to see if there is alread a ScreenQuadObject
		//-------------------------------------------------------------------------
		//
		ChainIteratorOf<ScreenQuadObject *> iterator(ScreenQuadGroups);
		ScreenQuadObject *quad_group;
		bool found = false;
		while(((quad_group = iterator.ReadAndNext()) != NULL)
			&& (!found))
		{
			Check_Object(quad_group);
			found = quad_group->quadElement.GetCurrent()->GetStateChange() == quadState;
		}
		if(!found)
		{
			//------------------------------------
			//Create and Initialize new ScreedQuad
			//------------------------------------
			quad_group = new ScreenQuadObject(10);
			Check_Object(quad_group);
			ScreenQuadGroups->Add(quad_group);
			quad_group->quadElement.GetCurrent()->AdoptStateChange(quadState);
		}

		//
		//--------------------------------------------
		//Get the next available index for a new quad!
		//--------------------------------------------
		//
		QuadIndexObject *new_quad_index = quad_group->availableQuadStack.Peek();
		quad_group->availableQuadStack.Pop();

		quad_group->quadElement.GetCurrent()->GetQuadData(
			new_quad_index->quadIndex, 
			&polyCorners, 
			&polyColors,
			&polyUVs,
			&polyStatus
		);
	}
	//
	//---------------------------------------------------------------------
	//Read in the Location for the Object (Top Left corner in Screen Space)
	//---------------------------------------------------------------------
	//
	Vector4D v[4];
	//----------------------------
	//Top Left corner X coordinate
	//----------------------------
	int screen_coord;
	instance_page->GetEntry("XLocation", &screen_coord, true);
	if((screen_coord < 0) || (screen_coord > Environment.screenWidth))
	{
		STOP(("%s :All X Coordinates must be > 0  and < %d", (const char*)guiObjectName, Environment.screenWidth));
	}
	else
	{
		v[0].x = (screen_coord / 640.0f);
		v[1].x = v[0].x;
	}
	//----------------------------
	//Top Left Corner Y coordinate
	//----------------------------
	instance_page->GetEntry("YLocation", &screen_coord, true);
	if((screen_coord < 0) || (screen_coord > Environment.screenHeight))
	{
		STOP(("%s :All Y Coordinates must be > 0  and < %d", (const char*)guiObjectName, Environment.screenHeight));
	}
	else
	{
		v[0].y = (screen_coord / 480.0f);
		v[3].y = v[0].y;
	}
	//--------------
	//Width of Image
	//--------------
	instance_page->GetEntry("Width", &screen_coord, true);
	if(screen_coord < 0) 
	{
		STOP(("%s :Width must be > 0", (const char*)guiObjectName));
	}
	else
	{
		
		v[2].x = v[0].x + ((float)screen_coord / Environment.screenWidth);

		if(v[2].x > 1.0f)
		{
			STOP(("%s :This Width Extends the Screen Width", (const char*)guiObjectName));
		}
		v[3].x = v[2].x;
	}
	//---------------
	//Height of Image
	//---------------
	instance_page->GetEntry("Height", &screen_coord, true);
	if(screen_coord < 0) 
	{
		STOP(("%s :Height must be > 0", (const char*)guiObjectName));
	}
	else
	{
		v[1].y = v[0].y + ((float)screen_coord / Environment.screenHeight);

		if(v[1].y > 1.0f)
		{
			STOP(("%s :This Height Extends the Screen Height", (const char*)guiObjectName));
		}
		v[2].y = v[1].y;
	}
	v[0].z = 0.0f;
	v[0].w = 1.0f;
	v[1].z = 0.0f;
	v[1].w = 1.0f;
	v[2].z = 0.0f;
	v[2].w = 1.0f;
	v[3].z = 0.0f;
	v[3].w = 1.0f;
	//------------------------------------
	//Copy Vertices into ScreenQuadElement
	//------------------------------------
	Mem_Copy(
		polyCorners,
		v,
		4 * sizeof(Vector4D),
		4 * sizeof(Vector4D)
	);

	//----------------------
	//Read in UV Coordinates
	//----------------------
	Vector2DOf<Scalar> uv[4];
	Scalar texture_coord = 0.0f;
	instance_page->GetEntry("UVX0", &texture_coord);
	if((texture_coord < 0.0f) || (texture_coord > 1.0f))
	{
		STOP(("%s :UVs must be between 0 and 1", (const char*)guiObjectName));
	}
	uv[0].x = texture_coord;
	uv[1].x = texture_coord;

	texture_coord = 0.0f;
	instance_page->GetEntry("UVY0", &texture_coord);
	if((texture_coord < 0.0f) || (texture_coord > 1.0f))
	{
		STOP(("%s :UVs must be between 0 and 1", (const char*)guiObjectName));
	}
	uv[0].y = texture_coord;
	uv[3].y = texture_coord;

	texture_coord = 1.0f;
	instance_page->GetEntry("UVX1", &texture_coord);
	if((texture_coord < 0.0f) || (texture_coord > 1.0f))
	{
		STOP(("%s :UVs must be between 0 and 1", (const char*)guiObjectName));
	}
	uv[2].x = texture_coord;
	uv[3].x = texture_coord;

	texture_coord = 1.0f;
	instance_page->GetEntry("UVY1", &texture_coord);
	if((texture_coord < 0.0f) || (texture_coord > 1.0f))
	{
		STOP(("%s :UVs must be between 0 and 1", (const char*)guiObjectName));
	}
	uv[1].y = texture_coord;
	uv[2].y = texture_coord;

	//-------------------------------
	//Copy uvs into ScreenQuadElement
	//-------------------------------
	Mem_Copy(
		polyUVs,
		uv,
		4 * sizeof(Vector2DOf<Scalar>),
		4 * sizeof(Vector2DOf<Scalar>)
	);

	//----------------------------
	//Read in All RGBAColor Values
	//----------------------------
	RGBAColor color[4];
	RGBAColor vertex_color;
	instance_page->GetEntry("VertexColor", &vertex_color, true);

	color[0] = vertex_color;
	color[1] = vertex_color;
	color[2] = vertex_color;
	color[3] = vertex_color;

	//-----------------------------------------
	//Copy Vertex Colors into ScreenQuadElement
	//-----------------------------------------
	Mem_Copy(
		polyColors,
		color,
		4 * sizeof(RGBAColor),
		4 * sizeof(RGBAColor)
	);

	//------------------------
	//Read in Starting Display
	//------------------------
	bool starts_displayed = true;
	instance_page->GetEntry("StartsDisplayed", &starts_displayed);

	*polyStatus = starts_displayed;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::Save(MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);

	*stream << guiObjectName;
	quadState->Save(stream);
	stream->WriteBytes(polyCorners, 4 * sizeof(Vector4D));
	stream->WriteBytes(polyColors, 4 * sizeof(RGBAColor));
	stream->WriteBytes(polyUVs, 4 * sizeof(Vector2DOf<Scalar>));
	*stream << *polyStatus;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::HideImage()
{
	Check_Object(this);
	Check_Pointer(polyStatus);

	*polyStatus = false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::ShowImage()
{
	Check_Object(this);
	Check_Pointer(polyStatus);

	*polyStatus = true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GUIObject::~GUIObject()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::AdoptCamera(ElementRenderer::CameraElement *camera)
{
	Check_Pointer(camera);

	Camera = camera;

	ChainIteratorOf<ScreenQuadObject *> iterator(ScreenQuadGroups);
	ScreenQuadObject *quad_group;
	while((quad_group = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(quad_group);
		//-------------------------------
		//Attach screenquad to the camera
		//-------------------------------
		Camera->AttachChild(quad_group->quadElement.GetCurrent());
		quad_group->quadElement.GetCurrent()->Sync();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void
	GUIObject::RemoveCamera()
{
	Camera = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GUIObject::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}






