/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield 
 *
 * This software is open source and may be redistributed and/or modified under  
 * the terms of the GNU General Public License (GPL) version 2.0.
 * The full license is in LICENSE.txt file included with this distribution,.
 * 
 * This software 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 
 * include LICENSE.txt for more details.
*/

#include <osgPresentation/SlideEventHandler>
#include <osgPresentation/SlideShowConstructor>

#include <osg/AnimationPath>
#include <osg/Transform>
#include <osg/TexEnvCombine>
#include <osg/LightSource>
#include <osg/AlphaFunc>
#include <osg/io_utils>

#include <osgUtil/TransformCallback>
#include <osgUtil/GLObjectsVisitor>

#include <osgGA/AnimationPathManipulator>

#include <osgPresentation/AnimationMaterial>

#include <iostream>

using namespace osgPresentation;

static osg::observer_ptr<SlideEventHandler> s_seh;

SlideEventHandler* SlideEventHandler::instance() { return s_seh.get(); }

void LayerAttributes::callEnterCallbacks(osg::Node* node)
{
    osg::notify(osg::INFO)<<"LayerAttributes::callEnterCallbacks("<<node<<")"<<std::endl;
    for(LayerCallbacks::iterator itr = _enterLayerCallbacks.begin();
        itr != _enterLayerCallbacks.end();
        ++itr)
    {
        (*(*itr))(node);
    }
}

void LayerAttributes::callLeaveCallbacks(osg::Node* node)
{
    osg::notify(osg::INFO)<<"LayerAttributes::callLeaveCallbacks("<<node<<")"<<std::endl;
    for(LayerCallbacks::iterator itr = _leaveLayerCallbacks.begin();
        itr != _leaveLayerCallbacks.end();
        ++itr)
    {
        (*(*itr))(node);
    }
}


struct ImageStreamOperator : public ObjectOperator
{
    ImageStreamOperator(osg::ImageStream* imageStream):
        _imageStream(imageStream) {}

    virtual void* ptr() const { return _imageStream.get(); }

    virtual void enter()
    {
        osg::notify(osg::INFO)<<"enter() : _imageStream->rewind() + play"<<std::endl;

        reset();
    }
    
    virtual void maintain()
    {
    }
    
    virtual void leave()
    {
       osg::notify(osg::INFO)<<"leave() : _imageStream->pause()"<<std::endl;

        _imageStream->pause();
    }

    virtual void setPause(bool pause)
    {
       osg::notify(osg::INFO)<<"_imageStream->setPause("<<pause<<")"<<std::endl;

        if (pause) _imageStream->pause();
        else _imageStream->play();
    }
    
    virtual void reset()
    {
        osg::ImageStream::StreamStatus previousStatus = _imageStream->getStatus();

        _imageStream->rewind();


        //_imageStream->setVolume(previousVolume);
        
        if(previousStatus==osg::ImageStream::PLAYING)
        {
            _imageStream->play();
        }

        // add a delay so that movie thread has a chance to do the rewind    
        float microSecondsToDelay = SlideEventHandler::instance()->getTimeDelayOnNewSlideWithMovies() * 1000000.0f;
        OpenThreads::Thread::microSleep(static_cast<unsigned int>(microSecondsToDelay));
    }

    osg::ref_ptr<osg::ImageStream>  _imageStream;
};

struct CallbackOperator : public ObjectOperator
{
    CallbackOperator(osg::Node* node, osg::Referenced* callback):
        _node(node),
        _callback(callback) {}

    virtual void* ptr() const { return _callback.get(); }

    virtual void enter()
    {
        reset();
    }
    
    virtual void maintain()
    {
    }
    
    virtual void leave()
    {
    }

    virtual void setPause(bool pause)
    {
        osg::AnimationPathCallback* apc = dynamic_cast<osg::AnimationPathCallback*>(_callback.get());
        osgUtil::TransformCallback* tc = dynamic_cast<osgUtil::TransformCallback*>(_callback.get());
        AnimationMaterialCallback* amc = dynamic_cast<AnimationMaterialCallback*>(_callback.get());
        if (apc)
        {
            osg::notify(osg::INFO)<<"apc->setPause("<<pause<<")"<<std::endl;
            apc->setPause(pause);
        }
        if (tc)
        {
            osg::notify(osg::INFO)<<"tc->setPause("<<pause<<")"<<std::endl;
            tc->setPause(pause);
        }
        if (amc)
        {
            osg::notify(osg::INFO)<<"amc->setPause("<<pause<<")"<<std::endl;
            amc->setPause(pause);
        }
    }

    virtual void reset()
    {
        osg::AnimationPathCallback* apc = dynamic_cast<osg::AnimationPathCallback*>(_callback.get());
        osgUtil::TransformCallback* tc = dynamic_cast<osgUtil::TransformCallback*>(_callback.get());
        AnimationMaterialCallback* amc = dynamic_cast<AnimationMaterialCallback*>(_callback.get());
        if (apc)
        {
            apc->reset();
            apc->update(*_node);
        }
        if (tc)
        {
        }
        if (amc)
        {
            amc->reset();
            amc->update(*_node);
        }
    }


