/* -*- C++ -*- */

/****************************************************************************
** Copyright (c) 2001-2014
**
** This file is part of the QuickFIX FIX Engine
**
** This file may be distributed under the terms of the quickfixengine.org
** license as defined by quickfixengine.org and appearing in the file
** LICENSE included in the packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.quickfixengine.org/LICENSE for licensing information.
**
** Contact ask@quickfixengine.org if any conditions of this licensing are
** not clear to you.
**
****************************************************************************/

#ifndef FIX_FIELDMAP
#define FIX_FIELDMAP

#ifdef _MSC_VER
#pragma warning( disable: 4786 )
#endif

#include "Field.h"
#include "MessageSorters.h"
#include "Exceptions.h"
#include "Utility.h"
#include <map>
#include <vector>
#include <sstream>
#include <algorithm>

namespace FIX
{
/**
 * Stores and organizes a collection of Fields.
 *
 * This is the basis for a message, header, and trailer.  This collection
 * class uses a sorter to keep the fields in a particular order.
 */
class FieldMap
{
public:
#if defined(_MSC_VER) && _MSC_VER < 1300
  typedef std::multimap < int, FieldBase, message_order > Fields;
  typedef std::map < int, std::vector < FieldMap* >, std::less<int> > Groups;
#else
  typedef std::multimap < int, FieldBase, message_order, 
                          ALLOCATOR<std::pair<const int,FieldBase> > > Fields;
  typedef std::map < int, std::vector < FieldMap* >, std::less<int>, 
                     ALLOCATOR<std::pair<const int, std::vector< FieldMap* > > > > Groups;
#endif

  typedef Fields::const_iterator iterator;
  typedef iterator const_iterator;
  typedef Groups::const_iterator g_iterator;
  typedef Groups::const_iterator g_const_iterator;

  FieldMap( const message_order& order =
            message_order( message_order::normal ) )
  : m_fields( order ) {}

  FieldMap( const int order[] )
  : m_fields( message_order(order) ) {}

  FieldMap( const FieldMap& copy )
  { *this = copy; }

  virtual ~FieldMap();

  FieldMap& operator=( const FieldMap& rhs );

  /// Set a field without type checking
  void setField( const FieldBase& field, bool overwrite = true )
  throw( RepeatedTag )
  {
      if(!overwrite)
          m_fields.insert( Fields::value_type( field.getField(), field ) );
      else
      {
          Fields::iterator i = m_fields.find( field.getField() );
          if( i == m_fields.end() )
              m_fields.insert( Fields::value_type( field.getField(), field ) );
          else
              i->second = field;
      }
  }
  /// Set a field without a field class
  void setField( int field, const std::string& value )
  throw( RepeatedTag, NoTagValue )
  {
    FieldBase fieldBase( field, value );
    setField( fieldBase );
  }

  /// Get a field if set
  bool getFieldIfSet( FieldBase& field ) const
  {
    Fields::const_iterator iter = m_fields.find( field.getField() );
    if ( iter == m_fields.end() )
      return false;
    field = iter->second;
    return true;
  }

  /// Get a field without type checking
  FieldBase& getField( FieldBase& field )
  const throw( FieldNotFound )
  {
    field = getFieldRef( field.getField() );
    return field;
  }

  /// Get a field without a field class
  const std::string& getField( int field )
  const throw( FieldNotFound )
  {
    return getFieldRef( field ).getString();
  }

  /// Get direct access to a field through a reference
  const FieldBase& getFieldRef( int field )
  const throw( FieldNotFound )
  {
    Fields::const_iterator iter = m_fields.find( field );
    if ( iter == m_fields.end() )
      throw FieldNotFound( field );
    return iter->second;
  }

  /// Get direct access to a field through a pointer
  const FieldBase* const getFieldPtr( int field )
  const throw( FieldNotFound )
  {
    return &getFieldRef( field );
  }

  /// Check to see if a field is set
  bool isSetField( const FieldBase& field ) const
  { return isSetField( field.getField() ); }
  /// Check to see if a field is set by referencing its number
  bool isSetField( int field ) const
  { return m_fields.find( field ) != m_fields.end(); }

  /// Remove a field. If field is not present, this is a no-op.
  void removeField( int field );

  /// Add a group.
  void addGroup( int field, const FieldMap& group, bool setCount = true );

  /// Acquire ownership of Group object
  void addGroupPtr( int field, FieldMap * group, bool setCount = true );

  /// Replace a specific instance of a group.
  void replaceGroup( int num, int field, const FieldMap& group );

  /// Get a specific instance of a group.
  FieldMap& getGroup( int num, int field, FieldMap& group ) const
  throw( FieldNotFound )
  {
    return group = getGroupRef( num, field );
  }

  /// Get direct access to a field through a reference
  FieldMap& getGroupRef( int num, int field ) const
  throw( FieldNotFound )
  {
    Groups::const_iterator i = m_groups.find( field );
    if( i == m_groups.end() ) throw FieldNotFound( field );
    if( num <= 0 ) throw FieldNotFound( field );
    if( i->second.size() < (unsigned)num ) throw FieldNotFound( field );
    return *( *(i->second.begin() + (num-1) ) );
  }

  /// Get direct access to a field through a pointer
  FieldMap* getGroupPtr( int num, int field ) const
  throw( FieldNotFound )
  {
    return &getGroupRef( num, field );
  }

  /// Remove a specific instance of a group.
  void removeGroup( int num, int field );
  /// Remove all instances of a group.
  void removeGroup( int field );

  /// Check to see any instance of a group exists
  bool hasGroup( int field ) const;
  /// Check to see if a specific instance of a group exists
  bool hasGroup( int num, int field ) const;
  /// Count the number of instance of a group
  size_t groupCount( int field ) const;

  /// Clear all fields from the map
  void clear();
  /// Check if map contains any fields
  bool isEmpty();

  size_t totalFields() const;

  std::string& calculateString( std::string& ) const;

  int calculateLength( int beginStringField = FIELD::BeginString,
                       int bodyLengthField = FIELD::BodyLength,
                       int checkSumField = FIELD::CheckSum ) const;

  int calculateTotal( int checkSumField = FIELD::CheckSum ) const;

  iterator begin() const { return m_fields.begin(); }
  iterator end() const { return m_fields.end(); }
  g_iterator g_begin() const { return m_groups.begin(); }
  g_iterator g_end() const { return m_groups.end(); }

private:
  Fields m_fields;
  Groups m_groups;
};
/*! @} */
}

#define FIELD_SET( MAP, FIELD )           \
bool isSet( const FIELD& field ) const    \
{ return (MAP).isSetField(field); }       \
void set( const FIELD& field )            \
{ (MAP).setField(field); }                \
FIELD& get( FIELD& field ) const          \
{ return (FIELD&)(MAP).getField(field); } \
bool getIfSet( FIELD& field ) const       \
{ return (MAP).getFieldIfSet(field); }

#define FIELD_GET_PTR( MAP, FLD ) \
(const FIX::FLD*)MAP.getFieldPtr( FIX::FIELD::FLD )
#define FIELD_GET_REF( MAP, FLD ) \
(const FIX::FLD&)MAP.getFieldRef( FIX::FIELD::FLD )
#define FIELD_THROW_IF_NOT_FOUND( MAP, FLD ) \
if( !(MAP).isSetField( FIX::FIELD::FLD) ) \
  throw FieldNotFound( FIX::FIELD::FLD )
#endif //FIX_FIELDMAP

