/****************************************************************************************************

		Modeler.cpp
		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Author:	Arnaud
		Date:	12/2/2004

****************************************************************************************************/

#include "Modeler.h"


#include "COM3DUtilities.h"
#include "I3DEditorHostPart.h"
#include "I3DEditorHostPartDefs.h"
#include "I3dShFacetMesh.h"
#include "I3DShGroup.h"
#include "I3DShModule.h"
#include "I3DShObject.h"
#include "IMFDocument.h"
#include "IMFPart.h"
#include "IMFResponder.h"
#include "IMFWindow.h"
#include "ISceneDocument.h"
#include "IShComponent.h"
#include "IWorkingBox.h"
#include "MiscComUtilsImpl.h"
#include "vector3.h"

//Modeler includes
#include "SimpleModelerDef.h"
#include "PanePart.h"
#include "Renderable.h"
#include "MouseActions.h"
#include "PropertiesClient.h"


TModeler::TModeler()
{
	fImmediateUpdate		= false;	
	fActionBeingProcessed	= 0;
}


TModeler::~TModeler()
{
}

TModelerPrefs* TModeler::GetModelerPrefs()
{
	TMCCountedPtr<I3DShModule>	me;
	QueryInterface(IID_I3DShModule, (void**) &me); ThrowIfNil(me);
	return (TModelerPrefs*) me->GetPrefs();
}

MCCOMErr TModeler::Initialize(IMCUnknown* inElement)
{
	// Initialize is going to be called twice : once with the Universe, once with the Master Object
	if(!inElement) return MC_E_INVALIDARG;

	// Check if we are called with the universe.
	TMCCountedPtr<I3DShGroup> universe;
	if(inElement->QueryInterface(IID_I3DShGroup, (void**)&universe) == MC_S_OK)
	{
		TBasicModule::Initialize(inElement);
		return MC_S_OK;
	}

	if(inElement->QueryInterface(IID_I3DShPrimitive, (void**)&fPrimitive) == MC_S_OK)
	{
		Initialize2();
		return MC_S_OK;
	}

	return MC_E_INVALIDARG;
}



MCCOMErr TModeler::Import(IMCUnknown* inElement, void* inImportData)
{
	ThrowIfNil(fFacetMeshPrimitive);
	TMCCountedPtr<I3DShPrimitive> srcPrimitive;
	inElement->QueryInterface(IID_I3DShPrimitive, (void**)&srcPrimitive);
	if (!srcPrimitive) return MC_S_OK;

	fFacetMeshPrimitive->SetNbrLOD(1);
	TMCCountedPtr<FacetMesh> newMesh;
	srcPrimitive->GetRenderingFacetMesh(&newMesh);
	fFacetMeshPrimitive->SetFacetMesh(0, 0.0f, newMesh);
	return MC_S_OK;
}

void TModeler::Initialize2()
{
	ThrowIfNil(fPrimitive);

	//Get the external facetmesh primitive
	TMCCountedPtr<I3DShExternalPrimitive> externalPrimitive;
	fPrimitive->QueryInterface(IID_I3DShExternalPrimitive, (void**)&externalPrimitive);
	ThrowIfNil(externalPrimitive);
	
	TMCCountedPtr<IShComponent> component;
	externalPrimitive->GetPrimitiveComponent(&component);
	ThrowIfNil(component);
	
	TMCCountedPtr<I3DShPrimitiveComponent> primitiveComponent;
	component->QueryInterface(IID_I3DShPrimitiveComponent, (void**)&primitiveComponent);
	ThrowIfNil(primitiveComponent);
	
	primitiveComponent->QueryInterface(IID_I3DShFacetMeshPrimitive, (void**)&fFacetMeshPrimitive);
	ThrowIfNil(fFacetMeshPrimitive);

	//Get the document from out module interface
	QueryInterface(IID_I3DShModule, (void**) &fModule);
	ThrowIfNil(fModule);

	fDocument= fModule->GetDocumentNoAddRef();
	ThrowIfNil(fDocument);

	fDocument->QueryInterface(IID_IMFResponder, (void**)&fActionsContext);
	ThrowIfNil(fActionsContext);
	

	TMCCountedPtr<ISceneDocument> sceneDoc;
	fDocument->QueryInterface(IID_ISceneDocument, (void**)&sceneDoc);
	ThrowIfNil(sceneDoc);

	TMCCountedPtr<I3DShScene> scene;
	sceneDoc->GetScene(&scene);
	ThrowIfNil(scene);


	//-- Register ourselves as listeners to channels	
	scene->GetTreePropertyChangeChannel(&fTreePropertyChangeChannel);
	ThrowIfNil(fTreePropertyChangeChannel);
	fTreePropertyChangeChannel->RegisterListener(this);

	gShell3DUtilities->GetImmediateUpdateChannel(&fImmediateUpdateChannel);
	ThrowIfNil(fImmediateUpdateChannel);
	fImmediateUpdateChannel->RegisterListener(this);

	//-- Properties
	TMCCountedPtr<I3DShModule> shPropModule;
	gShell3DUtilities->GetDocumentModule(&shPropModule, fDocument, kPropertiesModuleID);	ThrowIfNil(shPropModule);

	TMCCountedPtr<IPropertiesModule> propertiesModule;	
	shPropModule->QueryInterface(IID_IPropertiesModule, (void**) &propertiesModule);		ThrowIfNil(propertiesModule);
	TPropertiesClient::Create(propertiesModule, this, &fPropertiesClient);
}