    osg::ref_ptr<osg::Node>         _node;
    osg::ref_ptr<osg::Referenced>   _callback;
};

struct LayerAttributesOperator : public ObjectOperator
{
    LayerAttributesOperator(osg::Node* node, LayerAttributes* la):
        _node(node),
        _layerAttribute(la)
    {
    }

    virtual void* ptr() const { return _layerAttribute.get(); }

    virtual void enter()
    {
        _layerAttribute->callEnterCallbacks(_node.get());

        if (!_layerAttribute->_keys.empty())
        {
            osg::notify(osg::INFO)<<"applyKeys {"<<std::endl;

            for(LayerAttributes::Keys::iterator itr = _layerAttribute->_keys.begin();
                itr != _layerAttribute->_keys.end();
                ++itr)
            {
                SlideEventHandler::instance()->dispatchEvent(*itr);
            }

            osg::notify(osg::INFO)<<"}"<<std::endl;
        }   
        if (!_layerAttribute->_runStrings.empty())
        {
            for(LayerAttributes::RunStrings::iterator itr = _layerAttribute->_runStrings.begin();
                itr != _layerAttribute->_runStrings.end();
                ++itr)
            {

                osg::notify(osg::NOTICE)<<"Run "<<itr->c_str()<<std::endl;
                osg::Timer_t startTick = osg::Timer::instance()->tick();

                int result = system(itr->c_str());

                osg::notify(osg::INFO)<<"system("<<*itr<<") result "<<result<<std::endl;

                double timeForRun = osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick());

                osgGA::EventQueue* eq = SlideEventHandler::instance()->getViewer()->getEventQueue();
                if (eq)
                {
                    osg::Timer_t new_startTick = eq->getStartTick() + osg::Timer_t(timeForRun / osg::Timer::instance()->getSecondsPerTick());
                    eq->setStartTick(new_startTick);
                }
            }
        }

    }
    
    virtual void maintain()
    {
    }
    
    virtual void leave()
    {
        osg::notify(osg::INFO)<<"LayerAttribute leave"<<std::endl;

         _layerAttribute->callLeaveCallbacks(_node.get());
    }

    virtual void setPause(bool pause)
    {
    }

    virtual void reset()
    {
    }


    osg::ref_ptr<osg::Node>                             _node;
    osg::ref_ptr<LayerAttributes> _layerAttribute;
};


class FindOperatorsVisitor : public osg::NodeVisitor
{
public:
    FindOperatorsVisitor(ActiveOperators::OperatorList& operatorList, osg::NodeVisitor::TraversalMode tm):
        osg::NodeVisitor(tm),
        _operatorList(operatorList) {}

    void apply(osg::Node& node)
    {
        if (node.getStateSet()) process(node.getStateSet());

        if (node.getUpdateCallback())
        {
            _operatorList.insert(new CallbackOperator(&node, node.getUpdateCallback()));
        }

        LayerAttributes* la = dynamic_cast<LayerAttributes*>(node.getUserData());
        if (la)
        {
            _operatorList.insert(new LayerAttributesOperator(&node, la));
        }

        traverse(node);
    }

    void apply(osg::Geode& node)    
    {
        apply((osg::Node&)node);
        for(unsigned int i=0;i<node.getNumDrawables();++i)
        {
            osg::Drawable* drawable = node.getDrawable(i);
            if (drawable->getStateSet()) process(drawable->getStateSet());
        }
    }

    virtual void process(osg::StateSet* ss)
    {
        for(unsigned int i=0;i<ss->getTextureAttributeList().size();++i)
        {
            osg::Texture* texture = dynamic_cast<osg::Texture*>(ss->getTextureAttribute(i,osg::StateAttribute::TEXTURE));
            osg::Image* image = texture ? texture->getImage(0) : 0;
            osg::ImageStream* imageStream = image ? dynamic_cast<osg::ImageStream*>(image) : 0;
            if (imageStream)
            {
                _operatorList.insert(new ImageStreamOperator(imageStream));
            }
        }
    }

    ActiveOperators::OperatorList& _operatorList;
};


ActiveOperators::ActiveOperators():
    _pause(false)
{
}

ActiveOperators::~ActiveOperators()
{
}

