/* 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_SEQUENCE_VALUE_HPP
#define NMM_SEQUENCE_VALUE_HPP NMM_SEQUENCE_VALUE_HPP

#include "nmm/nmm_prefix.hpp"

#include "Value.hpp"
#include "TypeInfo.hpp"

#include "ValueFactory.hpp"
#include "IValueStream.hpp"
#include "OValueStream.hpp"

#include <vector>

namespace NMM {

  /** A serializable pointer container.
   * This template class is a serializable container for serializable elements, which are stored as
   * pointers. Elements may be polymorphic. The container is based on std::vector.
   *
   * The element type must be a serializable type. If the element type is not a subclass of Value,
   * a TValue wrapper is used. Otherwise, the use of a TValue wrapper must be disabled explicitly:
   * @code
   * SequenceValue< MyValue > s;
   * s.useTValue(false);
   * @endcode
   */
  template<typename T = Value >
  class SequenceValue : public Value , public vector<T*> {
  public:
    /** @name Constructor & Destructor
     */
    //@{
    /** Default constructor.
     * This constructor creates an empty container.
     * A TValue wrapper is used during serialization. Call useTValue(false) to change this default.
     *
     * @param memory_mgm Memory management mode
     */
    SequenceValue(MemoryMgm memory_mgm = INVALUE);

    /** Constructor.
     * This constructor creates a non-empty container. All elements are initialized with null pointers.
     * A TValue wrapper is used during serialization. Call useTValue(false) to change this default.
     *
     * @param size Initial number of elements
     * @param memory_mgm Memory management mode
     */
    SequenceValue(size_t size, MemoryMgm memory_mgm = INOUTVALUE);

    /** Copy constructor.
     *
     * @param sv Object to copy from
     */
    SequenceValue(const SequenceValue& sv);
    //@}

    /** @name Serialization & Deserialization
     */
    //@{
    /** Assign this value from a stream.
     * This method deserializes an object from given IValueStream.
     * All elements are deserialized from the value stream using assignFromStream().
     *
     * @param iv Value stream used for serialization
     */
    virtual void assignFromStream(IValueStream& iv);

    /** Assign this value from a stream.
    *  This method deserializes an object from given IValueStream.
     * All elements are deserialized from the value stream using createFromStream().
     *
     * @param iv Value stream used for serialization
     */
    virtual void createFromStream(IValueStream& iv);

    /** Write this value to a stream.
     * This method serializes an object to the given OValueStream.
     * All attributes registered for automatic serialization are serialized to the value stream.
     *
     * @param ov Value stream used for serialization
     */
    virtual void toStream(OValueStream& ov)          const;

    /** Enable or disable use of TValue wrappres.
     * This method enables or disables the use of TValue wrappers for serialization.
     * If the element type is a subclass of Value, no TValue wrappers may be used.
     * If the element type is not a subclass of Value, TValue wrappers must be used.
     * This method must be called explicitly after constructing a SequenceValue.
     *
     * @param use_tvalue @c true to use TValue wrappers, @c false to not use TValue wrappers
     */
    inline  void useTValue(bool use_tvalue)                   { __use_tvalue = use_tvalue; }
    //@}

    /** @name Memory Management
     */
    //@{
    /** Release this value.
     * This method invokes releaseValue() on all elements.
     */
    virtual void releaseValue()                   const;

    /** Virtual copy constructor.
     *
     * @return A copy of this object which has the same type
     */
    virtual SequenceValue<T>* copy()              const;
    //@}

    /** @name Type information
     */
    //@{
    /** Set type information.
     *
     * @param type_name Type name of the type of this object
     */
    inline void setTypeName(const string& type_name)  {__type_info.t_name = type_name;}

    /** Set type information.
     *
     * @param ns Type namespace of the type of this object
     */
    inline void setNamespace(const string& ns)        {__type_info.t_namespace = ns; }

    /** Get type information.
     *
     * @return Type name of the type of this object
     */
    inline  const string& getTypeName()           const { return __type_info.t_name; }

    /** Get type information.
     *
     * @return Type namespace of the type of this object
     */
    inline  const string& getNamespace()          const { return __type_info.t_namespace; }

    /** Get type information.
     *
     * @return Type information of the type of this object
     */
    inline const TypeInfo& getTypeInfo()  const       {return __type_info;}
    //@}

    /** Assignment operator.
     *
     * @param sv Object to assign from
     * @return *this
     */
    const SequenceValue<T>& operator=(const SequenceValue<T>& sv);

  private:
    /** Assignment.
     * This method implements the assignment operator.
     * It is used by the assignment operator and the copy constructor.
     *
     * @param sv Object to assign from
     * @return *this
     */
    const SequenceValue<T>& assign(const SequenceValue<T>& sv);

    /** This method erases all elements.
     */
    void eraseElements();

  private: //attributes
    TypeInfo  __type_info;    ///< Type info for the type of this object
    bool      __use_tvalue;   ///< Use TValue wrappers for serialization
  };





  /** SequenceValue implementation **/
  template<typename T>
  SequenceValue<T>::SequenceValue(size_t _size, MemoryMgm memory_mgm )
    : Value(Value::SEQUENCE, memory_mgm), vector<T*>(_size), __type_info("SequenceValue","NMM"), __use_tvalue(true)
  {
    /* Initialize the internal array with NULL pointer */
    for (unsigned int i = 0; i < _size; ++i)
      (*this)[i] = NULL;
  }

  template<typename T>
  SequenceValue<T>::SequenceValue(MemoryMgm memory_mgm )
    : Value(Value::SEQUENCE, memory_mgm), __type_info("SequenceValue", "NMM"), __use_tvalue(true)
  {
  }

  template<typename T>
  SequenceValue<T>::SequenceValue(const SequenceValue<T>& av)
    : Value(av), vector<T*>(av)
  {
    assign(av);
  }

  template<typename T>
  const SequenceValue<T>& SequenceValue<T>::operator=(const SequenceValue<T> & seq){
    /* cleanup old vector */
    eraseElements();

    /* call the operator= from the base class Value */
    Value::operator=(seq);
    /* call the operator= from the base class vector */
    vector<T*>::operator=(seq);

    /* create a copy of values in geven seq */
    return assign(seq);
  }

  template<typename T>
  const SequenceValue<T> & SequenceValue<T>::assign(const SequenceValue<T> & seq){
    /*Begin> create a copy of the new values*/
    __use_tvalue = seq.__use_tvalue;

    for (size_t i = 0; i< seq.size(); ++i)
      if (seq[i])
        (*this)[i] = dynamic_cast<T*>(seq[i]->copy());
      else
        (*this)[i] = 0;
    /*End */
    __type_info = seq.getTypeInfo();
    return *this;
  }

  template<typename T>
  void SequenceValue<T>::eraseElements() {
    /*Begin> release the old values */
    for (size_t i = 0; i< this->size(); ++i)
      if ((*this)[i])
        (*this)[i]->releaseValue();
    /*End< */
    this->clear();
  }

  template<typename T>
  void SequenceValue<T>::assignFromStream(IValueStream& ivs){
    /* Begin> Type checking */
    if ( (ivs.getCurrentType() != getTypeName()) || ( ivs.getCurrentNamespace() != getNamespace())) {
      GLOBAL_ERROR_STREAM ("Serialized Value> Current type : " << ivs.getCurrentType() << "                Expected type: " << getTypeName() << endl);
      GLOBAL_ERROR_STREAM ("Serialized Value> Current namespace : " << ivs.getCurrentNamespace() << "      Expected namespace: " << getNamespace() << endl);
      return;
    }
    /* End< */

    /* Begin> Try to access attributes */
    if (!ivs.goDown()) {
      GLOBAL_DEBUG_STREAM2("Array seems to be empty");
      ivs.nextElement();
      return;
    }
    /* End< */

    /* Should we TValue-Wrapper for all Values (which is need for Events) */
    ivs >> __use_tvalue;

    bool is_pointer = false;

    unsigned int i = 0;
    while(!ivs.isEmpty()) {
      /* Is the Value a Pointer Type ?*/
      ivs >> is_pointer;
      if (i < this->size()) {
        /* If an object already exist, use it*/
        if ((*this)[i]) {
          /* forward the assign fromStream call, if it exist */
          ((*this)[i])->assignFromStream(ivs);
        }
        /* Otherwise, we must create it */
        else {
          Value* tmp_d = 0;
          if (__use_tvalue) {
            if (is_pointer) {
              /* create TValue which manages a pointer to the object */
              tmp_d = SValueFactory::getInstance().createTPointerValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
            } else {
              /* create a TValue for the object */
              tmp_d = SValueFactory::getInstance().createTValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
            }
          } else {
            /* create a simple Value object, without TValue-Wrapper */
            tmp_d = SValueFactory::getInstance().createValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
          }

          /* Begin> Use the created Value with the given ValueStream */
          if (!tmp_d) {
            /* If no such object could be created, skip this element */
            ivs.nextElement(); //skip this element
            /* If no such object could be created, we receive a NULL pointer */
            (*this)[i] = 0;
          } else {
            /* Otherwise call createFromStream */
            tmp_d->createFromStream(ivs);

            /* Perform type check */
            T* t_value = dynamic_cast<T*>(tmp_d);
            if (t_value) {
              /* insert the element */
              (*this)[i] = t_value;
            } else {
              /* If the dynamic_cast fails, no such object with correct type could be created, set a NULL-Ptr */
              (*this)[i] = 0;
              /* delete the tmp_value */
              delete tmp_d;
            }
          }
          /* End< */
        }
      } else {
        Value* tmp = 0;
        if (__use_tvalue) {
          if (is_pointer) {
            /* create TValue wich manages a pointer to the object */
            tmp = SValueFactory::getInstance().createTPointerValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
          } else {
            /* create a TValue for the object */
            tmp = SValueFactory::getInstance().createTValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
          }
        } else {
          /* create a simple Value object, without TValue-Wrapper */
          tmp = SValueFactory::getInstance().createValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
        }
        /* Begin> Use the created Value with the given ValueStream */

        if ( tmp ) {
          /* read the value */
          tmp->createFromStream(ivs);

          T* t_value = dynamic_cast<T*>(tmp);
          if (t_value) {
            /* insert the element */
            push_back(t_value);
          } else {
            /* If the dynamic_cast fails, no such object could be created, insert a NULL-Ptr */
            this->push_back(0);
            /* delete the tmp_value */
            delete tmp;
          }
        } else {
          /* we received a NULL pointer and insert it */
          this->push_back(0);
          ivs.nextElement();
        }
      }
      ++i;
    }

    ivs.goUp();
  }


  template<typename T>
  void SequenceValue<T>::createFromStream(IValueStream& ivs){
    /* Begin> Type checking */
    if ( (ivs.getCurrentType() != getTypeName()) || (ivs.getCurrentNamespace() != getNamespace())) {
      GLOBAL_ERROR_STREAM ("Invalid Type");
      return;
    }
    /* End< */

    /* Begin> Try to access attributes */
    if (!ivs.goDown()) {
      GLOBAL_DEBUG_STREAM2 ("Array seems to be empty");
      ivs.nextElement();
      return;
    }
    /* End< */

    /* Begin> Release the old values and create new one */
    for (unsigned int i = 0; i< this->size(); ++i)
      if ((*this)[i])
        ((*this)[i]->releaseValue());
    /* End <*/

    /* Clear the internal parameters */
    this->clear();

    ivs >> __use_tvalue;

    bool is_pointer = false;

    Value* tmp = 0;
    while(!ivs.isEmpty()) {
      tmp = 0;
      /* Read the TValue-type that should be created */
      ivs >> is_pointer;

      if (__use_tvalue) {
        if (is_pointer) {
          /* create TValue wich manages a pointer to the object */
          tmp = SValueFactory::getInstance().createTPointerValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
        } else {
          /* create a TValue for the object */
          tmp = SValueFactory::getInstance().createTValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
        }
      } else {
        /* create a simple Value object, without TValue-Wrapper */
        tmp = SValueFactory::getInstance().createValue(ivs.getCurrentType(), ivs.getCurrentNamespace());
      }

      /* Begin> Use the created Value with the given ValueStream */
      T* value = dynamic_cast<T*>(tmp);
      if (value) {
        /* If the dynamic_cast was successfull, read the value */
        value->createFromStream(ivs);
        /* Insert the new value */
        push_back(value);
      } else {
        /* If the dynamic_cast fails, insert a NULL Pointer */
        this->push_back(0);
        ivs.nextElement();
        /* delete the tmp value */
        delete tmp;
      }
      /* End< */
    }

    ivs.goUp();
  }


  template<typename T>
  void SequenceValue<T>::toStream(OValueStream& ovs)  const{
    /* Write the TypeInfo */
    ovs.beginType(getTypeName(), getNamespace());

    ovs << __use_tvalue;
    for (unsigned int i = 0; i< this->size(); ++i) {
      if ((*this)[i])  {
        /* Write the pointer type */
        ovs << (*this)[i]->isPointerType();
        /* write the Value */
        (*this)[i]->toStream(ovs);
      } else {
        ovs << false; //No Pointer type
        /* Write the TypeInfo for a NULL-Pointer */
        ovs.beginType("NULL", "std");
        ovs.endType();
      }
    }
    ovs.endType();
  }

  template<typename T>
  SequenceValue<T>* SequenceValue<T>::copy()            const{
    if (getMemoryStrategy() != Value::INVALUE)
      return const_cast<SequenceValue<T>*>(this);

    return new SequenceValue<T>(*this);
  }

  template<typename T>
  void SequenceValue<T>::releaseValue() const {
    if (getMemoryStrategy() == Value::INVALUE) {
      for (unsigned int i = 0; i< this->size(); ++i) {
        if ( (*this)[i] )
          (*this)[i]->releaseValue();
      }
    }
  }
  
}

#include "nmm/nmm_suffix.hpp"

#endif
