void CParticleEditor::play(CWorkspaceNode &node)
{
	if (isRunning(&node)) return;
	// NB : node must be stopped, no check is done
	nlassert(node.isLoaded());
	// if node not started, start it
	node.memorizeState();
	// enable the system to take the right date from the scene	
	node.getPSModel()->enableAutoGetEllapsedTime(true);
	node.getPSPointer()->setSystemDate(0.f);
	node.getPSPointer()->reactivateSound();
	node.getPSModel()->activateEmitters(true);
	if (node.getPSPointer()->getAutoCountFlag())
	{
		if (node.getResetAutoCountFlag())
		{
			// reset particle size arrays
			node.getPSPointer()->matchArraySize();
		}
		resetAutoCount(&node, false);
	}		
	
	// Set speed playback particle system
	node.getPSModel()->setEllapsedTimeRatio(_Speed);
}
NL3D::CParticleSystemModel *CParticleEditor::getModelFromPS(NL3D::CParticleSystem *ps) const
{
	if (!ps) return	NULL;
	CWorkspaceNode *node = _PW->getNodeFromPS(ps);
	if (!node) return NULL;
	return node->getPSModel();
}
void CParticleEditor::pause(CWorkspaceNode &node)
{
	if (!isRunning(&node)) return;
	nlassert(node.isLoaded());
	node.getPSModel()->enableAutoGetEllapsedTime(false);
	node.getPSModel()->setEllapsedTime(0.f);
}
void CWorkspacePage::insertPS()
{
	QString fileName = QFileDialog::getOpenFileName(this,
					   tr("Open NeL data file"), ".",
					   tr("Particle System file (*.ps);;"));

	if (!fileName.isEmpty())
	{
		// TODO: create method particle editor insertNewPS and multiple add
		CWorkspaceNode *node = Modules::psEdit().getParticleWorkspace()->addNode(NLMISC::CFile::getFilename(fileName.toUtf8().constData()));
		if (node != 0)
		{
			try
			{
				node->loadPS();
			}
			catch(NLMISC::EStream &e)
			{
				QMessageBox::critical(this, tr("NeL particle system editor"),
									  QString(e.what()),
									  QMessageBox::Ok);
			}
			if (!node->isLoaded())
				Modules::psEdit().getParticleWorkspace()->removeNode(Modules::psEdit().getParticleWorkspace()->getNumNode() - 1);
			else
			{
				QModelIndex index = _treeModel->index(0, 0);
				_treeModel->insertRows(node, static_cast<CParticleTreeItem *>(index.internalPointer())->childCount(), index);
			}
		}
	}
}
void CParticleLinkDialog::setUnlink()
{
	CWorkspaceNode *node = Modules::psEdit().getActiveNode();
	if (node == NULL)
		return;

	node->unstickPSFromSkeleton();
}
void CParticleEditor::startMultiple()
{
	switch(_State)
	{
	case State::Stopped:
		{
			if (!_PW) return;
			nlassert(_PlayingNodes.empty());
			TPWNodeItr itr = _PW->getNodeList().begin();
			while(itr != _PW->getNodeList().end())
			{
				CWorkspaceNode *node = (*itr);
				if (node->isLoaded())
					if (checkHasLoop(*node)) return;
				itr++;
			}
			
			itr = _PW->getNodeList().begin();
			while(itr != _PW->getNodeList().end())
			{
				CWorkspaceNode *node = (*itr);
				if (node->isLoaded())
				{
					// really start the node only if there's no trigger anim
					if (node->getTriggerAnim().empty())
						play(*node);

					_PlayingNodes.push_back(node);
				}
				itr++;
			}
		}
		break;
		case State::PausedSingle:
		case State::RunningSingle:
			stop();
			startMultiple();
		break;
		case State::RunningMultiple:
			// no-op
			return;
		break;
		case State::PausedMultiple:
			for(uint k = 0; k < _PlayingNodes.size(); ++k)
			{
				if (_PlayingNodes[k])
				{
					unpause(*_PlayingNodes[k]);
				}
			}
		break;
		default:
			nlassert(0);
		break;
	}
	_State = State::RunningMultiple;
}
void CParticleEditor::saveWorkspaceContent()
{
	TPWNodeItr itr = _PW->getNodeList().begin();
	while(itr != _PW->getNodeList().end())
	{
		CWorkspaceNode *node = (*itr);
		if (node->isModified())
			node->savePS();
		node->setModified(false);
		itr++;
	}
}
void CParticleEditor::setSpeed(float value)
{
	_Speed = value;

	if (!isRunning()) return;

	TPWNodeItr itr = _PW->getNodeList().begin();
	while(itr != _PW->getNodeList().end())
	{
		CWorkspaceNode *node = (*itr);
		if (node->isLoaded())
			node->getPSModel()->setEllapsedTimeRatio(value);
		itr++;
	}
}
void CParticleLinkDialog::setLink()
{
	CWorkspaceNode *node = Modules::psEdit().getActiveNode();
	if (node == NULL)
		return;

	std::string curObj = Modules::objView().getCurrentObject();
	if (curObj.empty())
		return;

	CSkeletonTreeItem *item = static_cast<CSkeletonTreeItem *>(_ui.treeView->currentIndex().internalPointer());

	NL3D::CSkeletonModel *skel = Modules::objView().getEntity(curObj).getSkeleton().getObjectPtr();
	uint boneIndex = item->getId();
	std::string parentSkelName = Modules::objView().getEntity(curObj).getFileNameSkeleton();
	std::string parentBoneName = skel->Bones[boneIndex].getBoneName();

	node->stickPSToSkeleton(skel, boneIndex, parentSkelName, parentBoneName);
}
void CParticleEditor::loadWorkspace(const std::string &fullPath)
{
	// Add to the path
	std::auto_ptr<CParticleWorkspace> newPW(new CParticleWorkspace);
	newPW->init(fullPath);
	
	// save empty workspace
	try
	{
		newPW->load();
	}
	catch(NLMISC::EStream &e)
	{
		nlerror(e.what());
		return;
	}	
	
	// try to load each ps
	CWorkspaceNode *firstLoadedNode = NULL;
	TPWNodeItr itr = newPW->getNodeList().begin();
	while(itr != newPW->getNodeList().end())
	{
		CWorkspaceNode *node = (*itr);
		try
		{
			node->loadPS();
		}
		catch(NLMISC::EStream &e)
		{
			nlwarning(e.what());
		}
		if (node->isLoaded() && !firstLoadedNode)
			firstLoadedNode = node;
		itr++;
	}
	closeWorkspace();
	_PW = newPW.release();
	setActiveNode(firstLoadedNode);
}
void CWorkspacePage::createPS()
{
	QString fileName = QFileDialog::getSaveFileName(this, tr("Create new particle system file"),
					   ".",
					   tr("ps files (*.ps)"));
	if (!fileName.isEmpty())
	{

		// TODO: create method particle editor createNewPS
		if (Modules::psEdit().getParticleWorkspace()->containsFile(NLMISC::CFile::getFilename(fileName.toUtf8().constData())))
		{
			QMessageBox::critical(this, tr("NeL particle system editor"),
								  tr("Failed to create new particle system"),
								  QMessageBox::Ok);
			return;
		}
		CWorkspaceNode *node = Modules::psEdit().getParticleWorkspace()->addNode(NLMISC::CFile::getFilename(fileName.toUtf8().constData()));
		// should always succeed because we tested if file already exists
		nlassert(node);
		node->createEmptyPS();
		try
		{
			node->savePS();
			node->setModified(false);
		}
		catch (NLMISC::Exception &e)
		{
			QMessageBox::critical(this, tr("NeL particle system editor"),
								  QString(e.what()),
								  QMessageBox::Ok);
			return;
		}
		QModelIndex index = _treeModel->index(0, 0);
		_treeModel->insertRows(node, static_cast<CParticleTreeItem *>(index.internalPointer())->childCount(), index);
	}
}
void CParticleEditor::stop(CWorkspaceNode &node)
{
	if (!isRunning(&node)) return;
	nlassert(node.isLoaded());
	node.restoreState();
	node.getPSModel()->enableAutoGetEllapsedTime(false);
	node.getPSModel()->setEllapsedTime(0.f);
	node.getPSModel()->activateEmitters(true);
	node.getPSPointer()->stopSound();
}
bool CParticleEditor::checkHasLoop(CWorkspaceNode &node)
{
	nlassert(node.isLoaded());
	if (!node.getPSPointer()->hasLoop()) return false;
	return true;	
}
void CParticleEditor::update()
{
	if (!_ActiveNode) return;
	if (_PW == NULL) return;

	NL3D::CParticleSystem *currPS = _ActiveNode->getPSPointer();
	
	// compute BBox
	if (_DisplayBBox)
	{
		if (_AutoUpdateBBox)
		{
			NLMISC::CAABBox currBBox;
			currPS->forceComputeBBox(currBBox);
			if (_EmptyBBox)
			{
				_EmptyBBox = false;
				_CurrBBox = currBBox;
			}
			else
			{
				_CurrBBox = NLMISC::CAABBox::computeAABBoxUnion(currBBox, _CurrBBox);
			}
			currPS->setPrecomputedBBox(_CurrBBox);
		}
	}
		
	// auto repeat feature
	if (_AutoRepeat)
	{
		if (isRunning())
		{
			bool allFXFinished = true;
			bool fxStarted = false;
			TPWNodeItr itr = _PlayingNodes.begin();
			while(itr != _PlayingNodes.end())
			{
				CWorkspaceNode *node = (*itr);
				if (node->isLoaded())
				{
					if (isRunning(node))
					{
						fxStarted = true;
						if (node->getPSPointer()->getSystemDate() <= node->getPSPointer()->evalDuration())
						{
							allFXFinished = false;
							break;
						}
						else 
						{
							if (node->getPSPointer()->getCurrNumParticles() != 0)
							{
								allFXFinished = false;
								break;
							}
						}
					}
				}
				itr++;
			}
			if (fxStarted && allFXFinished)
				restartAllFX();
		}
	}

	// draw PSmodel
	TPWNodeItr itr = _PW->getNodeList().begin();
	while(itr != _PW->getNodeList().end())
	{
		CWorkspaceNode *node = (*itr);
		if (node->isLoaded())
		{
			if (node  == _ActiveNode)
				node->getPSModel()->enableDisplayTools(!isRunning(node) || _DisplayHelpers);
			else
				node->getPSModel()->enableDisplayTools(false);

			// hide / show the node
			if (_State == State::RunningMultiple || _State == State::PausedMultiple)
			{
				if (isRunning(node))
					node->getPSModel()->show();
				else
					node->getPSModel()->hide();
			}
			else
			{
				if (node == _ActiveNode)
					node->getPSModel()->show();
				else
					node->getPSModel()->hide();
			}
		}
		itr++;
	}
}