void ActiveOperators::collect(osg::Node* incommingNode, osg::NodeVisitor::TraversalMode tm)
{
    _previous.swap(_current);
    
    _current.clear();
    
    FindOperatorsVisitor fov(_current, tm);
    incommingNode->accept(fov);
    
    osg::notify(osg::INFO)<<"ActiveOperators::collect("<<incommingNode<<")"<<std::endl;
    osg::notify(osg::INFO)<<"  _previous.size()="<<_previous.size()<<std::endl;
    osg::notify(osg::INFO)<<"  _current.size()="<<_current.size()<<std::endl;
    
    _outgoing.clear();
    _incomming.clear();
    _maintained.clear();
    
    for(OperatorList::iterator itr = _previous.begin();
        itr != _previous.end();
        ++itr)
    {
        ObjectOperator* prev = itr->get();
        if (_current.count(prev)==0) _outgoing.insert(prev);
        else _maintained.insert(prev);
    }
    
    for(OperatorList::iterator itr = _current.begin();
        itr != _current.end();
        ++itr)
    {
        ObjectOperator* curr = itr->get();
        if (_previous.count(curr)==0) _incomming.insert(curr);
    }
}

void ActiveOperators::setPause(bool pause)
{
    _pause = pause;
    for(OperatorList::iterator itr = _current.begin();
        itr != _current.end();
        ++itr)
    {
        (*itr)->setPause(_pause);
    }
}


void ActiveOperators::reset()
{
    for(OperatorList::iterator itr = _current.begin();
        itr != _current.end();
        ++itr)
    {
        (*itr)->reset();
    }
}

void ActiveOperators::process()
{
    processOutgoing();
    processMaintained();
    processIncomming();
}

void ActiveOperators::processOutgoing()
{
    osg::notify(osg::INFO)<<"  outgoing.size()="<<_outgoing.size()<<std::endl;    
    for(OperatorList::iterator itr = _outgoing.begin();
        itr != _outgoing.end();
        ++itr)
    {
        (*itr)->leave();
    }
}

void ActiveOperators::processMaintained()
{
    osg::notify(osg::INFO)<<"  maintained.size()="<<_maintained.size()<<std::endl;
    for(OperatorList::iterator itr = _maintained.begin();
        itr != _maintained.end();
        ++itr)
    {
        (*itr)->maintain();
    }
}

void ActiveOperators::processIncomming()
{
    osg::notify(osg::INFO)<<"  incomming.size()="<<_incomming.size()<<std::endl;
    for(OperatorList::iterator itr = _incomming.begin();
        itr != _incomming.end();
        ++itr)
    {
        (*itr)->enter();
        (*itr)->setPause(_pause);
    }
}




class FindHomePositionVisitor : public osg::NodeVisitor
{
public:

    FindHomePositionVisitor():
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) {}
        
    void apply(osg::Node& node)
    {
        HomePosition* homePosition = dynamic_cast<HomePosition*>(node.getUserData());
        if (homePosition)
        {
            _homePosition = homePosition;
        }
        
        traverse(node);
    }
        
    osg::ref_ptr<HomePosition> _homePosition;
        
};

class FindNamedSwitchVisitor : public osg::NodeVisitor
{
public:

    FindNamedSwitchVisitor(const std::string& name):
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
        _name(name),
        _switch(0) {}
        
    void apply(osg::Switch& sw)
    {
        if (sw.getName().find(_name)!=std::string::npos)
        {
            _switch = &sw;
            return; // note, no need to do traverse now we've located the relevant switch
        }
        
        traverse(sw);
    }
        
    std::string     _name;
    osg::Switch*    _switch;
        
};


class FindFilePathDataVisitor : public osg::NodeVisitor
{
public:

    FindFilePathDataVisitor():
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) {}
        
    void apply(osg::Node& node)
    {
        FilePathData* fdd = dynamic_cast<FilePathData*>(node.getUserData());
        if (fdd) 
        {
            osg::notify(osg::INFO)<<"Recorded FilePathData"<<std::endl;
            osgDB::setDataFilePathList(fdd->filePathList);
        }
        
        traverse(node);
    }
        
};

class UpdateLightVisitor : public osg::NodeVisitor
{
public:

    UpdateLightVisitor(const osg::Matrixd& viewMatrix, float currentX, float currentY):
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
        _viewMatrix(viewMatrix),
        _currentX(currentX), _currentY(currentY) {}
        
    void apply(osg::Node& node)
    {
        if (node.getStateSet()) 
        {
            apply(*node.getStateSet());
        }
        
        traverse(node);
    }
    
    void apply(osg::LightSource& lightsource)
    {
        if (lightsource.getStateSet()) 
        {
            apply(*lightsource.getStateSet());
        }
    
        if (lightsource.getLight())
        {
            osg::notify(osg::INFO)<<"Adjusting light"<<std::endl;

            osg::Light* light = lightsource.getLight();

            float azim = _currentX*osg::PI;
            float elevation = _currentY*osg::PI_2;
            osg::Vec3 direction(sin(azim)*cos(elevation),sin(elevation),cos(azim)*cos(elevation));
            
            if (lightsource.getReferenceFrame()==osg::LightSource::RELATIVE_RF)
            {
                osg::notify(osg::INFO)<<"Relative to absolute"<<std::endl;
            }
            else
            {
                osg::Matrix matrix(osg::computeEyeToLocal(_viewMatrix,_nodePath));
                osg::notify(osg::INFO)<<"ModelView"<<matrix<<std::endl;

                //direction = osg::Matrixd::transform3x3(matrix,direction);
                //direction.normalize();

                //direction = direction*matrix;
                //direction.normalize();

            }

            light->setPosition(osg::Vec4(direction,0.0f));

        }
        
        traverse(lightsource);
    }

