//------------------------------------------------------------------------------
void
ParticleEmitterTranslator::translate(Ogre::ScriptCompiler *compiler, const Ogre::AbstractNodePtr &node)
{
    Ogre::ObjectAbstractNode *obj = reinterpret_cast<Ogre::ObjectAbstractNode*>(node.get());

    // name can't be empty because we get renderer type from it
    if(obj->name.empty())
    {
        compiler->addError(Ogre::ScriptCompiler::CE_OBJECTNAMEEXPECTED, obj->file, obj->line);
        return;
    }

    ParticleTechnique* tech = Ogre::any_cast<ParticleTechnique*>(obj->parent->context);

    ParticleEmitter* emitter = tech->CreateEmitter(obj->name);
    if (emitter == NULL)
    {
        compiler->addError(Ogre::ScriptCompiler::CE_OBJECTALLOCATIONERROR, obj->file, obj->line);
        return;
    }
    Ogre::LogManager::getSingletonPtr()->logMessage("ParticleEmitterTranslator: create emitter.");

    for (Ogre::AbstractNodeList::iterator i = obj->children.begin(); i != obj->children.end(); ++i)
    {
        if ((*i)->type == Ogre::ANT_PROPERTY)
        {
            Ogre::PropertyAbstractNode *prop = reinterpret_cast<Ogre::PropertyAbstractNode*>((*i).get());
            Ogre::String value = prop->getValue();
/*
            // Glob the values together
            for (Ogre::AbstractNodeList::iterator i = prop->values.begin(); i != prop->values.end(); ++i)
            {
                if((*i)->type == Ogre::ANT_ATOM)
                {
                    if (value.empty())
                    {
                        value = ((Ogre::AtomAbstractNode*)(*i).get())->value;
                    }
                    else
                    {
                        value = value + " " + ((Ogre::AtomAbstractNode*)(*i).get())->value;
                    }
                }
                else
                {
                    compiler->addError(Ogre::ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line);
                    break;
                }
            }
*/
            Ogre::LogManager::getSingletonPtr()->logMessage("ParticleEmitterTranslator: Set param '" + prop->name + "' to '" + value + "'.");
            if (!emitter->setParameter(prop->name, value))
            {
                compiler->addError(Ogre::ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line);
            }
        }
    }
}
	//-----------------------------------------------------------------------
	void SlaveEmitter::_unprepare(ParticleTechnique* particleTechnique)
	{
		ParticleSystem* system = particleTechnique->getParentSystem();
		if (system)
		{
			ParticleTechnique* masterTechnique = system->getTechnique(mMasterTechniqueName);
			if (masterTechnique)
			{
				masterTechnique->removeTechniqueListener(this);
			}
		}
	}
	//-----------------------------------------------------------------------
	void SlaveEmitter::_prepare(ParticleTechnique* particleTechnique)
	{
		ParticleSystem* system = particleTechnique->getParentSystem();
		if (system)
		{
			ParticleTechnique* masterTechnique = system->getTechnique(mMasterTechniqueName);
			if (masterTechnique)
			{
				masterTechnique->addTechniqueListener(this);
			}
			mEnabled = false;
		}
	}
	//-----------------------------------------------------------------------
	void ParticleSystem::stopFade(void)
	{
		size_t i;
		size_t j;
		size_t numTechniques = getNumTechniques();
		size_t numEmitters;
		ParticleTechnique* technique;
		ParticleEmitter* emitter;
		for (i = 0; i < numTechniques; ++i)
		{
			technique = getTechnique(i);
			numEmitters = getTechnique(i)->getNumEmitters();
			for (j = 0; j < numEmitters; ++j)
			{
				emitter = technique->getEmitter(j);
				emitter->setEnabled(false);
			}
		}
		mStopFadeSet = true;
	}
	//-----------------------------------------------------------------------
	void ParticlePool::_increaseParticleTechniquePool(size_t size, 
		Particle::ParticleBehaviourList& behaviours, 
		ParticleSystem* system)
	{
		size_t oldSize = mTechniques.size();
		if (size < oldSize)
			return;

		// Create new techniques, based on the techniques in the particle system and which are marked for emission.
		size_t numberOfEmittedTechniques = system->getNumEmittedTechniques();
		if (numberOfEmittedTechniques == 0)
			return;

		ParticleTechnique* existingTechnique = 0;
		ParticleTechnique* clonedTechnique = 0;
		size_t numTechniques = system->getNumTechniques();

		// Distribute size equally
		size_t increment = (size-oldSize) / numberOfEmittedTechniques;

		// Run through techniques of the system
		for (size_t techniqueCount = 0; techniqueCount < numTechniques; techniqueCount++)
		{
			existingTechnique = system->getTechnique(techniqueCount);
			if (existingTechnique->_isMarkedForEmission())
			{
				// Clone the technique 'increment' times and add to the pool
				for (size_t i = 0; i < increment; i++)
				{
					clonedTechnique = ParticleSystemManager::getSingletonPtr()->cloneTechnique(existingTechnique);
					clonedTechnique->_setMarkedForEmission(true);
					clonedTechnique->copyBehaviours(behaviours);
					mParticleTechniquePool.addElement(clonedTechnique->getName(), clonedTechnique);
					mTechniques.push_back(clonedTechnique);

					/** Important note:
						Calling the _prepare() function of the cloned techniques must not be done here. It is
						handled in the _update() function which is called when processing all particles in the
						pool.
					*/
				}
			}
		}
	}
	//-------------------------------------------------------------------------
	void ExternTranslator::translate(ScriptCompiler* compiler, const AbstractNodePtr &node)
	{
		ObjectAbstractNode* obj = reinterpret_cast<ObjectAbstractNode*>(node.get());
		ObjectAbstractNode* parent = obj->parent ? reinterpret_cast<ObjectAbstractNode*>(obj->parent) : 0;

		// The name of the obj is the type of the Extern
		// Remark: This can be solved by using a listener, so that obj->values is filled with type + name. Something for later
		String type;
		if(!obj->name.empty())
		{
			type = obj->name;
		}
		else
		{
			compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, obj->file, obj->line);
			return;
		}

		// Get the factory
		ExternFactory* externFactory = ParticleSystemManager::getSingletonPtr()->getExternFactory(type);
		if (!externFactory)
		{
			compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, obj->file, obj->line);
			return;
		}

		// Create the Extern
		mExtern = ParticleSystemManager::getSingletonPtr()->createExtern(type);
		if (!mExtern)
		{
			compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, obj->file, obj->line);
			return;
		}

		if (!obj->parent->context.isEmpty())
		{
			ParticleTechnique* technique = any_cast<ParticleTechnique*>(obj->parent->context);
			technique->addExtern(mExtern);
		}
		else
		{
			// It is an alias
			mExtern->setAliasName(parent->name);
			ParticleSystemManager::getSingletonPtr()->addAlias(mExtern);
		}

		// The first value is the (optional) name
		String name;
		if(!obj->values.empty())
		{
			getString(obj->values.front(), &name);
			mExtern->setName(name);
		}

		// Set it in the context
		obj->context = Any(mExtern);

		// Run through properties
		for(AbstractNodeList::iterator i = obj->children.begin(); i != obj->children.end(); ++i)
		{
			// No properties of its own
			if((*i)->type == ANT_PROPERTY)
			{
				PropertyAbstractNode* prop = reinterpret_cast<PropertyAbstractNode*>((*i).get());
				if (externFactory->translateChildProperty(compiler, *i))
				{
					// Parsed the property by another translator; do nothing
				}
				else
				{
					errorUnexpectedProperty(compiler, prop);
				}
			}
			else if((*i)->type == ANT_OBJECT)
			{
				if (externFactory->translateChildObject(compiler, *i))
				{
					// Parsed the object by another translator; do nothing
				}
				else
				{
					processNode(compiler, *i);
				}
			}
            else
			{
				errorUnexpectedToken(compiler, *i);
			}
		}
	}
	//-----------------------------------------------------------------------
	void DoEnableComponentEventHandler::_handle (ParticleTechnique* particleTechnique, Particle* particle, Real timeElapsed)
	{
		/** Search for the component.
		*/
		ParticleTechnique* technique = 0;
		switch (mComponentType)
		{
			case CT_EMITTER:
			{
				ParticleEmitter* emitter = particleTechnique->getEmitter(mComponentName);
				if (!emitter)
				{
					// Search all techniques in this ParticleSystem for an emitter with the correct name
					ParticleSystem* system = particleTechnique->getParentSystem();
					size_t size = system->getNumTechniques();
					for(size_t i = 0; i < size; ++i)		
					{
						technique = system->getTechnique(i);
						emitter = technique->getEmitter(mComponentName);
						if (emitter)
						{
							break;
						}
					}
				}
				if (emitter)
				{
					emitter->setEnabled(mComponentEnabled);
				}
			}
			break;

			case CT_AFFECTOR:
			{
				ParticleAffector* affector = particleTechnique->getAffector(mComponentName);
				if (!affector)
				{
					// Search all techniques in this ParticleSystem for an affector with the correct name
					ParticleSystem* system = particleTechnique->getParentSystem();
					size_t size = system->getNumTechniques();
					for(size_t i = 0; i < size; ++i)
					{
						technique = system->getTechnique(i);
						affector = technique->getAffector(mComponentName);
						if (affector)
						{
							break;
						}
					}
				}
				if (affector)
				{
					affector->setEnabled(mComponentEnabled);
				}
			}
			break;

			case CT_OBSERVER:
			{
				ParticleObserver* observer = particleTechnique->getObserver(mComponentName);
				if (!observer)
				{
					// Search all techniques in this ParticleSystem for an observer with the correct name
					ParticleSystem* system = particleTechnique->getParentSystem();
					size_t size = system->getNumTechniques();
					for(size_t i = 0; i < size; ++i)		
					{
						technique = system->getTechnique(i);
						observer = technique->getObserver(mComponentName);
						if (observer)
						{
							break;
						}
					}
				}
				if (observer)
				{
					observer->setEnabled(mComponentEnabled);
				}
			}
			break;

			case CT_TECHNIQUE:
			{
				// Search in this ParticleSystem for a technique with the correct name
				ParticleSystem* system = particleTechnique->getParentSystem();
				technique = system->getTechnique(mComponentName);
				if (technique)
				{
					technique->setEnabled(mComponentEnabled);
				}
			}
			break;
		}
	}