/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM 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 General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef NMM_GENERIC_CONVERTER_NODE_HPP
#define NMM_GENERIC_CONVERTER_NODE_HPP NMM_GENERIC_CONVERTER_NODE_HPP

#include "nmm/nmm_prefix.hpp"

#include "GenericProcessorNode.hpp"

#include "visibility.hpp"

namespace NMM {

  /** An abstract converter.
   * A converter is an object which converts buffers from one format to another.
   * It provides an interface which accepts buffers and returns buffers containing converted data.
   * The implementation of this interface is provided by subclasses.
   *
   * Converter objects are managed by converter nodes (subclasses of GenericConverterNode)
   * and associated with pairs of formats.
   *
   * @see GenericConverterNode
   */
  class DLLEXPORT ConverterObject {
  public :
    /** Virtual destructor.
     */
    virtual ~ConverterObject() {};

    /** Convert a buffer.
     * This method performs the data conversion. Its implementation is provided by subclasses.
     * The implementation creates a new buffer, stores the converted data in the new buffer
     * and returns the new buffer. The incoming buffer must be released.
     *
     * @param buffer Buffer containing the input data
     * @return A new buffer containing the converted data
     */
    virtual Buffer* convert(Buffer* buffer) = 0;
  };

  /** An adapter for a conversion method.
   * This converter object implements the conversion method by calling a member conversion method
   * of an object of class T. Converter nodes can use this class to conveniently implement conversion
   * methods within the node class and registering a converter object for each conversion method.
   *
   * Usage:
   * @code
   * // Create a converter object for MyNode::myConversionMethod
   * ConverterObject* converter = new TConverterObject<MyNode>(&MyNode::myConversionMethod, this);
   * @endcode
   *
   * The signature of the conversion method must be as follows:
   * @code
   * Buffer* convert(Buffer* buffer)
   * @endcode
   * where @c buffer is the input buffer and the result is a new buffer containing the converted
   * data (similar to the convert()).
   *
   * @see GenericConverterNode
   */
  template<class T>
  class TConverterObject : ConverterObject
  {
    /** Signature of a conversion method.
     */
    typedef Buffer* (T::*CMethod)(Buffer*);

  public:
    /** Constructor.
     *
     * @param node The object which provides the conversion method
     * @param method The conversion method
     */
    TConverterObject(T* node, CMethod method) {this->node = node; this->method = method;}

    /** Convert a buffer.
     * This method performs the data conversion by invoking the conversion method.
     *
     * @param buffer Buffer containing the input data
     * @return A new buffer containing the converted data
     */
    virtual Buffer* convert(Buffer* buffer) { return (node->*method)(buffer);}

  private:
    CMethod method;     ///< Conversion method
    T* node;            ///< Object which provides the conversion method
  };


  /** Generic converter node.
   * A converter node is a special kind of processor node which provides a generic implementation of
   * a node which converts data using converter objects. For each supported pair of formats, the node
   * registers a converter object which provides the actual implementation of a conversion function.
   *
   * Subclasses of this class implement conversion methods or provide subclasses of ConverterObject.
   * Converter objects are typically registered in doInit(). The TConverterObject is a convenient
   * adapter class which can be used to directly register methods of the node class as converter
   * objects.
   *
   * @see ConverterObject TConverterObject
   */
  class DLLEXPORT GenericConverterNode : public GenericProcessorNode
  {
    /** Signature of a conversion method.
     */
    typedef Buffer* (*Converter)(Buffer* source_buffer, const GenericConverterNode*);

  public:
    /** Create a new converter node.
     * The type of the node is INode::CONVERTER.
     *
     * @param name Name of the node
     * @param queue_mode Initial queue mode for all input queues
     * @param size Initial maximum queue size for all input queues
     */
    GenericConverterNode(const char* name = "GenericConverterNode",
                         const StreamQueue::Mode queue_mode = StreamQueue::DEFAULT_MODE, 
                         const int size = StreamQueue::DEFAULT_SIZE)
      : GenericProcessorNode(name, queue_mode, size)
    {
      setType(INode::CONVERTER);
    }

    /** Virtual destructor.
     */
    virtual ~GenericConverterNode() { }

    /** Add a converter function to the converter map.
     * This method registers a free function as a converter for the given pair of formats.
     * If the input and output formats of the node match the given formats, then the
     * processBuffer() method will invoke the specified conversion function.
     *
     * Example:
     * @code
     * // Free conversion function
     * static Buffer* convertMyData(Buffer* buffer, const GenericConverterNode* node);
     *
     * // Register converter
     * Format* i_format;
     * Format* o_format;
     * addConverter(i_format, o_format, &convertMyData);
     * @endcode
     *
     * @param i_format Input format of the conversion
     * @param o_format Output format of the conversion
     * @param converter Pointer to a free conversion function
     */
    void addConverter(NMM::Format* i_format, NMM::Format* o_format , Converter converter);

    /** Add a converter object to the converter map.
     * This method registers a ConverterObject as a converter for the given pair of formats.
     * If the input and output formats of the node match the given formats, then the
     * processBuffer() method will invoke the conversion method of the specified converter object.
     *
     * Example:
     * @code
     * class MyConverterNode {
     *   // ...
     *
     *   // Conversion method
     *   Buffer* convertMyData(Buffer* buffer);
     *
     *   // ...
     * };
     *
     * // Register converter
     * Format* i_format;
     * Format* o_format;
     * addConverter(i_format, o_format, new TConverterNode<MyConverterNode>(&convertMyData, this));
     * @endcode
     *
     * @param i_format Input format of the conversion
     * @param o_format Output format of the conversion
     * @param converter Pointer to a converter object
     *
     * @note This method is not fully implemented
     */
    void addConverter(NMM::Format* i_format, NMM::Format* o_format, ConverterObject* converter);

    /** Set the current converter function.
     * This method sets the converter function that should be used.
     *
     * @param c Converter function to use
     */
    virtual void setConverter(Converter c) {converter = c;}

    /** Perform state transition from OUTPUT_INITIALIZED to ACTIVATED.
     * This method changes the state and invokes the doActivate method of the node.
     * The state transition fails if doActivate fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * This implementation selects the conversion function based on the current
     * connection formats at the input and output streams.
     *
     * @exception StateTransitionFailedException if doActivate has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    virtual void activate()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

  protected:
    /** Process a buffer.
     * This method is called when a buffer has been dequeued from an input queue and has to be processed by the node.
     * This implementation invokes the conversion method selected by doActivate().
     *
     * @param buffer The input buffer, if any, or 0
     * @return The returned message, if any, or 0
     */
    virtual Message *processBuffer(Buffer *buffer);

  private:
    Converter converter; ///< Selected converter function

    map<pair<const NMM::Format*,const NMM::Format*>, Converter > converter_map;                 ///< Converter function map
    map<pair<const NMM::Format*,const NMM::Format*>, ConverterObject* > object_converter_map;   ///< Converter object map
  };
}

#include "nmm/nmm_suffix.hpp"

#endif