    void apply(osg::StateSet& stateset)
    {
        osg::TexEnvCombine* texenvcombine = dynamic_cast<osg::TexEnvCombine*>(stateset.getTextureAttribute(0,osg::StateAttribute::TEXENV));
        if (texenvcombine)
        {
            apply(*texenvcombine);
        }
    }
       
    void apply(osg::TexEnvCombine& texenv)
    {
        osg::notify(osg::INFO)<<"Adjusting tex env combine"<<std::endl;
        
        osg::Matrix matrix(osg::computeEyeToLocal(_viewMatrix,_nodePath));
        
        osg::notify(osg::INFO)<<"ModelView"<<matrix<<std::endl;

        float azim = _currentX*osg::PI;
        float elevation = _currentY*osg::PI_2;
        osg::Vec3 direction(sin(azim)*cos(elevation),sin(elevation),cos(azim)*cos(elevation));

        direction = osg::Matrixd::transform3x3(matrix,direction);
        direction.normalize();

        texenv.setConstantColor(osg::Vec4((direction.x()+1.0f)*0.5f,(direction.y()+1.0f)*0.5f,(direction.z()+1.0f)*0.5f,1.0f));
    }


    osg::Matrixd    _viewMatrix;
    float           _currentX, _currentY;
        
};

class UpdateAlphaVisitor : public osg::NodeVisitor
{
public:

    UpdateAlphaVisitor(bool modAlphaFunc, bool modMaterial, float currentX, float currentY):
        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
        _modAlphaFunc(modAlphaFunc), _modMaterial(modMaterial),
        _currentX(currentX), _currentY(currentY) {}
        
    void apply(osg::Node& node)
    {
        if (node.getStateSet()) apply(*node.getStateSet());
        traverse(node);
    }
    
    void apply(osg::StateSet& stateset)
    {
        if (_modAlphaFunc)
        {
            osg::AlphaFunc* alphaFunc = dynamic_cast<osg::AlphaFunc*>(stateset.getAttribute(osg::StateAttribute::ALPHAFUNC));
            if (alphaFunc)
            {
                osg::notify(osg::INFO)<<"Adjusting alpha func"<<std::endl;

                float alpha = alphaFunc->getReferenceValue();
                alpha = osg::clampBetween((1.0f-_currentY)*0.5f,0.0f,1.0f);

                alphaFunc->setReferenceValue(alpha);
            }
        }
        
        if (_modMaterial)
        {        
            osg::Material* material = dynamic_cast<osg::Material*>(stateset.getAttribute(osg::StateAttribute::MATERIAL));
            if (material)
            {
                osg::notify(osg::INFO)<<"Adjusting material func"<<std::endl;
                float alpha = osg::clampBetween((_currentY+1.0f)*0.5f,0.0f,1.0f);
                material->setAlpha(osg::Material::FRONT_AND_BACK,alpha);
            }
        }
    }

    bool _modAlphaFunc, _modMaterial;
    float   _currentX, _currentY;
        
};


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SlideEventHandler
//
SlideEventHandler::SlideEventHandler(osgViewer::Viewer* viewer):
    _viewer(viewer),
    _presentationSwitch(0),
    _activeSlide(0),
    _slideSwitch(0),
    _activeLayer(0),
    _firstTraversal(true),
    _previousTime(-1.0f),
    _timePerSlide(1.0),
    _autoSteppingActive(false),
    _loopPresentation(false),
    _pause(false),
    _hold(false),
    _updateLightActive(false),
    _updateOpacityActive(false),
    _previousX(0), _previousY(0),
    _cursorOn(true),
    _releaseAndCompileOnEachNewSlide(false),
    _firstSlideOrLayerChange(true),
    _tickAtFirstSlideOrLayerChange(0),
    _tickAtLastSlideOrLayerChange(0),
    _timeDelayOnNewSlideWithMovies(0.25f),
    _minimumTimeBetweenKeyPresses(0.25),
    _timeLastKeyPresses(-1.0)
{
    s_seh = this;
}

double SlideEventHandler::getDuration(const osg::Node* node) const
{
    const LayerAttributes* la = dynamic_cast<const LayerAttributes*>(node->getUserData());
    return la ? la->_duration : -1.0;
}