MCCOMErr TModeler::Destroy()
{	
	fModule					= NULL;
	fDocument				= NULL;
	fActionsContext			= NULL;
	
	fPrimitive				= NULL;
	fPrimitiveRenderable	= NULL;

	if (fPropertiesClient)
	{
		fPropertiesClient.CheckLastRef();
		fPropertiesClient		= NULL;
	}
		

	if (fImmediateUpdateChannel)		fImmediateUpdateChannel->UnregisterListener(this);
	if (fTreePropertyChangeChannel)		fTreePropertyChangeChannel->UnregisterListener(this);

	fImmediateUpdateChannel		= NULL;
	fTreePropertyChangeChannel	= NULL;

	DestroyWindows();

	return TBasicModule::Destroy();
}


MCCOMErr TModeler::Activate()
{
	if (fWindow)
	{
		CreateWindows();
	}
	
	fWindow->SelectWindow();
	
	ThrowIfNil(fPropertiesClient);
	fPropertiesClient->Activate();
		
	return MC_S_OK;
}

MCCOMErr TModeler::Deactivate()
{
	ThrowIfNil(fPropertiesClient);
	fPropertiesClient->Deactivate();

	return MC_S_OK;
}



MCCOMErr TModeler::Hydrate()
{
	if (fWindow)
	{
		CreateWindows();	// It may happen that our window was closed during a dehydratation
	}	

	fWindow->Show(true, false);

	ThrowIfNil(fPropertiesClient);
	fPropertiesClient->Hydrate();

	return MC_S_OK;
}


MCCOMErr TModeler::Dehydrate()
{
	ThrowIfNil(fPropertiesClient);
	fPropertiesClient->Dehydrate();

	if (fWindow)
	{
		// Hide the window
		fWindow->Show(false, false);
	}

	return MC_S_OK;
}


MCCOMErr TModeler::AboutToCloseMainWindow()
{
	DestroyWindows();	// We need to loose our references, because the window is about to die
	return MC_S_OK;
}


void TModeler::SetWindowTitle()
{
	if (fWindow && fPrimitive)
	{
		TMCString31	objectKey("^0");
		TMCString31	docKey("^1");

		//Get the title pattern
		TMCString255 title;
		fWindow->GetConstTitle(title);
		
		TMCCountedPtr<I3DShObject> object;
		fPrimitive->QueryInterface(IID_I3DShObject, (void**) &object); ThrowIfNil(object);
		
		//Insert the name of the object in the title
		TMCString255 name;
		object->GetName(name);
		uint32 index = title.SearchStr(objectKey);
		if (index != kUndefinedIndex)
		{
			title.EraseStr(index, objectKey.Length());
			title.InsertStr(name, index);
		}
		
		//Insert the name of the document in the title
		TMCString255 docName;
		fDocument->GetTitle(docName);
		index = title.SearchStr(docKey);
		if (index != kUndefinedIndex)
		{
			title.EraseStr(index, docKey.Length());
			title.InsertStr(docName, index);
		}
		
		fWindow->SetTitle(title);
	}
}


