/*
This file is part of Caelum.
See http://www.ogre3d.org/wiki/index.php/Caelum 

Copyright (c) 2006-2008 Caelum team. See Contributors.txt for details.

Caelum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Caelum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Caelum. If not, see <http://www.gnu.org/licenses/>.
*/

#include "CaelumPrecompiled.h"
#include "CaelumPrerequisites.h"

#if CAELUM_SCRIPT_SUPPORT

#include "CaelumScriptTranslator.h"
#include "CaelumSystem.h"
#include "CaelumExceptions.h"

using namespace Ogre;

namespace Caelum
{
    PropScriptResource::PropScriptResource
    (
        Ogre::ResourceManager* creator, const Ogre::String& name, Ogre::ResourceHandle handle,
        const Ogre::String& group, bool isManual, Ogre::ManualResourceLoader* loader
    ):
        Ogre::Resource (creator, name, handle, group, isManual, loader)
    {
        //Ogre::LogManager::getSingleton().logMessage(
        //        "PropScriptResource::PropScriptResource");
    }

    PropScriptResource::~PropScriptResource() {
        //Ogre::LogManager::getSingleton().logMessage(
        //        "PropScriptResource::~PropScriptResource");
    }

    PropScriptResourceManager::PropScriptResourceManager() {
        mLoadOrder = 1000;
        mResourceType = "PropertyScript";
    }

    PropScriptResource* PropScriptResourceManager::createImpl(
                const String& name, ResourceHandle handle, const String& group,
                bool isManual, ManualResourceLoader* loader, const NameValuePairList* createParams)
    {
        //Ogre::LogManager::getSingleton().logMessage(
        //        "PropScriptResourceManager::createImpl");
        return new PropScriptResource (this, name, handle, group, isManual, loader);
    }