void SlideEventHandler::set(osg::Node* model)
{
#if 0
    // pause all slides, then just reenable the current slide.
    ActivityUpdateCallbacksVisitor aucv(ALL_OBJECTS, true);
    aucv.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
    model->accept(aucv);
#endif

    ActiveOperators operators;
    operators.collect(model, osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
    operators.setPause(true);

    FindNamedSwitchVisitor findPresentation("Presentation");
    model->accept(findPresentation);
    
    if (findPresentation._switch)
    {
        osg::notify(osg::INFO)<<"Presentation '"<<model->getName()<<"'"<<std::endl;
        _presentationSwitch = findPresentation._switch;
        
        double duration = getDuration(_presentationSwitch.get());
        if (duration>=0.0)
        {
            osg::notify(osg::INFO)<<"Presentation time set to "<<duration<<std::endl;
            _timePerSlide = duration;
        }
        
        //selectSlide(0);
    }
    else
    {
        osg::notify(osg::INFO)<<"No presentation present in scene."<<std::endl;

        _presentationSwitch = 0;
        _activeSlide = 0;
        
        FindNamedSwitchVisitor findSlide("Slide");
        model->accept(findSlide);
        
        if (findSlide._switch)
        {
            osg::notify(osg::INFO)<<"Found presentation slide"<<findSlide._switch->getName()<<std::endl;

            _slideSwitch = findSlide._switch;
            //selectLayer(0);
        }
        else
        {
            osg::notify(osg::INFO)<<"No slides present in scene, unable to operate as a slideshow."<<std::endl;
        }
    
    }
}

double SlideEventHandler::getCurrentTimeDelayBetweenSlides() const
{
    if (_slideSwitch.valid())
    {
        double duration = -1.0;
        if (_activeLayer<static_cast<int>(_slideSwitch->getNumChildren()))
        {
            duration = getDuration(_slideSwitch->getChild(_activeLayer));
        }

        if (duration < 0.0)
        {
            duration = getDuration(_slideSwitch.get());
        }

        if (duration >=0 )
        {
            return duration;
        }        
    }
        
    return _timePerSlide;
}


void SlideEventHandler::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
    osgGA::EventVisitor* ev = dynamic_cast<osgGA::EventVisitor*>(nv);
    if (ev)
    {
        if (node->getNumChildrenRequiringEventTraversal()>0) traverse(node,nv);

        if (ev->getActionAdapter() && !ev->getEvents().empty())
        {
            for(osgGA::EventQueue::Events::iterator itr = ev->getEvents().begin();
                itr != ev->getEvents().end();
                ++itr)
            {
                handleWithCheckAgainstIgnoreHandledEventsMask(*(*itr), *(ev->getActionAdapter()), node, nv);
            }
        }
    }
}

bool SlideEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
{

    if (!_viewer)
    {
        _viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
        selectSlide(0);
        home();
        osg::notify(osg::NOTICE)<<"Assigned viewer. to SlideEventHandler"<<std::endl;
    }
    // else  osg::notify(osg::NOTICE)<<"SlideEventHandler::handle()"<<std::endl;


    if (ea.getHandled()) return false;

    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::FRAME):
        {
            if (_autoSteppingActive && !_pause)
            {
                double time = ea.time();
                
                if (_firstTraversal)
                {
                    _firstTraversal = false;
                    _previousTime = time;
                }
                else if (time-_previousTime>=getCurrentTimeDelayBetweenSlides())
                {
                    // _previousTime = time;
                    
                    if (!_hold)
                    {
                        // increment the previous by the required time delay, note relative to the current
                        // to keep the time relative to an absolute time signal, thus avoid drift of timing.
                        _previousTime += getCurrentTimeDelayBetweenSlides();

                        nextLayerOrSlide();
                    }
                    else
                    {
                        // we're holding of the move to next layer to slide, but we need slip the time forward accordingly
                        // componensate for the extra time that this frame is recieving.
                        _previousTime = time-getCurrentTimeDelayBetweenSlides();
                    }
                }
            }
            return false;
        }

        case(osgGA::GUIEventAdapter::KEYDOWN):
        {
            double time = ea.time();
            double deltaTime = time - _timeLastKeyPresses;
            if (deltaTime < _minimumTimeBetweenKeyPresses)
            {
                 break;
            } 
            
            _timeLastKeyPresses = time;

            if (ea.getKey()=='g')
            {
                if (!_autoSteppingActive)
                {
                    _autoSteppingActive = true;
                    _previousTime = ea.time();
                }
                return true;
            }
            else if (ea.getKey()=='h')
            {
                if (_autoSteppingActive)
                {
                    _autoSteppingActive = false;
                    _previousTime = ea.time();
                }
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Home ||
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Home)
            {
                _autoSteppingActive = false;
                selectSlide(0);
                home(ea,aa);
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_End ||
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_End)
            {
                _autoSteppingActive = false;
                selectSlide(LAST_POSITION,LAST_POSITION);
                home(ea,aa);
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Down ||
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Down)
            {
                _autoSteppingActive = false;
                nextLayer();
                return true;
            }
            else if (ea.getKey()=='n')
            {
                _autoSteppingActive = false;
                nextLayerOrSlide();
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Up ||
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Up)
            {
                _autoSteppingActive = false;
                previousLayer();
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Page_Down || 
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Page_Down)
            {
                _autoSteppingActive = false;
                nextLayerOrSlide();
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Page_Up ||
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Page_Up)
            {
                _autoSteppingActive = false;
                previousLayerOrSlide();
                return true;
            }
            else if (ea.getKey()=='N' || 
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_Right || 
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Right)
            {
                _autoSteppingActive = false;
                nextSlide();
                home(ea,aa);
                return true;
            }
            else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Left || 
                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Left)
            {
                _autoSteppingActive = false;
                previousSlide();
                home(ea,aa);
                return true;
            }
            else if (ea.getKey()=='p')
            {
                if (!_pause)
                {
                    _pause = true;
#if 0                    
                    resetUpdateCallbackActivity(ALL_OBJECTS);
#endif
                    _activeOperators.setPause(_pause);
                }
                
                return true;
                
            }
            else if (ea.getKey()=='o')
            {
                if (_pause)
                {
                    _pause = false;
#if 0                    
                    resetUpdateCallbackActivity(ALL_OBJECTS);
#endif
                    _activeOperators.setPause(_pause);
                }
                return true;
                
            }
            else if (ea.getKey()=='h')
            {
                _hold = true;
                return true;
            }
            else if (ea.getKey()=='r')
            {
#if 0
                resetUpdateCallbacks(ALL_OBJECTS);
#endif
                _activeOperators.reset();
                return true;
            }
