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

  	StairCommand.cpp

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

	Author:	Jerome Bignon

	Date:	20/04/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods
	15/07/99	Jerome		Creation

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

#include "math.h"
#include "StairCommand.h"
#include "I3DShGroup.h"
#include "I3DShTreeElement.h"
#include "I3DShModifier.h"

extern IChangeManager*	gChangeManager;

const MCGUID CLSID_StairCommand(R_CLSID_StairCommand);


void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

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


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

	if (classId == CLSID_StairCommand) res = new StairCommand;

	MCAssert(res);

	return res;
}

// Constructor of the C++ Object :

StairCommand::StairCommand()
{
	// Data initialisation
	// fData variables
	fData.fNbStep = 10;
	fData.fDx = 0.0;
	fData.fDy = 1.0;
	fData.fDz = 1.0;
	fData.fApplyModifier = NULL;

	// Protected variables
	fSceneDocument = NULL;
	fTree = NULL;
	fScene = NULL;
	fSelection = NULL;
}
  
StairCommand::~StairCommand()
{
}

void* StairCommand::GetExtensionDataBuffer()
{
	return &fData; // used by the shell to set the new parameters
}
  
short StairCommand::GetResID()
{
	return 150; // this is the view ID in the resource file.
}


// For the component chooser
MCCOMErr StairCommand::ExtensionDataChanged()
{
	MCCOMErr result = MC_S_OK;

	// If the user choosed a modifier, then we put it in the fApplyModifier variable
	if (fData.fApplyModifier)
		result = fData.fApplyModifier->QueryInterface(IID_I3DExModifier, (void**)&fApplyModifier);

	return result;
}
                                                                            
// TBasicSceneCommand methods :
class IsSelectionNonEmpty : public TBasicMenuCallBack
{
protected:
	IsSelectionNonEmpty() {}
	virtual ~IsSelectionNonEmpty() {}
public:
	void static Create(ISelfPrepareMenuCallBack** out);
	virtual boolean MCCOMAPI SelfPrepareMenu(ISceneDocument* sceneDocument);
};

void IsSelectionNonEmpty::Create(ISelfPrepareMenuCallBack** out)
{
	TMCCountedCreateHelper<ISelfPrepareMenuCallBack> result(out);
	result = new IsSelectionNonEmpty();
}

boolean IsSelectionNonEmpty::SelfPrepareMenu(ISceneDocument* sceneDocument)
{
	if (!sceneDocument) return false;
	TMCCountedPtr<ISceneSelection> selection;
	sceneDocument->GetSceneSelection(&selection);

	if (!selection) return false;

	// check that there is at least one object selected other than the universe
	TTreeSelectionIterator selectionIter(selection);
	I3DShTreeElement* tree= selectionIter.First();
	while (tree)
	{
		TMCCountedPtr<I3DShTreeElement> parent;
		tree->GetTop(&parent);
		if (parent) return true;
		tree = selectionIter.Next(); // here, we make sure that this tree element is in the triple buffer
	}

	return false;
}

void StairCommand::GetMenuCallBack(ISelfPrepareMenuCallBack** callBack)
{
	IsSelectionNonEmpty::Create(callBack);
}

MCCOMErr StairCommand::Init(ISceneDocument* sceneDocument)
{
	fSceneDocument = sceneDocument;
	sceneDocument -> GetSceneSelection(&fSelection);
	fSelection -> Clone(&fCloneSelection);


	fData.fNbStep = 5;
	fData.fDx = 0.0;
	fData.fDy = 1.0;
	fData.fDz = 1.0;
	return MC_S_OK;
}


MCCOMErr StairCommand::Prepare()
{
	if (fSceneDocument)
	{
		fSceneDocument->GetScene(&fScene);

		fScene->GetTreePropertyChangeChannel(&fTreePropertyChannel);
		ThrowIfNil(fTreePropertyChannel);

		fSceneDocument->GetSceneSelectionChannel(&fSelectionChannel);
		ThrowIfNil(fSelectionChannel);
	}

	if (!fTree)
	{
		TMCCountedPtr<I3DShGroup> group;
		fScene->GetTreeRoot(&group);
		if (group->QueryInterface(IID_I3DShTreeElement, (void **) &fTree) != MC_S_OK)
			return MC_S_OK;
	}
	return MC_S_OK;
}

