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

		Modeler.cpp

		Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

		Author:	Jerome Bignon
		Date:	05/18/99

		Revision History

		when		who			what & why
		==========	==========	======================================================
		08/20/99	cph			updated to remove COM prefix
		06/27/99	cph			added include for MCCountedPtrHelper.h
		06/11/99	Jerome		Adding animation and saving
		05/18/99	Jerome		Creation

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

#include "Modeler.h"
#include "ModelerDef.h"
#include "ModelerView.h"

#include "COMUtilities.h"
#include "COM3DUtilities.h"
#include "IShUtilities.h"
#include "I3DEditorHostPartDefs.h"
#include "IShComponent.h"
#include "ComMessages.h"
#include "IMFResponder.h"
#include "IComponentAnim.h"
#include "ModelerProp.h"
#include "MCCountedPtrHelper.h"
#include "I3DShModule.h"
#include "COMSafeUtilities.h"

const MCGUID CLSID_Modeler(R_CLSID_Modeler);


//-- Methods for the Modeler creation and initialization -----------------------------

Modeler::Modeler()
{
	fModule = NULL;
	fDoc = NULL;
	fSceneDoc = NULL;
	fScene = NULL;

	fShPrimitive = NULL;

	fWindow = NULL;

	fImmediateUpdate = false;
	gChangeManager->CreateChannel(&fSelectionChangeChannel);
}


MCCOMErr Modeler::QueryInterface(const MCIID& riid, void** ppvObj)
{
	return TBasicModule::QueryInterface(riid, ppvObj);
}

uint32 Modeler::AddRef()
{
	return TBasicModule::AddRef();
}

MCCOMErr Modeler::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;

	TMCCountedPtr<I3DShGroup> universe;
	if(inElement->QueryInterface(IID_I3DShGroup, (void**) &universe) == MC_S_OK)
	{
	// TBasicModule::Initialize(inElement);
		return Initialize1(universe);
	}

	if(!fUniverse) return MC_E_INVALIDARG;

	TMCCountedPtr<I3DShPrimitive> primitive;
	if(inElement->QueryInterface(IID_I3DShPrimitive, (void**)&primitive) == MC_S_OK)
	{
		return Initialize2(primitive);
	}

	return MC_E_INVALIDARG;
}

MCCOMErr Modeler::Initialize1(I3DShGroup* universe)
{
	// Initialization Call #1 : the Universe is passed

	if(!universe) return MC_E_INVALIDARG;
	fUniverse = universe;
	
	return MC_S_OK;
}

MCCOMErr Modeler::Initialize2(I3DShPrimitive* primitive)
{
	// Initialization Call #2 : the Master Object (primitive) is passed
	//-- Get target data
	fShPrimitive = primitive;
	ThrowIfNil(fShPrimitive);

	TMCCountedPtr<I3DShExternalPrimitive> externalPrimitive;
	fShPrimitive->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);

	TMCCountedPtr<IMCUnknown> extPrimitive;
	primitiveComponent->GetExternalPrimitive(&extPrimitive);
	ThrowIfNil(extPrimitive);

	extPrimitive->QueryInterface(IID_I3DExGeometricPrimitive, (void**)&fExPrimitive);
	ThrowIfNil(fExPrimitive);

	extPrimitive->QueryInterface(IID_ITreePrim, (void**) &fCOMTreePrim);
	ThrowIfNil(fCOMTreePrim);

	//-- Load windows and set up UI elements
	CreateWindows();

	//-- Register ourselves as listeners to some channels
	fScene->GetTimeChangeChannel(&fTimeChangeChannel);
	ThrowIfNil(fTimeChangeChannel);
	fTimeChangeChannel->RegisterListener(this);

	fScene->GetTreePropertyChangeChannel(&fTreePropertyChangeChannel);
	ThrowIfNil(fTreePropertyChangeChannel);
	fTreePropertyChangeChannel->RegisterListener(this);

	fSceneDoc->GetSceneSelectionChannel(&fSelectionChannel);
	ThrowIfNil(fSelectionChannel);
	fSelectionChannel->RegisterListener(this);

	fSceneDoc->GetTreeHierarchyChannel(&fTreeHierarchyChannel);
	ThrowIfNil(fTreeHierarchyChannel);
	fTreeHierarchyChannel->RegisterListener(this);

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

	//-- Properties

	MCNotify("Todo: finish sample");