/*            
            else if (ea.getKey()=='c')
            {
                _cursorOn = !_cursorOn;
            
                for( unsigned int i = 0; i < _viewer->getCameraConfig()->getNumberOfCameras(); i++ )
                {
                    Producer::Camera* cam = _viewer->getCameraConfig()->getCamera(i);
                    Producer::RenderSurface* rs = cam->getRenderSurface();
                    rs->useCursor(_cursorOn);
                }

                return true;
            }
*/
            else if (ea.getKey()=='u')
            {
                updateAlpha(true,false,ea.getX(),ea.getY());
                return true;
            }
            else if (ea.getKey()=='i')
            {
                updateAlpha(false,true,ea.getX(),ea.getY());
                return true;
            }
            else if (ea.getKey()=='k')
            {
                updateLight(ea.getX(),ea.getY());
                return true;
            }

            return false;
        }
        case(osgGA::GUIEventAdapter::KEYUP):
        {
            if (ea.getKey()=='h')
            {
                _hold = false;
                return true;
            }
            return false;
        }
        default:
            return false;
    }
    return false;
}

void SlideEventHandler::getUsage(osg::ApplicationUsage& usage) const
{
    usage.addKeyboardMouseBinding("a","Toggle on/off the automatic advancement for image to image");
    usage.addKeyboardMouseBinding("n","Advance to next layer or slide");
    usage.addKeyboardMouseBinding("p","Move to previous layer or slide");
}

unsigned int SlideEventHandler::getNumSlides()
{
    if (_presentationSwitch.valid()) return _presentationSwitch->getNumChildren();
    else return 0;
}