boolean StairCommand::Do()
{
	TTreeTransform						stepTransform;
	TVector3							offset = TVector3::kZero;	// we initialize the offset
	TTreeSelectionIterator				iter(fCloneSelection);

	// First, we unselect everything. 
	TTreeSelectionIterator selectionIter(fSelection);
	I3DShTreeElement* tree= selectionIter.First();
	while (tree)
	{
		tree->PostMoveChange();
		tree = selectionIter.Next(); // here, we make sure that this tree element is in the triple buffer
	}

	fSelection->ClearSelection();

	// Second, create the stairs, select and insert in the scene
	TMCCountedPtr<I3DShTreeElement>	newStep;
	I3DShTreeElement* iTree= iter.First();
	while (iTree)
	{
		for (int16 iStep=0; iStep<fData.fNbStep; iStep++)
		{ 
			iTree->ComClone(&newStep, kWithAnim, true);

			if (!newStep)
				return false;

			// Insert in the scene
			iTree->InsertRight(newStep);

			// Store for undo
			fSteps.AddElem(newStep);

			newStep->GetLocalTreeTransform(stepTransform);

			offset[0] += fData.fDx;
			offset[1] += fData.fDy;
			offset[2] += fData.fDz;

			stepTransform.SetOffset(offset);
			newStep->SetLocalTreeTransform(stepTransform);

			if(fApplyModifier)  // If the user chose a modifier, we apply it on the new instances.
			{
				TMCCountedPtr<I3DShModifier>		shModifier;
				fApplyModifier->QueryInterface (IID_I3DShModifier,(void**)&shModifier);
				newStep->InsertModifier(shModifier, -1, kWithAnim);
			}

			// Select the new tree element
			TMCCountedPtr<ISelectableObject> newSelectable;
			newStep->QueryInterface(IID_ISelectableObject, (void**)&newSelectable);
			newSelectable->AddToSelection(fSelection,true);
		}
		iTree= iter.Next();
	}

	// Third, tell the Shell what object must be redrawn (it can not guess, but we easily can tell it)
	// Note :	using the selection-change-channel makes the interactive renderer add this I3DShTreeElement to the triple buffer, 
	//			and therefore redraws correctly.
	selectionIter = TTreeSelectionIterator(fSelection);
	tree = selectionIter.First();
	while (tree)
	{
		tree->PostMoveChange(); // here, we make sure that this tree element is in the triple buffer
		tree = selectionIter.Next();
	}

	gChangeManager->PostChange(fSelectionChannel, 0, fSelection);

	return true;
}

boolean StairCommand::CanUndo()
{
	return true;
}

boolean StairCommand::Undo()
{
	fSceneDocument->GetSceneSelection(&fSelection);

	// Clear the selection
	TTreeSelectionIterator	selectionIter(fSelection);
	I3DShTreeElement* tree= selectionIter.First();
	while (tree)
	{
		tree->PostMoveChange();
		tree = selectionIter.Next();
	}

	gChangeManager->PostChange(fSelectionChannel, 0, fSelection);

	fSelection->ClearSelection();

	// Prepare the redo info
	fRelinkInfo.ArrayFree();

	// Remove the new items from the scene
	TMCCountedPtrArray<I3DShTreeElement>::iterator	iter(fSteps);
	for (tree = iter.First(); iter.More(); tree = iter.Next())
	{
		RelinkTreeElementInfo info;
		TMCCountedPtr<I3DShTreeElement> unlinkedTree;
		tree->Unlink(&unlinkedTree, &info);
		fRelinkInfo.AddElem(info);
		gChangeManager->PostChange(fTreePropertyChannel, kChange_TreeRemoved, tree);
	}

	// Restore the initial selection state
	fSelection->Select(fCloneSelection);

	gChangeManager->PostChange(fSelectionChannel, 0, fSelection);

	return true;
}

boolean StairCommand::Redo()
{
	fSceneDocument->GetSceneSelection(&fSelection);

	// Clear the selection
	TTreeSelectionIterator	selectionIter(fSelection);
	I3DShTreeElement* tree= selectionIter.First();
	while (tree)
	{
		tree->PostMoveChange();
		tree = selectionIter.Next();
	}


	gChangeManager->PostChange(fSelectionChannel, 0, fSelection);

	fSelection->ClearSelection();

	// Redo
	TMCArray<RelinkTreeElementInfo>::iterator iter(fRelinkInfo);
	for (RelinkTreeElementInfo* info=iter.Last(); iter.MorePrev(); info=iter.Prev())
	{
		// Restore the instances position
		TTreeTransform globalT;
		info->fTreeElement->GetGlobalTreeTransform(globalT);
		info->fTreeElement->Relink(info);
		info->fTreeElement->SetGlobalTreeTransform(globalT);
		gChangeManager->PostChange(fTreePropertyChannel, kChange_TreeAdded, info->fTreeElement);

		// Select the new tree element
		TMCCountedPtr<ISelectableObject> newSelectable;
		info->fTreeElement->QueryInterface(IID_ISelectableObject, (void**)&newSelectable);
		newSelectable->AddToSelection(fSelection,true);

	}

	return true;
}
