#include <osgParticle/ParticleProcessor>

#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/CopyOp>
#include <osg/Matrix>
#include <osg/Notify>

#include <osgUtil/CullVisitor>


using namespace osg;

osgParticle::ParticleProcessor::ParticleProcessor()
:    osg::Node(),
    _rf(RELATIVE_RF),
    _enabled(true),
    _t0(-1),
    _ps(0),
    _first_ltw_compute(true),
    _need_ltw_matrix(false),
    _first_wtl_compute(true),
    _need_wtl_matrix(false),
    _current_nodevisitor(0),
    _endless(true),
    _lifeTime(0.0),
    _startTime(0.0),
    _currentTime(0.0),
    _resetTime(0.0)
{
    setCullingActive(false);
}

osgParticle::ParticleProcessor::ParticleProcessor(const ParticleProcessor& copy, const osg::CopyOp& copyop)
:    osg::Node(copy, copyop),
    _rf(copy._rf),
    _enabled(copy._enabled),
    _t0(copy._t0),
    _ps(static_cast<ParticleSystem* >(copyop(copy._ps.get()))),
    _first_ltw_compute(copy._first_ltw_compute),
    _need_ltw_matrix(copy._need_ltw_matrix),
    _first_wtl_compute(copy._first_wtl_compute),
    _need_wtl_matrix(copy._need_wtl_matrix),
    _current_nodevisitor(0),
    _endless(copy._endless),
    _lifeTime(copy._lifeTime),
    _startTime(copy._startTime),
    _currentTime(copy._currentTime),
    _resetTime(copy._resetTime)
{
}

void osgParticle::ParticleProcessor::traverse(osg::NodeVisitor& nv)
{
    // typecast the NodeVisitor to CullVisitor
    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);

    // continue only if the visitor actually is a cull visitor
    if (cv) {

        // continue only if the particle system is valid
        if (_ps.valid())
        {

            if (nv.getFrameStamp())
            {

                // retrieve the current time
                double t = nv.getFrameStamp()->getReferenceTime();

                // reset this processor if we've reached the reset point
                if ((_currentTime >= _resetTime) && (_resetTime > 0))
                {
                    _currentTime = 0;
                    _t0 = -1;
                }

                // skip if we haven't initialized _t0 yet
                if (_t0 != -1)
                {

                    // check whether the processor is alive
                    bool alive = false;
                    if (_currentTime >= _startTime)
                    {
                        if (_endless || (_currentTime < (_startTime + _lifeTime)))
                            alive = true;
                    }

                    // update current time
                    _currentTime += t - _t0;

                    // process only if the particle system is not frozen/culled
                    if (alive && 
                        _enabled && 
                        !_ps->isFrozen() && 
                        (_ps->getLastFrameNumber() >= (nv.getFrameStamp()->getFrameNumber() - 1) || !_ps->getFreezeOnCull()))
                    {
                        // initialize matrix flags
                        _need_ltw_matrix = true;
                        _need_wtl_matrix = true;
                        _current_nodevisitor = &nv;

                        // do some process (unimplemented in this base class)
                        process(t - _t0);
                    }
                }

                // update _t0
                _t0 = t;

            }
            else
            {
                osg::notify(osg::WARN) << "osgParticle::ParticleProcessor::traverse(NodeVisitor&) requires a valid FrameStamp to function, particles not updated.\n";
            }

        } else 
        {
            osg::notify(osg::WARN) << "ParticleProcessor \"" << getName() << "\": invalid particle system\n";
        }
    }


    // call the inherited method
    Node::traverse(nv);
}

osg::BoundingSphere osgParticle::ParticleProcessor::computeBound() const
{
    return osg::BoundingSphere();
}