bool SlideEventHandler::selectSlide(int slideNum,int layerNum)
{
    if (!_presentationSwitch) return false;

    osg::notify(osg::INFO)<<"selectSlide("<<slideNum<<","<<layerNum<<")"<<std::endl;
    
    if (slideNum==LAST_POSITION && _presentationSwitch->getNumChildren()>0)
    {
        slideNum = _presentationSwitch->getNumChildren()-1;
    }
    
    if (slideNum>=static_cast<int>(_presentationSwitch->getNumChildren())) return false;


    osg::Timer_t tick = osg::Timer::instance()->tick();

    if (_firstSlideOrLayerChange)
    {
        _firstSlideOrLayerChange = false;
        _tickAtFirstSlideOrLayerChange = tick;
        _tickAtLastSlideOrLayerChange = tick;
    }

    osg::notify(osg::INFO)<<"selectSlide("<<slideNum<<","<<layerNum<<") at time "<<osg::Timer::instance()->delta_s(_tickAtFirstSlideOrLayerChange, tick)<<" seconds, length ="<<osg::Timer::instance()->delta_s(_tickAtLastSlideOrLayerChange, tick)<<" seconds"<<std::endl;
    
    _tickAtLastSlideOrLayerChange = tick;
    
    // dectivate movies etc on current active slide.
    bool newSlide = _activeSlide!=slideNum;
    if (newSlide)
    {
        if (_releaseAndCompileOnEachNewSlide)
        {
            releaseSlide(_activeSlide);
        }
    }

    _activeSlide = slideNum;
    _presentationSwitch->setSingleChildOn(_activeSlide);

    //osg::notify(osg::INFO)<<"Selected slide '"<<_presentationSwitch->getChild(_activeSlide)->getName()<<"'"<<std::endl;

    
    FindNamedSwitchVisitor findSlide("Slide");
    _presentationSwitch->getChild(_activeSlide)->accept(findSlide);

    bool result = false;
    if (findSlide._switch)
    {
        //osg::notify(osg::INFO)<<"Found slide '"<<findSlide._switch->getName()<<"'"<<std::endl;
        _slideSwitch = findSlide._switch;

        result = selectLayer(layerNum);

        
    }
    else
    {
        //osg::notify(osg::INFO)<<"Not found slide"<<std::endl;
        updateOperators();
    }
    

    // refersh the viewer.
    //_viewer->getKeySwitchMatrixManipulator()->setMinimumDistance(0.001);
    
    _viewer->getCameraManipulator()->setNode(_slideSwitch.get());
    
    _viewer->computeActiveCoordinateSystemNodePath();
    
    // resetUpdateCallbacks(ALL_OBJECTS);
    
    bool _useSlideFilePaths = false;
    if (_useSlideFilePaths)
    {
        // set up the file paths
        FindFilePathDataVisitor ffpdv;
        _presentationSwitch->accept(ffpdv);
    }
    
    if (newSlide && _releaseAndCompileOnEachNewSlide)
    {
        compileSlide(slideNum);
    }

    return result;
    
}

bool SlideEventHandler::selectLayer(int layerNum)
{
    if (!_slideSwitch) return false;

    if (layerNum==LAST_POSITION && _slideSwitch->getNumChildren()>0)
    {
        layerNum = _slideSwitch->getNumChildren()-1;
    }

    if (layerNum>=static_cast<int>(_slideSwitch->getNumChildren())) return false;
    
    _activeLayer = layerNum;
    _slideSwitch->setSingleChildOn(_activeLayer);

    updateOperators();

    osg::notify(osg::INFO)<<"Selected layer '"<<_slideSwitch->getChild(_activeLayer)->getName()<<"' num="<<_activeLayer<< std::endl;

    return true;
}

bool SlideEventHandler::nextLayerOrSlide()
{
    if (nextLayer()) return true;
    else return nextSlide();
}

bool SlideEventHandler::previousLayerOrSlide()
{
    if (previousLayer()) return true;
    else return previousSlide();
}

bool SlideEventHandler::nextSlide()
{
    LayerAttributes* la = _slideSwitch.valid() ? dynamic_cast<LayerAttributes*>(_slideSwitch->getUserData()) : 0;
    if (la && la->requiresJump())
    {
        if (la->getRelativeJump())
        {
            int previousSlide = getActiveSlide();
            int previousLayer = getActiveLayer();
            int newSlide = previousSlide + la->getSlideNum();
            int newLayer = previousLayer + la->getLayerNum();
            if (newLayer<0) 
            {
                newLayer = 0;
            }

            return selectSlide(newSlide, newLayer);
        }
        else
        {
            return selectSlide(la->getSlideNum(),la->getLayerNum());
        }
    }

    if (selectSlide(_activeSlide+1)) return true;
    else if (_loopPresentation) return selectSlide(0);
    else return false;
}

bool SlideEventHandler::previousSlide()
{
#if 1
    // start position when doing previous slide set to top of slide
    if (_activeSlide>0) return selectSlide(_activeSlide-1);
    else if (_loopPresentation && _presentationSwitch.valid()) return selectSlide(_presentationSwitch->getNumChildren()-1);
    else return false;
#else
    // start position when doing previous slide set to end of slide
    if (_activeSlide>0) return selectSlide(_activeSlide-1,LAST_POSITION);
    else if (_loopPresentation && _presentationSwitch.valid()) return selectSlide(_presentationSwitch->getNumChildren()-1,LAST_POSITION);
    else return false;
#endif
}

bool SlideEventHandler::nextLayer()
{
    LayerAttributes* la = (_slideSwitch.valid() && _activeLayer>=0) ? dynamic_cast<LayerAttributes*>(_slideSwitch->getChild(_activeLayer)->getUserData()) : 0;
    if (la)
    {
        la->callLeaveCallbacks(_slideSwitch->getChild(_activeLayer));
    
        if (la->requiresJump())
        {
            if (la->getRelativeJump())
            {
                int previousSlide = getActiveSlide();
                int previousLayer = getActiveLayer();
                int newSlide = previousSlide + la->getSlideNum();
                int newLayer = previousLayer + la->getLayerNum();
                if (newLayer<0) 
                {
                    newLayer = 0;
                }

                return selectSlide(newSlide, newLayer);
            }
            else
            {
                return selectSlide(la->getSlideNum(),la->getLayerNum());
            }
        }
    }

    return selectLayer(_activeLayer+1);
}