    TypeDescriptorScriptTranslator::TypeDescriptorScriptTranslator (TypeDescriptor* typeDescriptor):
            mTypeDescriptor(typeDescriptor)
    {
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            bool& value)
    {
        if (prop->values.empty ()) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 1) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 1 argument");
            return false;
        } 
        if (!Ogre::ScriptTranslator::getBoolean(prop->values.front(), &value)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                        prop->values.front()->getValue() + " is not a valid number");
            return false;
        }
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            ColourValue& value)
    {
        if (prop->values.empty ()) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 4) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 4 arguments");
            return false;
        }
        if (prop->values.size () < 3) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at least 3 arguments");
        }
        if (!getColour(prop->values.begin(), prop->values.end(), &value)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                    prop->name + " requires a colour argument");
        }
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            float& value)
    {
        if (prop->values.empty ()) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 1) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 1 argument");
            return false;
        } 
        if (!Ogre::ScriptTranslator::getFloat(prop->values.front(), &value)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                        prop->values.front()->getValue() + " is not a valid number");
            return false;
        }
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            int& value)
    {
        if (prop->values.empty ()) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 1) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 1 argument");
            return false;
        } 
        if (!Ogre::ScriptTranslator::getInt(prop->values.front(), &value)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                        prop->values.front()->getValue() + " is not a valid integer");
            return false;
        }
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            double& value)
    {
        if (prop->values.empty ()) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 1) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 1 argument");
            return false;
        } 
        // We do need a string stream here for the extra precision.
        std::stringstream strStream (std::string(prop->values.front()->getValue()));
        strStream >> value;
        if (strStream.fail()) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                    prop->values.front()->getValue() + " is not a valid number");
            return false;
        }
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            Ogre::Degree& value)
    {
        if (prop->values.size () == 0) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 3) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 3 arguments");
            return false;
        } 
        // Allow 3 components.
        float degMinSec[3] = { 0, 0, 0 };
        int k = 0;
        for (AbstractNodeList::const_iterator it = prop->values.begin(), endIt = prop->values.end(); it != endIt; ++it, ++k) {
            if (!Ogre::ScriptTranslator::getFloat(*it, &degMinSec[k])) {
                compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                        (*it)->getValue () + " is not a valid number");
                return false;
            }
        }
        value = Ogre::Degree(degMinSec[0] + degMinSec[1] / 60.0 + degMinSec[2] / 3600);
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            Ogre::String& value)
    {
        if (prop->values.size () == 0) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 1) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 1 arguments");
            return false;
        } 
        if (!Ogre::ScriptTranslator::getString(prop->values.front(), &value)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
                        prop->values.front()->getValue() + " is not a valid string");
            return false;
        }
        return true;
    }

    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            Ogre::Vector3& value)
    {
        if (prop->values.size () == 0) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 3) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 3 arguments");
            return false;
        } 
        float floats[3];
        if (!Ogre::ScriptTranslator::getFloats(prop->values.begin(), prop->values.end(), floats, 3)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,         
                    "incorrect vector parameters.");
            return false;
        }
        value.x = floats[0];
        value.y = floats[1];
        value.z = floats[2];
        return true;
    }
 
    bool TypeDescriptorScriptTranslator::getPropValueOrAddError (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            Ogre::Vector2& value)
    {
        if (prop->values.size () == 0) {
            compiler->addError (ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
            return false;
        }
        if (prop->values.size () > 2) {
            compiler->addError (ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
                    prop->name + " must have at most 3 arguments");
            return false;
        } 
        float floats[2];
        if (!Ogre::ScriptTranslator::getFloats(prop->values.begin(), prop->values.end(), floats, 2)) {
            compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,         
                    "incorrect vector parameters.");
            return false;
        }
        value.x = floats[0];
        value.y = floats[1];
        return true;
    }

    template<class T>
    static bool tryHandlePropertyType (
            ScriptCompiler* compiler, PropertyAbstractNode* propNode,
            void* targetObject, const ValuePropertyDescriptor* propDesc)
    {
        if (propDesc->getValueTypeId () == typeid(T)) {
            T val;
            if (TypeDescriptorScriptTranslator::getPropValueOrAddError (compiler, propNode, val)) {
                propDesc->setValue (targetObject, Ogre::Any(val));
            }
            return true;
        }
        return false;
    }

    void TypeDescriptorScriptTranslator::translateProperty (
            ScriptCompiler* compiler,
            PropertyAbstractNode* prop,
            void* targetObject,
            const TypeDescriptor* typeDescriptor)
    {
        const ValuePropertyDescriptor* propDesc = typeDescriptor->getPropertyDescriptor (prop->name);
        if (!propDesc) {
            compiler->addError (ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, 
                    "property \"" + prop->name + "\" not recognized; missing from type descriptor.");
            return;
        }
        if (!propDesc->canSetValue ()) {
            compiler->addError (ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, 
                    "property \"" + prop->name + "\" is read-only and can't be set from a script.");
            return;
        }

        //LogManager::getSingleton ().logMessage ("TypeDescriptorScriptTranslator::translateProperty"
        //            " name '" + prop->name + "'");

        bool handled = false
                || tryHandlePropertyType<int> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<float> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<double> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<bool> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<Ogre::Degree> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<Ogre::String> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<Ogre::Vector3> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<Ogre::Vector2> (compiler, prop, targetObject, propDesc)
                || tryHandlePropertyType<Ogre::ColourValue> (compiler, prop, targetObject, propDesc);

        if (!handled) {
            compiler->addError (ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, 
                    "property \"" + prop->name + "\" is found in type descriptor but has "
                    "unsupported type. Mangled typeid is '" + propDesc->getValueTypeId().name() + "'");
        }
    }

    void TypeDescriptorScriptTranslator::translate (ScriptCompiler* compiler, const AbstractNodePtr& node)
    {
        //LogManager::getSingleton ().logMessage ("TypeDescriptorScriptTranslator::translate begin");

        // Check type descriptor was set.
        assert (getTypeDescriptor () && "Type descriptor must be set before we can translate.");

        // Fetch target object.
		ObjectAbstractNode *objNode = reinterpret_cast<ObjectAbstractNode*>(node.get());
        assert (!objNode->context.isEmpty ());
        void* targetObject = any_cast<void*> (objNode->context);
        assert (targetObject);

		for (AbstractNodeList::iterator i = objNode->children.begin(); i != objNode->children.end(); ++i)
		{
			if ((*i)->type == ANT_PROPERTY)
            {
				PropertyAbstractNode *prop = reinterpret_cast<PropertyAbstractNode*>((*i).get());

                translateProperty (compiler, prop, targetObject, getTypeDescriptor());
			}
			else if((*i)->type == ANT_OBJECT)
			{
                compiler->addError (ScriptCompiler::CE_INVALIDPARAMETERS, (*i)->file, (*i)->line);
            }
        }

        //LogManager::getSingleton ().logMessage ("TypeDescriptorScriptTranslator::translate end");
    }

    CaelumSystemScriptTranslator::CaelumSystemScriptTranslator ():
            mResourceManager(false),
            mTranslationTarget(0),
            mTranslationTargetFound(false),
            mTypeDescriptor(0)
    {
    }

    void CaelumSystemScriptTranslator::setTranslationTarget (CaelumSystem* target, const Ogre::String& name)
    {
        assert (target != 0);
        this->mTranslationTarget = target;
        this->mTranslationTargetName = name;
        this->mTranslationTargetFound = false;
    }

    void CaelumSystemScriptTranslator::clearTranslationTarget () {
        this->mTranslationTarget = 0;
        this->mTranslationTargetName.clear();
        this->mTranslationTargetFound = false;
    }

    void CaelumSystemScriptTranslator::translate (ScriptCompiler* compiler, const AbstractNodePtr& node)
    {
        //LogManager::getSingleton ().logMessage ("CaelumSystemScriptTranslator::translate begin");

		ObjectAbstractNode *objNode = reinterpret_cast<ObjectAbstractNode*>(node.get());

        CaelumSystem* sys = 0;

        // Check for a translation target.
        if (this->getTranslationTarget ()) {
            sys = this->getTranslationTarget ();

            // Check for a name match.
            if (this->getTranslationTargetName () != objNode->name) {
                //LogManager::getSingleton ().logMessage (
                //        "Caelum: Skipped " + objNode->cls + " name " + objNode->name + " while loading");
                return;
            }
            
            // Clear the target; this ensure that properties which are not
            // mentioned are set to their default values.
            // We only do this after we found a target; this ensure that if
            // the target is not found it's not modified either.
            sys->clear();

            //LogManager::getSingleton ().logMessage (
            //        "Caelum: Found " + objNode->cls + " name " + objNode->name + "; filling properties.");
            mTranslationTargetFound = true;
        } else if (this->getResourceManager ()) {
            // If we don't have a target but have a resource manager then create a resource.
            //LogManager::getSingleton ().logMessage (
            //        "Caelum: Saved " + objNode->cls + " name " + objNode->name + " as a resource");
            PropScriptResourceManager* mgr = this->getResourceManager ();
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
            ResourcePtr resource = mgr->createResource (objNode->name, compiler->getResourceGroup());
#else
            ResourcePtr resource = mgr->create (objNode->name, compiler->getResourceGroup());
#endif
            resource->_notifyOrigin (objNode->file);
            return;
        }

        objNode->context = sys;

		for (AbstractNodeList::iterator i = objNode->children.begin(); i != objNode->children.end(); ++i)
		{
			if ((*i)->type == ANT_PROPERTY)
            {
				PropertyAbstractNode *prop = reinterpret_cast<PropertyAbstractNode*>((*i).get());

                // Properties implemented through type descriptor.
                TypeDescriptorScriptTranslator::translateProperty(
                        compiler, prop,
                        static_cast<void*>(sys),
                        getTypeDescriptor ());
			}
			else if((*i)->type == ANT_OBJECT)
			{
				ObjectAbstractNode *childObjNode = reinterpret_cast<ObjectAbstractNode*>((*i).get());

                //LogManager::getSingleton ().logMessage ("CaelumSystemScriptTranslator::translate child object"
                //        " value '" + childObjNode->getValue () + "'"
                //        " name '" + childObjNode->name + "'"
                //        " cls '" + childObjNode->cls + "'"
                //        " base '" + childObjNode->base + "'");
                
                // Only allow declarations with one class token; like "moon { }"
#if OGRE_VERSION < 0x010700
                if (childObjNode->name.empty () == false || childObjNode->base.empty () == false) {
#else
                if (childObjNode->name.empty () == false || childObjNode->bases.size () != 0) {
#endif
                    compiler->addError (
                            ScriptCompiler::CE_FEWERPARAMETERSEXPECTED,
                            childObjNode->file, childObjNode->line,
                            "caelum_sky_system components can't have names or bases");
                    continue;
                }
                const String& className = childObjNode->cls;

                try {
                    if (className == "sun") {
                        sys->setSun (new Sun (sys->getSceneMgr (), sys->getCaelumCameraNode ()));
                        childObjNode->context = static_cast<void*> (sys->getSun ());
                    } else if (className == "sky_dome") {
                        sys->setSkyDome (new SkyDome (sys->getSceneMgr (), sys->getCaelumCameraNode ()));
                        childObjNode->context = static_cast<void*>(sys->getSkyDome ());
                    } else if (className == "moon") {
                        sys->setMoon (new Moon (sys->getSceneMgr (), sys->getCaelumCameraNode ()));
                        childObjNode->context = static_cast<void*>(sys->getMoon ());
                    } else if (className == "ground_fog") {
                        sys->setGroundFog (new GroundFog (sys->getSceneMgr (), sys->getCaelumCameraNode ()));
                        childObjNode->context = static_cast<void*>(sys->getGroundFog ());
                    } else if (className == "depth_composer") {
                        sys->setDepthComposer (new DepthComposer (sys->getSceneMgr ()));
                        childObjNode->context = static_cast<void*>(sys->getDepthComposer ());
                    } else if (className == "point_starfield") {
                        sys->setPointStarfield (new PointStarfield (sys->getSceneMgr (), sys->getCaelumCameraNode()));
                        childObjNode->context = static_cast<void*>(sys->getPointStarfield ());
                    } else if (className == "precipitation") {
                        sys->setPrecipitationController (new PrecipitationController (sys->getSceneMgr ()));
                        childObjNode->context = static_cast<void*>(sys->getPrecipitationController ());
                    } else if (className == "cloud_system") {
                        sys->setCloudSystem (new CloudSystem (sys->getSceneMgr (), sys->getCaelumGroundNode ()));
                        childObjNode->context = static_cast<void*>(sys->getCloudSystem ());
                    } else {
                        LogManager::getSingleton ().logMessage ("CaelumSystemScriptTranslator::translate "
                                "unknown child object class '" + className + "'");
                    }
                } catch (Caelum::UnsupportedException& ex) {
                    // Catch all unsupported exceptions and report them.
                    // This should usually happen because the proper shaders are not supported by hardware.
                    //
                    // Script parsing should still succeed.
                    compiler->addError (
                            ScriptCompiler::CE_UNSUPPORTEDBYRENDERSYSTEM,
                            childObjNode->file, childObjNode->line,
                            "Failed to create component \"" + className + "\": " + ex.getFullDescription ());
                    continue;
                }
                processNode (compiler, *i);
            }
        }

        //LogManager::getSingleton ().logMessage ("SkySystemScriptTranslator::translate END");
    }

    void CloudSystemScriptTranslator::translate (ScriptCompiler* compiler, const AbstractNodePtr& node)
    {
        //LogManager::getSingleton ().logMessage ("SkySystemScriptTranslator::translate begin");

		ObjectAbstractNode *objNode = reinterpret_cast<ObjectAbstractNode*>(node.get());
        assert (!objNode->context.isEmpty ());
        void* rawTargetObject = any_cast<void*> (objNode->context);
        assert (rawTargetObject);

        CloudSystem* target = static_cast<CloudSystem*>(rawTargetObject);

		for (AbstractNodeList::iterator i = objNode->children.begin(); i != objNode->children.end(); ++i)
		{
			if ((*i)->type == ANT_PROPERTY)
            {
                compiler->addError (
                            ScriptCompiler::CE_INVALIDPARAMETERS,
                            objNode->file, objNode->line,
                            "cloud_system doesn't have any properties");
			}
			else if((*i)->type == ANT_OBJECT)
			{
				ObjectAbstractNode *childObjNode = reinterpret_cast<ObjectAbstractNode*>((*i).get());

                /*
                LogManager::getSingleton ().logMessage ("CloudSystemScriptTranslator::translate child object"
                        " value '" + childObjNode->getValue () + "'"
                        " name '" + childObjNode->name + "'"
                        " cls '" + childObjNode->cls + "'"
                        " base '" + childObjNode->base + "'");
                 */

                const Ogre::String& className = childObjNode->cls;
        
                if (className == "cloud_layer") {
                    // Don't allow names.
#if OGRE_VERSION < 0x010700
                    if (childObjNode->base.empty () == false) {
#else
                    if (childObjNode->bases.size () != 0) {
#endif
                        compiler->addError (
                                ScriptCompiler::CE_FEWERPARAMETERSEXPECTED,
                                childObjNode->file, childObjNode->line,
                                "cloud_layer can't have a base");
                        continue;
                    }
                    // Height here is irrelevant. It's silly to have it as a FlatCloudLayer ctor parameter.
                    target->createLayerAtHeight (0);
                    FlatCloudLayer* layer = target->getLayer (target->getLayerCount () - 1);

                    // Add the new layer as a context for the object node.
                    // This will eventually pass to the TypeDescriptorScriptTranslator for a cloud layer.
                    childObjNode->context = static_cast<void*>(layer);
                } else {
                    LogManager::getSingleton ().logMessage ("CloudSystemScriptTranslator::translate "
                            "unknown child object class '" + className + "'");
                }
                processNode (compiler, *i);
            }
        }

        //LogManager::getSingleton ().logMessage ("CloudSystemScriptTranslator::translate END");
    }

    CaelumScriptTranslatorManager::CaelumScriptTranslatorManager
    (
        CaelumDefaultTypeDescriptorData* typeData
    ):
        mCaelumSystemTranslator(),
        mCloudSystemTranslator(),
        mFlatCloudLayerTranslator(typeData->FlatCloudLayerTypeDescriptor),
        mSunTranslator(typeData->BaseSkyLightTypeDescriptor),
        mMoonTranslator(typeData->BaseSkyLightTypeDescriptor),
        mPointStarfieldTranslator(typeData->PointStarfieldTypeDescriptor),
        mGroundFogTranslator(typeData->GroundFogTypeDescriptor),
        mDepthComposerTranslator(typeData->DepthComposerTypeDescriptor),
        mPrecipitationTranslator(typeData->PrecipitationTypeDescriptor),
        mSkyDomeTranslator(typeData->SkyDomeTypeDescriptor)
    {
        mCaelumSystemTranslator.setTypeDescriptor(typeData->CaelumSystemTypeDescriptor);

        // Build translator map to member translators.
        mTranslatorMap.insert (std::make_pair ("caelum_sky_system", &mCaelumSystemTranslator));
        mTranslatorMap.insert (std::make_pair ("cloud_system", &mCloudSystemTranslator));
        mTranslatorMap.insert (std::make_pair ("cloud_layer", &mFlatCloudLayerTranslator));
        mTranslatorMap.insert (std::make_pair ("sun", &mSunTranslator));
        mTranslatorMap.insert (std::make_pair ("moon", &mMoonTranslator));
        mTranslatorMap.insert (std::make_pair ("point_starfield", &mPointStarfieldTranslator));
        mTranslatorMap.insert (std::make_pair ("ground_fog", &mGroundFogTranslator));
        mTranslatorMap.insert (std::make_pair ("depth_composer", &mDepthComposerTranslator));
        mTranslatorMap.insert (std::make_pair ("precipitation", &mPrecipitationTranslator));
        mTranslatorMap.insert (std::make_pair ("sky_dome", &mSkyDomeTranslator));
    }

    size_t CaelumScriptTranslatorManager::getNumTranslators () const {
        // Turns out this is never called.
        assert(0 && "This method should be removed from Ogre::ScriptTranslatorManager");
        return mTranslatorMap.size ();
    }

    void CaelumScriptTranslatorManager::_setPropScriptResourceManager (PropScriptResourceManager* mgr)
    {
        mCaelumSystemTranslator.setResourceManager (mgr);
    }

    ScriptTranslator* CaelumScriptTranslatorManager::getTranslator (const AbstractNodePtr& node)
    {
        //LogManager::getSingleton ().logMessage ("CaelumScriptTranslatorManager::getTranslator");
        if (node->type == ANT_ATOM) {
            //ObjectAbstractNode* atomNode = reinterpret_cast<ObjectAbstractNode*>(node.get());
            //LogManager::getSingleton ().logMessage ("CaelumScriptTranslatorManager::getTranslator atom node " + atomNode->getValue ());
        } else if (node->type == ANT_OBJECT) {
            ObjectAbstractNode* objNode = reinterpret_cast<ObjectAbstractNode*>(node.get());
            //LogManager::getSingleton ().logMessage ("CaelumScriptTranslatorManager::getTranslator object node " + objNode->getValue ());

            // Pass down the context.
            ScriptTranslatorMap::const_iterator it = mTranslatorMap.find(objNode->cls);
            if (it != mTranslatorMap.end()) {
                return it->second;
            }
        }

        // Not found in this manager.
        return 0;
    }
}

#endif // CAELUM_SCRIPT_SUPPORT
