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

	Behavior.cpp

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

	Author:	Guillaume Chermette

	Date:	11/06/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	
****************************************************************************************/

#include "Behavior.h"
#include "COM3DUtilities.h"
#include "I3DExHierarchyModule.h"
#include "I3DShScene.h"
#include "I3DShTreeElement.h"
#include "I3DShUtilities.h"
#include "IMFPart.h"
#include "IMFResponder.h"
#include "ISceneDocument.h"
#include "MCCountedPtrHelper.h"
#include "MFPartMessages.h"
#include "MiscComUtilsImpl.h"
#include "Transforms.h"

const MCGUID CLSID_Behavior(R_CLSID_Behavior);


//------------------------------------------------------
void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialisation there
}


//------------------------------------------------------
void Extension3DCleanup()
{
	// Perform any nec clean-up here
}


//------------------------------------------------------
TBasicUnknown* MakeCOMObject (const MCCLSID& classId) // This method instanciate the object Behavior
{
	TBasicUnknown* res = NULL;

	if (classId == CLSID_Behavior)
		res = new Behavior;

	return res;
}


//------------------------------------------------------
int16 Behavior::GetResID()
{
	return 142;		// This is the view ID in the resource file
}


// Constructor of the Object :
//------------------------------------------------------
Behavior::Behavior()
{
	fObject1 = TTreeIdPath::InvalidPath();
	fObject2 = TTreeIdPath::InvalidPath();
	fData.fRelPos = 0.5; // 50% , at the middle of the two objects
}


MCErr  Behavior::QueryInterface(const MCIID& riid, void** ppvObj)
{
	if (MCIsEqualIID(riid, CLSID_Behavior)) 
	{
		TMCCountedGetHelper<Behavior>	result(ppvObj);
		result = this;
		return MC_S_OK;
	}
	if (MCIsEqualIID(riid, IID_IExStreamIO)) 
	{
		TMCCountedGetHelper<IExStreamIO>	result(ppvObj);
		result = this;
		return MC_S_OK;
	}
	return TBasicTreeModifier::QueryInterface(riid, ppvObj);
}

// I3DExModifier methods :
//------------------------------------------------------
void* Behavior::GetExtensionDataBuffer()
{
  return &fData; // used by the shell to set the new parameters
}

  
//------------------------------------------------------
MCCOMErr Behavior::ExtensionDataChanged()
{
  return MC_S_OK;
}

//------------------------------------------------------
MCCOMErr Behavior::HandleEvent(int32 message, IMFResponder* source, void* data)
{
	switch (message)
	{
	case EMFPartMessage::kMsg_PartValueChanged:
		{
			if (source)
			{
				TMCCountedPtr<IMFPart> sourcePart;
				source->QueryInterface(IID_IMFPart,(void**)&sourcePart);
				ThrowIfNil(sourcePart);

				IDType partID = sourcePart->GetIMFPartID();

				switch (partID)
				{
				case kObject1:
				case kObject2:
					TMCDynamicString title, explanation;
					{
						CWhileInCompResFile resFile(kRID_ModifierFamilyID, 'WTbh');
						gResourceUtilities->GetIndString(title, 128, 1);
						gResourceUtilities->GetIndString(explanation, 128, 2);
					}

					// get the active scene
					TMCPtr<ISceneDocument>	sceneDoc;
					sceneDoc = gShell3DUtilities->GetLastActiveSceneDoc();
					MCVerify(sceneDoc);
					TMCPtr<I3DShScene> scene;
					scene = sceneDoc->GetScene();
					MCVerify(scene);
					
					TMCCountedPtr<I3DShTreeElement> tree;
					scene->GetTreeByIDPath(&tree, (partID == kObject1)?fObject1:fObject2);

					if (gShell3DUtilities->ChooseTreeDialog(&tree, title, explanation, nil, tree))
					{
						if (partID == kObject1)
							fObject1 = TTreeIdPath::Root() + scene->GetTreePermanentID(tree);
						else
							fObject2 = TTreeIdPath::Root() + scene->GetTreePermanentID(tree);
					}

					break;
				}
			}
		}
	}
	return MC_S_OK;
}


//------------------------------------------------------
void Behavior::Clone(IExDataExchanger** res,IMCUnknown* pUnkOuter)
{
	TMCCountedCreateHelper<IExDataExchanger> result(res);
	Behavior* clone = new Behavior();
	result = (IExDataExchanger*)clone;

	if (clone)
	{
	    clone->fData=fData; // copy the BehaviorData
		clone->fObject1 = fObject1;
		clone->fObject2 = fObject2;
	}
	clone->SetControllingUnknown(pUnkOuter);
} 

//------------------------------------------------------
MCCOMErr Behavior::Apply(I3DShTreeElement* tree)
{
  TTransform3D tr1, tr2, tr;
  TMCCountedPtr<I3DShTreeElement> tree1, tree2;
  TMCCountedPtr<I3DShScene> scene;

  // Get the scene
  tree->GetScene(&scene);
  if (!scene)
	  return MC_S_OK; // abort any modifications if you can not get the scene

  // Search tree element 1 by name
  scene->GetTreeByIDPath(&tree1, fObject1);
  if (!tree1)
	  return MC_S_OK; // abort because object 1 not found
  tree1->GetGlobalTransform3D(tr1);

  // Search tree element 2 by name
  scene->GetTreeByIDPath(&tree2, fObject2);
  if (!tree2)
	  return MC_S_OK; // abort because object 2 not found
  tree2->GetGlobalTransform3D(tr2);

  // Set the new position of tree
  tree->GetGlobalTransform3D(tr);
  tr.fTranslation = tr1.fTranslation + (tr2.fTranslation - tr1.fTranslation) * fData.fRelPos;
  tree->SetGlobalTransform3D(tr);

  return MC_S_OK;
}

//------------------------------------------------------
MCCOMErr Behavior::CopyComponentExtraData (IExDataExchanger* dest)
{
	TMCCountedPtr<Behavior> destBehavior;
	dest->QueryInterface(CLSID_Behavior, (void**)&destBehavior);
	if (destBehavior)
	{
		destBehavior->fObject1 = fObject1;
		destBehavior->fObject2 = fObject2;
	}
	return MC_S_OK;
}

// IExStreamIO methods
//------------------------------------------------------
MCCOMErr Behavior::Read				(IShTokenStream* stream, ReadAttributeProc readUnknown, void* privData)
{
	int8 token[256];
	MCCOMErr result = MC_S_OK;

	result = stream->GetBegin();
	if (result) return result;

	result=stream->GetNextToken(token);
	if (result) return result;

	TMCiostream& ioStream = stream->GetStream();

	fObject1 = TTreeIdPath::InvalidPath();
	fObject2 = TTreeIdPath::InvalidPath();

	while (!stream->IsEndToken(token))
	{
		int32 keyword;
		stream->CompactAttribute(token,&keyword);

		switch (keyword)
		{
		case kObject1:
			fObject1 = TTreeIdPath::Read(ioStream);
			break;
		case kObject2:
			fObject2 = TTreeIdPath::Read(ioStream);
			break;
		default:
			readUnknown(keyword, stream, privData);
			break;
		};
		result=stream->GetNextToken(token);
		if (result)
		{
			return result;
		}
	}

	return MC_S_OK;	
}

MCCOMErr Behavior::Write (IShTokenStream* stream)
{
	TMCiostream& ioStream = stream->GetStream();

	if (fObject1.isValid())
		fObject1.Write(ioStream, kObject1);

	if (fObject2.isValid())
		fObject2.Write(ioStream, kObject2);

	return MC_S_OK;
}