bool SlideEventHandler::previousLayer()
{
    if (_activeLayer>0) return selectLayer(_activeLayer-1);
    else return false;
}


void SlideEventHandler::updateOperators()
{
    _activeOperators.collect(_slideSwitch.get());
    _activeOperators.process();

    if (_viewer.valid())
    {
        UpdateLightVisitor uav(_viewer->getCamera()->getViewMatrix(),0.0f,0.0f);
        _viewer->getSceneData()->accept(uav);
    }
}

bool SlideEventHandler::home(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
{
    FindHomePositionVisitor fhpv;
    osg::Node* node = _viewer->getSceneData();
    if (node) node->accept(fhpv);

    if (fhpv._homePosition.valid())
    {
        osg::notify(osg::INFO)<<"Doing home for stored home position."<<std::endl;

        _viewer->getCameraManipulator()->setAutoComputeHomePosition(false);
        _viewer->getCameraManipulator()->setHomePosition(
                                             fhpv._homePosition->eye,
                                             fhpv._homePosition->center,
                                             fhpv._homePosition->up);
    }
    else
    {
        _viewer->getCameraManipulator()->setAutoComputeHomePosition(true);
    }
    _viewer->getCameraManipulator()->home(ea,aa);

    return true;
}

bool SlideEventHandler::home()
{
    osg::ref_ptr<osgGA::GUIEventAdapter> ea =  new osgGA::GUIEventAdapter;
    ea->setEventType(osgGA::GUIEventAdapter::FRAME);
    ea->setTime(_viewer->getEventQueue()->getTime());

    home(*ea,*_viewer);
    return true;
}

void SlideEventHandler::updateAlpha(bool modAlphaFunc, bool modMaterial, float x, float y)
{
    osg::notify(osg::INFO)<<"updateAlpha("<<x<<","<<y<<")"<<std::endl;
    
    UpdateAlphaVisitor uav(modAlphaFunc, modMaterial, x,y);
    if (_presentationSwitch.valid()) _presentationSwitch->accept(uav);
    else if (_viewer->getSceneData()) _viewer->getSceneData()->accept(uav);
}


void SlideEventHandler::updateLight(float x, float y)
{
    osg::notify(osg::INFO)<<"updateLight("<<x<<", "<<y<<")"<<std::endl;

    UpdateLightVisitor uav(_viewer->getCamera()->getViewMatrix(),x,y);
    _viewer->getSceneData()->accept(uav);
}

void SlideEventHandler::compileSlide(unsigned int slideNum)
{
    if (!_compileSlideCallback)
    {
        _compileSlideCallback = new CompileSlideCallback();

        osgViewer::Viewer::Cameras cameras;
        _viewer->getCameras(cameras);

        for(osgViewer::Viewer::Cameras::iterator itr = cameras.begin();
            itr != cameras.end();
            ++itr)
        {
            (*itr)->setPreDrawCallback(_compileSlideCallback.get());
        }

    }            
    
    _compileSlideCallback->needCompile(_presentationSwitch->getChild(slideNum));
    
}

void SlideEventHandler::releaseSlide(unsigned int slideNum)
{
    osgUtil::GLObjectsVisitor globjVisitor(osgUtil::GLObjectsVisitor::RELEASE_DISPLAY_LISTS|
                                           osgUtil::GLObjectsVisitor::RELEASE_STATE_ATTRIBUTES);
    globjVisitor.setNodeMaskOverride(0xffffffff);
    
    _presentationSwitch->getChild(slideNum)->accept(globjVisitor);
}

void SlideEventHandler::dispatchEvent(const KeyPosition& keyPosition)
{
    osg::notify(osg::INFO)<<" keyPosition._key "<<keyPosition._key<<" "<<keyPosition._x<<" "<<keyPosition._y<<std::endl;

    osgGA::EventQueue* eq = _viewer->getEventQueue();

    // reset the time of the last key press to ensure thatthe event is disgarded as a key repeat.
    _timeLastKeyPresses = -1.0;
    
    if (keyPosition._x!=FLT_MAX)
    {
        float xRescaled = eq->getCurrentEventState()->getXmin() + (keyPosition._x+1.0f)*0.5f*(eq->getCurrentEventState()->getXmax()-eq->getCurrentEventState()->getXmin());
        eq->getCurrentEventState()->setX(xRescaled);
    }
    
    if (keyPosition._y!=FLT_MAX)
    {
        float yRescaled = eq->getCurrentEventState()->getYmin() + (keyPosition._y+1.0f)*0.5f*(eq->getCurrentEventState()->getXmax()-eq->getCurrentEventState()->getYmin());
        eq->getCurrentEventState()->setY(yRescaled);
    }

    eq->keyPress(keyPosition._key);
    eq->keyRelease(keyPosition._key);
}