/*Arnaud: Do NOT create a properties module !!! And not NOT add ref manually!! (get the prop module...)
	gShell3DUtilities->CreateModule(&fPropertiesModule, 'prop', fDoc);
	ThrowIfNil(fPropertiesModule);
	TMCCountedPtr<IPropertiesModule> propertiesModule;
	fPropertiesModule->QueryInterface(IID_IPropertiesModule, (void**) &propertiesModule);
	ThrowIfNil(propertiesModule);
	ModelerProp* client = new ModelerProp(propertiesModule, this);
	TMCCountedPtr<IPropertiesClient> propertiesClient;
	client->QueryInterface(IID_IPropertiesClient, (void**) &propertiesClient);
	propertiesModule->SetCurrentClient(propertiesClient);
	propertiesClient->AddRef();	//*** since IPropertiesModule::SetCurrentClient does not do it !!!...
*/

	//-- Hierarchy
	gShell3DUtilities->GetDocumentModule(&fHierarchyModule, fDoc, 'hier');
	ThrowIfNil(fHierarchyModule);

	return MC_S_OK;

}

//-- End of the methods for creation and inialization -----------------------------------

//-- Method to destroy the Modeler ---------------------------------------------------

MCCOMErr Modeler::Destroy()	
{
	DestroyWindows();

	//-- Clear everything
	fHierarchyModule = NULL;
	fUniverse = NULL;
	fWindow = NULL;
	fShPrimitive = NULL;

	//-- Deregister ourselves as listeners
	if (fTimeChangeChannel)			fTimeChangeChannel->UnregisterListener(this);
	if (fImmediateUpdateChannel)	fImmediateUpdateChannel->UnregisterListener(this);
	if (fTreePropertyChangeChannel)	fTreePropertyChangeChannel->UnregisterListener(this);
	if (fSelectionChannel)			fSelectionChannel->UnregisterListener(this);
	if (fTreeHierarchyChannel)		fTreeHierarchyChannel->UnregisterListener(this);

	fTimeChangeChannel			= NULL;
	fImmediateUpdateChannel		= NULL;
	fTreePropertyChangeChannel	= NULL;
	fSelectionChannel			= NULL;
	fTreeHierarchyChannel		= NULL;

	return TBasicModule::Destroy();
}

//-- End of the method to destroy -------------------------------------------------------

//-- Methods for the Modeler handling ------------------------------------------------

MCCOMErr Modeler::Hydrate()
{
	TMCCountedPtr<IMFWindow> win;
	fWindow->QueryInterface(IID_IMFWindow, (void **)&win); ThrowIfNil(win);
	win->Show(true, false);
	gMenuUtilities->SetCurrentGlobalTool(9400, true);
	
	//-- Hydrate other Modules
	if (fHierarchyModule)
		fHierarchyModule->Hydrate();
	if (fPropertiesModule)
		fPropertiesModule->Hydrate();

	return TBasicModule::Hydrate();
}

MCCOMErr Modeler::Dehydrate()
{
	//-- Dehydrate other Modules
	if (fHierarchyModule)
		fHierarchyModule->Dehydrate();
	if (fPropertiesModule)
		fPropertiesModule->Dehydrate();

	//-- Hide window
	if (fWindow)
	{
		TMCCountedPtr<IMFWindow> win;
		fWindow->QueryInterface(IID_IMFWindow, (void **)&win); ThrowIfNil(win);
		win->Show(false, false);
	}

	return TBasicModule::Dehydrate();
}

MCCOMErr Modeler::Activate()
{
	//-- Select window
	TMCCountedPtr<IMFWindow> win;
	fWindow->QueryInterface(IID_IMFWindow, (void**) &win);	ThrowIfNil(win);
	win->SelectWindow();

	//-- Activate other Modules
	if (fHierarchyModule)
		fHierarchyModule->Activate();
	if (fPropertiesModule)
		fPropertiesModule->Activate();

	return MC_S_OK;
}

MCCOMErr Modeler::Deactivate()
{
	//-- Deactivate other Modules
	if (fHierarchyModule)
		fHierarchyModule->Deactivate();
	if (fPropertiesModule)
		fPropertiesModule->Deactivate();

	return MC_S_OK;
}

void Modeler::GetActionResponder(IMFResponder** responder)
{
	fDoc->QueryInterface(IID_IMFResponder, (void**) responder);
}


void Modeler::DataChanged(IChangeChannel*		channel, 
							 IDType 			changeKind, 
							 IMCUnknown* 		changedData)
{
	TBasicModule::DataChanged(channel, changeKind, changedData);

	if ( channel == fTimeChangeChannel )
	{
		GetIMFPartNoAddRef()->Invalidate();
		GetIMFPartNoAddRef()->ProcessUpdatesPart();

		// We want the properties palette to update

		TMCCountedPtr<ISceneSelection> selection;
		TMCCountedPtr<IChangeChannel> channel;
		GetSelection(&selection);
		GetSelectionChannel(&channel);
		gChangeManager->PostChange(channel, changeKind, selection);
	}
}