void TModeler::CreateWindows()
{
	CWhileInCompResFile myResources(kRID_ModuleFamilyID, kModelerClassID);

	TMCCountedPtr<IMFPart> windowPart;
	fModule->CreateWindowByResource(&windowPart, kModelerWindowResID, true);
	ThrowIfNil(windowPart);

	windowPart->QueryInterface(IID_IMFWindow, (void**)&fWindow); ThrowIfNil(fWindow);

	IMFPart* contentPart= fWindow->GetContentPart(); ThrowIfNil(contentPart);
	SetWindowTitle();

	TMCCountedPtr<IMFPart> hostPart;
	contentPart->FindChildPartByID(&hostPart, 'host');
	ThrowIfNil(hostPart);

	hostPart->QueryInterface(IID_I3DEditorHostPart, (void **)&f3DEditorHostPart);
	ThrowIfNil(f3DEditorHostPart);

	TMCCountedPtr<IWorkingBox> workingBox;
	f3DEditorHostPart->GetWorkingBox(&workingBox);
	workingBox->SetCurrentPlane(0);

	TMCCountedPtr<IMFResponder> actionResponder;
	fModule->GetDocumentNoAddRef()->QueryInterface(IID_IMFResponder, (void**)&actionResponder);
	f3DEditorHostPart->SetActionResponder(actionResponder); //otherwise the indirect actions (track, camera rotation) don't work properly


	f3DEditorHostPart->SetPaneExtension(kPanePartClassID);
	
	//Set the modeler on the pane parts
	fPaneExtensions.SetElemCount(4);
	for (int32 paneIndex = 0; paneIndex < 4; paneIndex++)
	{
		TMCCountedPtr<IMFPart> part;
		contentPart->FindChildPartByID(&part, kBasePanePartID + paneIndex);
		ThrowIfNil(part);

		TMCCountedPtr<I3DEditorHostPanePart> editorHostPanePart;
		part->QueryInterface(IID_I3DEditorHostPanePart, (void**) &editorHostPanePart);
		ThrowIfNil(editorHostPanePart);

		TMCCountedPtr<IEx3DEditorHostPanePart> exEditorHostPanePart;
		editorHostPanePart->GetPaneExtension(&exEditorHostPanePart);
		ThrowIfNil(exEditorHostPanePart);

		TMCCountedPtr<TPanePart> panePart;
		exEditorHostPanePart->QueryInterface(TPanePart::sClassID, (void**)&panePart);
		ThrowIfNil(panePart);
		fPaneExtensions.SetElem(paneIndex, panePart);

		panePart->SetModeler(this);
	}
}


void TModeler::DestroyWindows()
{
	if (fWindow)
	{
		fWindow->TryToClose();
	}

	fWindow				= NULL;
	f3DEditorHostPart	= NULL;
	fPaneExtensions.ArrayFree();
}


void TModeler::AddPrimitiveRenderables(TRenderableAndTfmArray& outRenderables)
{
	TRenderableAndTfm	itPrimitive;

	if (fPrimitiveRenderable)
	{
		TRenderable::Create(&fPrimitiveRenderable, this, I3DShRenderable::kType_Facet);
	}
	
	itPrimitive.fInstance = NULL;
	itPrimitive.fRenderable = static_cast<I3DShRenderable*>(fPrimitiveRenderable);
	itPrimitive.fT.fRotationAndScale.Reset();
	itPrimitive.fT.fTranslation = TVector3::kZero;
	itPrimitive.fModelFromWorldTfm.fRotationAndScale.Reset();
	itPrimitive.fModelFromWorldTfm.fTranslation = TVector3::kZero;
	
	outRenderables.AddElem(itPrimitive);
}


void TModeler::GetCurrentFacetMesh(real lod, FacetMesh** outMesh)
{
	fPrimitive->GetRenderingFacetMesh(outMesh); //edit the best mesh only
}

void TModeler::GetCurrentBoundingBox(TBBox3D& bb)
{
	MCAssert(fPrimitive);
	fPrimitive->GetI3DShObject()->GetBoundingBox(bb);
}


