#include "AdeptHeaders.hpp"

#include "VideoHeaders.hpp"
#include "LightComponent.hpp"
#include "Tool.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Component::ClassData*
	VideoRenderer::CreateFactoryRequest(
		const char* component_type,
		Component::FactoryRequestParameters *parameters,
		Entity::ClassData *class_data
	)
{
	Check_Pointer(component_type);
	Check_Object(parameters);

	MemoryStream *component_stream = parameters->m_stream;
	Check_Object(component_stream);
	Channel::FactoryRequestParameters *channel_parameters =
		Cast_Pointer(Channel::FactoryRequestParameters*,parameters);

	//
	//---------------------------------------------
	// Check for any video specific component types
	//---------------------------------------------
	//
	if (!_stricmp(component_type, "ShapeComponent"))
	{
		*component_stream << static_cast<int>(ShapeComponentClassID);
		return ShapeComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type, "CameraComponent"))
	{
		*component_stream << static_cast<int>(CameraComponentClassID);
		return CameraComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type, "SwitchComponent"))
	{
		*component_stream << static_cast<int>(SwitchComponentClassID);
		return SwitchComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type, "LODComponent"))
	{
		*component_stream << static_cast<int>(LODComponentClassID);
		return LODComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type, "ScalableShapeComponent"))
	{
		*component_stream << static_cast<int>(ScalableShapeComponentClassID);
		return ScalableShapeComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type,"gosFXComponent"))
	{
		*component_stream << static_cast<int>(gosFXComponentClassID);
		return gosFXComponent::CreateFactoryRequest(channel_parameters, class_data);
	}

	if (!_stricmp(component_type,"BeamComponent"))
	{
		*component_stream << static_cast<int>(BeamComponentClassID);
		return BeamComponent::CreateFactoryRequest(channel_parameters, class_data);
	}

	if (!_stricmp(component_type,"SlidingShapeComponent"))
	{
		*component_stream << static_cast<int>(SlidingShapeComponentClassID);
		return SlidingShapeComponent::CreateFactoryRequest(channel_parameters, class_data);
	}

	if (!_stricmp(component_type,"MultiLODComponent"))
	{
		*component_stream << static_cast<int>(MultiLODComponentClassID);
		return MultiLODComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type,"LightComponent"))
	{
		*component_stream << static_cast<int>(LightComponentClassID);
		return LightComponent::CreateFactoryRequest(channel_parameters, class_data);
	}

	if (!_stricmp(component_type,"ConeComponent"))
	{
		*component_stream << static_cast<int>(ConeComponentClassID);
		return ConeComponent::CreateFactoryRequest(channel_parameters);
	}

	if (!_stricmp(component_type, "GroupComponent"))
	{
		*component_stream << static_cast<int>(GroupComponentClassID);
		return GroupComponent::CreateFactoryRequest(channel_parameters);
	}

	//
	//--------------------------------------------
	// Have the base renderer encode the component
	//--------------------------------------------
	//
	return
		Renderer::CreateFactoryRequest(
			component_type,
			parameters,
			class_data
		);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VideoRenderer::CreateRendererData(
		ResourceID			*stream_resource,
		Entity::ClassData	*class_data,
		NotationFile		*renderer_file
	)
{
	Check_Object(stream_resource);
	Check_Object(class_data);
	Check_Object(renderer_file);

	Check_Object(ResourceManager::Instance);

	Resource video_res(renderer_file->GetFileName());
	if (!video_res.DoesResourceExist() || !video_res.IsResourceUpToDate())
	{
		//
		//----------------------------------------------------------------
		// Now start building the stream by first putting in the number of 
		// components
		//----------------------------------------------------------------
		//
		const char* child_name;
		NotationFile::PageIterator *component_names = renderer_file->MakePageIterator();
		Check_Object(component_names);
		int page_count =
			component_names->GetSize() - renderer_file->DoesPageExist("Locator");
		Channel::FactoryRequestParameters parameters;

		DynamicMemoryStream component_stream;
		component_stream << page_count;
		parameters.m_stream = &component_stream;

		parameters.m_index = 0;
		parameters.m_commandEncoder = CommandEncoder;
		parameters.m_components = new DynamicArrayOf<ComponentDescriptor>(page_count);
		Check_Object(parameters.m_components);

		//
		//-------------------------------------------------------------------
		// Get the list of component names, and make sure we skip the locator
		// page
		//-------------------------------------------------------------------
		//
		while ((parameters.m_page = component_names->ReadAndNext()) != NULL)
		{
			Check_Object(parameters.m_page);

			if (!_stricmp(parameters.m_page->GetName(), "Locator"))
				continue;

			//
			//------------------------------
			// Get the type of the component
			//------------------------------
			//
			const char* component_type;
			parameters.m_page->GetEntry("Type", &component_type, true);

			//
			//------------------------------------------------------------
			// Store where the stream currently is, and set a dummy length
			//------------------------------------------------------------
			//
			int record_start = component_stream.GetIndex();
			component_stream << 0;

			//
			//---------------------------
			// Call the component creator
			//---------------------------
			//
			Component::ClassData *derivation =
				CreateFactoryRequest(
					component_type,
					&parameters,
					class_data
				);
			int end = component_stream.GetIndex();
			component_stream.SetPointer(record_start);
			component_stream << (end - record_start);
			component_stream.SetPointer(end);
			Check_Object(derivation);

			ComponentDescriptor *descriptor = &(*parameters.m_components)[parameters.m_index++];
			Check_Object(descriptor);
			descriptor->m_name = parameters.m_page->GetName();
			descriptor->m_classData = derivation;
		}
		Verify(parameters.m_index == page_count);

		//
		//------------------------------------------------------
		// Now write out the child indices from the locator page
		//------------------------------------------------------
		//
		Page *locator_page = renderer_file->FindPage("Locator");
		int child_id = -1;
		if (locator_page)
		{
			ChainOf<Note*> *child_chain = locator_page->MakeNoteChain("Child");
			Check_Object(child_chain);
			Page::NoteIterator children(child_chain);
			Note *child;
			while ((child = children.ReadAndNext()) != NULL)
			{
				Check_Object(child);
				child->GetEntry(&child_name);
				Check_Pointer(child_name);
				child_id = ComponentDescriptor::FindName(parameters.m_components, parameters.m_index, child_name);
				if (child_id == -1)
				{
					STOP((
						"%s: {[Locator]Child=%s}: Unknown component name!",
						renderer_file->GetFileName(),
						child_name
					));
				}
				else
				{
					Component::ClassData* class_data = (*parameters.m_components)[child_id].m_classData;
					if (!class_data->IsDerivedFrom(VideoComponent::DefaultData))
					{
						STOP((
							"%s: {[Locator]Child=%s}: Child is not an VideoComponent component!",
							renderer_file->GetFileName(),
							child_name
						));
					}
				}
				component_stream << child_id;
				child_id = -1;
			}
			Check_Object(child_chain);
			delete child_chain;
		}

		//
		//---------------------------------------------------------------
		// Make sure to always write the null ID to stop the child stream
		//---------------------------------------------------------------
		//
		component_stream << child_id;

		//
		//-------------------------
		// Release allocated memory
		//-------------------------
		//
		Check_Object(parameters.m_components);
		delete parameters.m_components;
		Check_Object(component_names);
		delete component_names;

		//
		//---------------------------------------
		// Store the damage zone in the resources
		//---------------------------------------
		//
		Check_Object(Resource::ParentFileDependencies);
		video_res.Save(&component_stream, Resource::ParentFileDependencies);
	}
	else if (!video_res.IsRegistered())
		video_res.AddFileDependenciesTo(Resource::ParentFileDependencies);
	*stream_resource = video_res.GetResourceID();
	Check_Object(Resource::ParentFileDependencies);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	VideoRenderer::CommandEncoder(const char* command_name)
{
	if (!_stricmp(command_name, "EnableLocator"))
	{
		return EnableLocatorCommandID;
	}
	else if (!_stricmp(command_name, "DisableLocator"))
	{
		return DisableLocatorCommandID;
	}
	else
	{
		return Renderer::CommandEncoder(command_name);
	}
}
