/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/


#ifndef INCLUDED_SLIDESHOW_BASENODE_HXX
#define INCLUDED_SLIDESHOW_BASENODE_HXX

#include <canvas/debug.hxx>
#include <tools/diagnose_ex.h>
#include <osl/diagnose.hxx>

#include "event.hxx"
#include "animationnode.hxx"
#include "slideshowcontext.hxx"
#include "shapesubset.hxx"

#include <boost/noncopyable.hpp>
#include <vector>

namespace slideshow {
namespace internal {

/** Context for every node.
    
    Besides the global AnimationNodeFactory::Context data,
    this struct also contains the current DocTree subset
    for this node. If start and end index of the
    DocTreeNode are equal, the node should use the
    complete shape.
*/
struct NodeContext
{
    NodeContext( const SlideShowContext&                 rContext,
                 const ::basegfx::B2DVector&             rSlideSize )
        : maContext( rContext ),
          maSlideSize( rSlideSize ),
          mpMasterShapeSubset(),
          mnStartDelay(0.0),
          mbIsIndependentSubset( true )
        {}
    
    void dispose() 
    { 
        maContext.dispose(); 
        mpMasterShapeSubset.reset(); 
    }
    
    /// Context as passed to createAnimationNode()
    SlideShowContext                 maContext;

    /// Size in user coordinate space of the corresponding slide
    ::basegfx::B2DVector             maSlideSize;

    /// Shape to be used (provided by parent, e.g. for iterations)
    ShapeSubsetSharedPtr             mpMasterShapeSubset;
    
    /// Additional delay to node begin (to offset iterate effects)
    double                           mnStartDelay;
    
    /// When true, subset must be created during slide initialization
    bool                             mbIsIndependentSubset;
};

class BaseContainerNode;

/** This interface extends AnimationNode with some
    file-private accessor methods.
*/
class BaseNode : public AnimationNode,
                 public  ::osl::DebugBase<BaseNode>,
                 private ::boost::noncopyable
{
public:
    BaseNode( ::com::sun::star::uno::Reference< 
              ::com::sun::star::animations::XAnimationNode> const& xNode, 
              ::boost::shared_ptr<BaseContainerNode> const&        pParent,
              NodeContext const&                                   rContext );
    
    /** Provide the node with a shared_ptr to itself.
        
        Since implementation has to create objects which need
        a shared_ptr to this node, and a pointee cannot
        retrieve a shared_ptr to itself internally, have to
        set that from the outside.
    */
    void setSelf( const ::boost::shared_ptr< BaseNode >& rSelf );
    
    
#if defined(VERBOSE) && defined(DBG_UTIL)
    virtual void showState() const;
    virtual const char* getDescription() const;
    void showTreeFromWithin() const;
#endif
    
    const ::boost::shared_ptr< BaseContainerNode >& getParentNode() const
        { return mpParent; }
    
    // Disposable:
    virtual void dispose();
    
    // AnimationNode:
    virtual bool init();
    virtual bool resolve();
    virtual bool activate();
    virtual void deactivate();
    virtual void end();
    virtual ::com::sun::star::uno::Reference< 
        ::com::sun::star::animations::XAnimationNode> getXAnimationNode() const;
    virtual NodeState getState() const;
    virtual bool registerDeactivatingListener(
        const AnimationNodeSharedPtr& rNotifee );
    // nop:
    virtual void notifyDeactivating( const AnimationNodeSharedPtr& rNotifier );

    bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; }    

protected:
    void scheduleDeactivationEvent( EventSharedPtr const& pEvent =
                                    EventSharedPtr() );
    
    SlideShowContext const&                 getContext() const { return maContext; }
    ::boost::shared_ptr<BaseNode> const&    getSelf() const { return mpSelf; }

    bool checkValidNode() const {
        ENSURE_OR_THROW( mpSelf, "no self ptr set!" );
        bool const bRet = (meCurrState != INVALID);
        OSL_ENSURE( bRet, "### INVALID node!" );
        return bRet;
    }
    
private:
    // all state affecting methods have "_st" counterparts being called at
    // derived classes when in state transistion: no-ops here at BaseNode...
    virtual bool init_st();
    virtual bool resolve_st();
    virtual void activate_st();
    virtual void deactivate_st( NodeState eDestState );
    
private:
    /// notifies
    /// - all registered deactivation listeners
    /// - single animation end (every node)
    /// - slide animations (if main sequence root node)
    void notifyEndListeners() const;
    
    /// Get the node's restart mode
    sal_Int16 getRestartMode();
    
    /** Get the default restart mode
        
        If this node's default mode is
        AnimationRestart::DEFAULT, this method recursively
        calls the parent node.
    */
    sal_Int16 getRestartDefaultMode() const;
    
    /// Get the node's fill mode
    sal_Int16 getFillMode();
    
    /** Get the default fill mode.
        
        If this node's default mode is AnimationFill::DEFAULT,
        this method recursively calls the parent node.
    */
    sal_Int16 getFillDefaultMode() const;
    
    bool isTransition( NodeState eFromState, NodeState eToState,
                       bool debugAssert = true ) const {
        (void) debugAssert; // avoid warning
        bool const bRet =((mpStateTransitionTable[eFromState] & eToState) != 0);
        OSL_ENSURE( !debugAssert || bRet, "### state unreachable!" );
        return bRet;
    }

    bool inStateOrTransition( int mask ) const {
        return ((meCurrState & mask) != 0 ||
                (meCurrentStateTransition & mask) != 0);
    }
    
    class StateTransition;
    friend class StateTransition;
    
private:
    SlideShowContext                                   maContext;
    
    typedef ::std::vector< AnimationNodeSharedPtr >    ListenerVector;
    
    ListenerVector                                     maDeactivatingListeners;
    ::com::sun::star::uno::Reference< 
        ::com::sun::star::animations::XAnimationNode > mxAnimationNode;
    ::boost::shared_ptr< BaseContainerNode >           mpParent;
    ::boost::shared_ptr< BaseNode >                    mpSelf;
    const int*                                         mpStateTransitionTable;
    const double                                       mnStartDelay;
    NodeState                                          meCurrState;
    int                                                meCurrentStateTransition;
    EventSharedPtr                                     mpCurrentEvent;
    const bool                                         mbIsMainSequenceRootNode;
};

typedef ::boost::shared_ptr< BaseNode > BaseNodeSharedPtr;

} // namespace internal
} // namespace slideshow

#endif /* INCLUDED_SLIDESHOW_BASENODE_HXX */