void TModeler::DataChanged(IChangeChannel* channel, 
								IDType 			changeKind, 
								IMCUnknown* 	changedData)
{
	if (channel == fImmediateUpdateChannel)
	{
		boolean update=false;
		if (changeKind == kChange_ActionBegin)
		{
			fActionBeingProcessed++;
			return;
		}
		else if (changeKind == kChange_ActionEnd)
		{
			fActionBeingProcessed--;
			return;
		}


		TMCCountedPtr<I3DShPrimitive>	changedPrimitive;
		TMCCountedPtr<TPanePart>		changedPanePart;
		if (changedData)
		{
			changedData->QueryInterface(IID_I3DShPrimitive, (void**) &changedPrimitive);
			TMCCountedPtr<I3DEditorHostPanePart> hostPanePart;
			changedData->QueryInterface(IID_I3DEditorHostPanePart, (void**)&hostPanePart);
			if (hostPanePart)
			{
				TMCCountedPtr<IEx3DEditorHostPanePart> paneExtension;
				hostPanePart->GetPaneExtension(&paneExtension);
				if (MCVerify(paneExtension))
				{
					paneExtension->QueryInterface(TPanePart::sClassID, (void**)&changedPanePart);
				}
			}
		}
		
		int32 panesToUpdate[4]= {0,1,2,3}; //create an indirection so that we can update a single pane or the 4 of them.
		int32 panesCount= fPaneExtensions.GetElemCount(); //we may not have panes...
		if (changedPanePart)
		{
			const int32 paneIndex= fPaneExtensions.FindElem(changedPanePart);
			if (paneIndex != kUnusedIndex)
			{
				//only update the pane that changed
				panesToUpdate[0]= paneIndex;
				panesCount= 1;
			}
		}
		
		if ( (changedData == NULL) || (fPrimitive == changedPrimitive) || changedPanePart)
		{
			switch(changeKind)
			{
			case kChange_BeginImmediateUpdate:
				{
					fImmediateUpdate= true;
					for (int ii= 0; ii < panesCount; ++ii)
					{
						fPaneExtensions[panesToUpdate[ii]]->Get3DEditorHostPanePart()->SetImmediateMode(true);
					}
				}
				break;
			case kChange_ImmediateUpdate:
				{
					for (int ii= 0; ii < panesCount; ++ii)
					{
						const int32 currPaneIndex= panesToUpdate[ii];
						fPaneExtensions[currPaneIndex]->InvalidateRender((fActionBeingProcessed==0));
						fPaneExtensions[currPaneIndex]->InternalSelfDraw(nil,nil);
					}
					break;
				}
			case kChange_EndImmediateUpdate:
				{
					fImmediateUpdate= false;
					for (int ii= 0; ii < panesCount; ++ii)
					{
						fPaneExtensions[panesToUpdate[ii]]->Get3DEditorHostPanePart()->SetImmediateMode(false);
					}
					break;
				}
			}//switch
		}
	}//	if fImmediateUpdateChannel
	else if (channel == fTreePropertyChangeChannel)
	{
		if ((changeKind == kChange_MasterObjectName) && changedData)
		{
			TMCCountedPtr<I3DShObject>	shObject;
			fPrimitive->QueryInterface(IID_I3DShObject, (void**)&shObject);

			TMCCountedPtr<I3DShObject>	changedObject;
			changedData->QueryInterface(IID_I3DShObject, (void**)&changedObject);

			if (shObject == changedObject)
			{
				// Update window title
				SetWindowTitle();
			}
		}
		else if (changeKind == kChange_MasterObjectModified)
		{
			//We should NOT invalidate the panes when we are in ImmediateUpdate 
			//as it causes whatever is in the back buffer to be drawn on the screen
			if (!GetImmediateUpdate())
			{
				TMCCountedPtr<I3DShObject> shObject;
				fPrimitive->QueryInterface(IID_I3DShObject, (void**)&shObject);
				if (shObject == changedData)
				{
					// We simply invalidate the renderers.
					const int32 panesCount= fPaneExtensions.GetElemCount();
					for (int32 ii = 0; ii < panesCount; ii++)
					{
						fPaneExtensions[ii]->InvalidateRender(true, false);
					}//for
				}
			}
		}
	}
}



void TModeler::SelfPrepareMenus()
{
	TBasicModule::SelfPrepareMenus();

	const boolean editionEnabled=	true;

	gMenuUtilities->EnableGlobalTool(kMoveMeshTool,	editionEnabled, false);

}


void TModeler::BeginImmediateUpdate()
{
	if (MCVerify(!fImmediateUpdate))
	{	
		 gChangeManager->PostChange(fImmediateUpdateChannel, kChange_BeginImmediateUpdate, fPrimitive);
	}
}


void TModeler::PostImmediateUpdate(boolean invalidateGeometry)
{
	if (MCVerify(fImmediateUpdate))
	{
		gChangeManager->PostChange(fImmediateUpdateChannel, kChange_ImmediateUpdate, fPrimitive);
	}
}


void TModeler::EndImmediateUpdate()
{
	if (MCVerify(fImmediateUpdate))
	{
		gChangeManager->PostChange(fImmediateUpdateChannel, kChange_EndImmediateUpdate, fPrimitive);
	}
}


boolean TModeler::MouseDownInPane(	TPanePart*					inPane,
										const TMCPoint& 			inWhere, 
										const TMCPlatformEvent&		inEvent)
{
	boolean result= false;


	int32 currentTool= 0;
	gMenuUtilities->GetCurrentGlobalTool(currentTool); 
	
	switch (currentTool)
	{
	case kMoveMeshTool:
		{
			TMCCountedPtr<IShMouseAction> action;
			TMoveMouseAction::Create(&action, this, inPane);

			TMCCountedPtr<IMFPart> part;
			inPane->Get3DEditorHostPanePart()->QueryInterface(IID_IMFPart, (void**)&part);
			gActionManager->PostMouseAction(action, kMoveMeshTool, fActionsContext, part, inWhere);

			result= true;
		} 
		break;
	}


	return result;
}


IWorkingBoxPlane* TModeler::GetWorkingPlane()
{
	TMCCountedPtr<IWorkingBox> workingBox;
	f3DEditorHostPart->GetWorkingBox(&workingBox);
	ThrowIfNil(workingBox);
	
	TMCCountedPtr<IWorkingBoxPlane> workingPlane;
	workingBox->GetPlane(workingBox->GetCurrentPlane(), &workingPlane);

	return workingPlane;
}