//-- End of the handling methods --------------------------------------------------------

//-- Window handling methods ------------------------------------------------------------

void  Modeler::CreateWindows()
{
	QueryInterface(IID_I3DShModule, (void**) &fModule);
	ThrowIfNil(fModule);

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

	fDoc->QueryInterface(IID_ISceneDocument, (void**) &fSceneDoc);
	ThrowIfNil(fSceneDoc);

	fSceneDoc->GetScene(&fScene);
	ThrowIfNil(fScene);

	void* oldResources = NULL;
	gResourceUtilities->SetupComponentResources('modu', kModelerID, &oldResources);
	
	fModule->CreateWindowByResource(&fWindow, kModelerView, true);
 	ThrowIfNil(fWindow);
	
	fWindow->FindChildPartByID(&fMainPart, 'MODv');

	fMainPart->QueryInterface(IID_IModelerView, (void**) &fModelerPart);
	ThrowIfNil(fModelerPart);

	fModelerPart->SetModeler(this);

	gResourceUtilities->RestoreComponentResources(oldResources);
}

void Modeler::DestroyWindows()
{
	fModule = NULL;
	fDoc = NULL;
	fSceneDoc = NULL;
	fScene = NULL;
	fWindow = NULL;
	fMainPart = NULL;
}

MCCOMErr Modeler::AboutToCloseMainWindow()
{
	DestroyWindows();
	return MC_S_OK;
}

//-- End of the window handling methods -------------------------------------------------

//-- Methods for the Modeler menu ----------------------------------------------------

void Modeler::SelfPrepareMenus()
{
	TBasicModule::SelfPrepareMenus();
	gMenuUtilities->EnableMenuAction(20108, true);
	gMenuUtilities->EnableMenuAction(20102, true);
	gMenuUtilities->EnableMenuAction(20104, true);
	gMenuUtilities->EnableMenuAction(20106, true);
	gMenuUtilities->EnableMenuAction(20101, true);
	gMenuUtilities->EnableMenuAction(20103, true);
	gMenuUtilities->EnableMenuAction(20113, true);
	gMenuUtilities->EnableMenuAction(20112, true);
}

boolean	Modeler::SelfMenuAction(ActionNumber actionNumber)
{
	boolean result=true;

	switch( actionNumber )
	{
		case 20101 :
		case 20103 :
		case 20102 :
		case 20104 :
		case 20106 :
		case 20108 :
			GetPartNoAddRef()->SetViewSide( actionNumber - 20100 );				
			GetIMFPartNoAddRef()->Invalidate();
			GetIMFPartNoAddRef()->ProcessUpdatesPart();
			break;						
		case 20112 :
//			NbrStepsAction( context, actionNumber );
			break;
		case 20113 :
			GetPartNoAddRef()->SetZoom11();
			GetIMFPartNoAddRef()->Invalidate();
			GetIMFPartNoAddRef()->ProcessUpdatesPart();

			break;
		default :
			result= false;// Not implemented : the shell will find the right function.
	}
	return result;
}

//-- End of the methods for the menu ----------------------------------------------------

//-- Selection methods ------------------------------------------------------------------

void Modeler::GetSelection(ISceneSelection**	outSelection)
{
	TRefAddingPtr<ISceneSelection> result(outSelection);
	result = fSelection;
}

void Modeler::GetSelectionChannel(IChangeChannel** outChannel)
{
	TRefAddingPtr<IChangeChannel> result(outChannel);
	result = fSelectionChangeChannel;
}

//-- End of the selection methods -------------------------------------------------------

//-- Methods for updating the modeler ---------------------------------------------------

void Modeler::BeginImmediateUpdate()
{
	gChangeManager->PostChange(fImmediateUpdateChannel, kChange_BeginImmediateUpdate, NULL);
	gChangeManager->PostChange(fSelectionChangeChannel, kChange_BeginImmediateUpdate, NULL);
}

void Modeler::PostImmediateUpdate()
{
	gChangeManager->PostChange(fImmediateUpdateChannel, kChange_ImmediateUpdate, NULL);
	gChangeManager->PostChange(fSelectionChangeChannel, kChange_ImmediateUpdate, NULL);
}

void Modeler::EndImmediateUpdate()
{
	gChangeManager->PostChange(fImmediateUpdateChannel, kChange_EndImmediateUpdate, NULL);
	gChangeManager->PostChange(fSelectionChangeChannel, kChange_EndImmediateUpdate, NULL);
}

void Modeler::ImmediateUpdate()
{
	BeginImmediateUpdate();
	PostImmediateUpdate();
	EndImmediateUpdate();
}

//-- End of the updating methods --------------------------------------------------------