// This file may be redistributed and modified only under the terms of
// the GNU Lesser General Public License (See COPYING for details).
// Copyright (C) 2000-2001 Stefanus Du Toit and Aloril

#include <Atlas/Objects/BaseObject.h>

using Atlas::Message::Element;
using Atlas::Message::MapType;

namespace Atlas { namespace Objects {

NoSuchAttrException::~NoSuchAttrException() throw ()
{
}

BaseObjectData::BaseObjectData(BaseObjectData *defaults) :
    m_class_no(BASE_OBJECT_NO), m_refCount(0), m_defaults(defaults),
    m_attrFlags(0)
{
    if(defaults == NULL) {
        m_attrFlags = -1; //this is default object: all attributes here
    }
}

BaseObjectData::~BaseObjectData()
{
    assert( m_refCount==0 );
}

bool BaseObjectData::instanceOf(int classNo) const
{
    return BASE_OBJECT_NO == classNo;
}

bool BaseObjectData::hasAttr(const std::string& name) const
{
    int flag = getAttrFlag(name);
    if (flag >= 0) {
        return m_attrFlags & flag;
    } else {
        return (m_attributes.find(name) != m_attributes.end());
    }
}

bool BaseObjectData::hasAttrFlag(int flag) const
{
    return m_attrFlags & flag;
}

const Element BaseObjectData::getAttr(const std::string& name) const
    throw (NoSuchAttrException) 
{
    Element attr;
    if (copyAttr(name, attr) != 0) {
        throw NoSuchAttrException(name);
    }
    return attr;
}

int BaseObjectData::copyAttr(const std::string& name, Element & attr) const
{
    MapType::const_iterator I = m_attributes.find(name);
    if (I == m_attributes.end()) {
        return -1;
    };
    attr = I->second;
    return 0;
}

void BaseObjectData::setAttr(const std::string& name, const Element& attr)
{
    m_attributes[name] = attr;
}

void BaseObjectData::removeAttr(const std::string& name)
{
    int flag = getAttrFlag(name);
    if (flag >= 0) {
        removeAttrFlag(flag);
    } else {
        m_attributes.erase(name);
    }
}

void BaseObjectData::removeAttrFlag(int flag)
{
    m_attrFlags &= ~flag;
}

const MapType BaseObjectData::asMessage() const
{
    MapType m;
    addToMessage(m);
    return m;
}

void BaseObjectData::addToMessage(MapType & m) const
{
    typedef MapType::const_iterator Iter;
    for (Iter I = m_attributes.begin(); I != m_attributes.end(); I++) {
        m[I->first] = I->second;
    }
}

void BaseObjectData::sendContents(Bridge & b) const
{
    Message::Encoder e(b);
    typedef MapType::const_iterator Iter;
    for (Iter I = m_attributes.begin(); I != m_attributes.end(); I++) {
        e.mapElementItem((*I).first, (*I).second);
    }
}

int BaseObjectData::getAttrClass(const std::string& name) const
{
    return -1;
}

int BaseObjectData::getAttrFlag(const std::string& name) const
{
    return -1;
}

void BaseObjectData::iterate(int& current_class, std::string& attr) const
{
    // m_attributes is handled separately, and we have no other attributes,
    // so we set the iterator to be at the end of the attributes

    current_class = BASE_OBJECT_NO;
    attr = "";
}

BaseObjectData::iterator::iterator(BaseObjectData& obj, int current_class)
    : m_obj(&obj), m_current_class(current_class), m_val("", *this)
{
    // invalid argument means start at the top
    m_I = (current_class < 0) ? m_obj->m_attributes.begin() : m_obj->m_attributes.end();

    // could have m_attributes.begin() == m_attributes.end(), need to check
    if(m_I != m_obj->m_attributes.end())
        m_val.first = m_I->first;
    else
        m_obj->iterate(m_current_class, m_val.first); // get first named attribute
}

// don't set m_val.second
BaseObjectData::iterator& BaseObjectData::iterator::operator=(const iterator& I)
{
    m_obj = I.m_obj;
    m_current_class = I.m_current_class;
    m_I = I.m_I;
    m_val.first = I.m_val.first;
    return *this;
}

// We go through the attributes in the top class
// first and work our way down, since the virtual iterate() function
// works that way.
BaseObjectData::iterator& BaseObjectData::iterator::operator++() // preincrement
{
    if(!m_obj)
        return *this;

    // Since we start with the top class first,
    // we always want attributes that are not
    // named in a particular class to come before
    // those that are. The attributes in m_attributes
    // are not named in any class, so they come first

    if(m_I != m_obj->m_attributes.end()) {
        ++m_I;
        if(m_I != m_obj->m_attributes.end()) {
            m_val.first = m_I->first;
            return *this;
        }
        // end of m_attributes, clear m_val.first so we can start on the rest
        m_val.first = "";
    }

    // we're through m_attributes, so we go on to the named
    // attributes in the classes

    m_obj->iterate(m_current_class, m_val.first);

    return *this;
}

bool BaseObjectData::iterator::operator==(const iterator& I) const
{
    if(m_obj != I.m_obj)
        return false;
    if(m_obj == 0) // ignore other arguments
        return true;

    if(m_I != I.m_I)
        return false;
    if(m_I != m_obj->m_attributes.end()) // ignore other arguments
        return true;

    assert(m_current_class >= 0); // we must be in the class arguments by now
    return m_current_class == I.m_current_class && m_val.first == I.m_val.first;
}

// FIXME figure out some way to use m_current_class to keep from
// having to call the virtual getAttr()/setAttr() in the toplevel class

BaseObjectData::iterator::PsuedoElement::operator Message::Element() const
{
    return (m_I.m_I != m_I.m_obj->m_attributes.end()) ?
	m_I.m_I->second : m_I.m_obj->getAttr(m_I.m_val.first);
}

const BaseObjectData::iterator::PsuedoElement& BaseObjectData::iterator::PsuedoElement::operator=(const Message::Element& val) const
{
    if(m_I.m_I != m_I.m_obj->m_attributes.end())
        m_I.m_I->second = val;
    else
        m_I.m_obj->setAttr(m_I.m_val.first, val);

    return *this;
}

BaseObjectData::const_iterator::const_iterator(const BaseObjectData& obj,
					       int current_class)
    : m_obj(&obj), m_current_class(current_class), m_val("", *this)
{
    // invalid argument means start at the top
    m_I = (current_class < 0) ? m_obj->m_attributes.begin() : m_obj->m_attributes.end();

    // could have m_attributes.begin() == m_attributes.end(), need to check
    if(m_I != m_obj->m_attributes.end())
        m_val.first = m_I->first;
    else
        m_obj->iterate(m_current_class, m_val.first); // get first named attribute
}

// don't set m_val.second
BaseObjectData::const_iterator& BaseObjectData::const_iterator::operator=(const const_iterator& I)
{
    m_obj = I.m_obj;
    m_current_class = I.m_current_class;
    m_I = I.m_I;
    m_val.first = I.m_val.first;
    return *this;
}

// We go through the attributes in the top class
// first and work our way down, since the virtual iterate() function
// works that way.
BaseObjectData::const_iterator& BaseObjectData::const_iterator::operator++() // preincrement
{
    if(!m_obj)
        return *this;

    // Since we start with the top class first,
    // we always want attributes that are not
    // named in a particular class to come before
    // those that are. The attributes in m_attributes
    // are not named in any class, so they come first

    if(m_I != m_obj->m_attributes.end()) {
        ++m_I;
        if(m_I != m_obj->m_attributes.end()) {
            m_val.first = m_I->first;
            return *this;
        }
        // end of m_attributes, clear m_val.first so we can start on the rest
        m_val.first = "";
    }

    // we're through m_attributes, so we go on to the named
    // attributes in the classes

    m_obj->iterate(m_current_class, m_val.first);

    return *this;
}

bool BaseObjectData::const_iterator::operator==(const const_iterator& I) const
{
    if(m_obj != I.m_obj)
        return false;
    if(m_obj == 0) // ignore other arguments
        return true;

    if(m_I != I.m_I)
        return false;
    if(m_I != m_obj->m_attributes.end()) // ignore other arguments
        return true;

    assert(m_current_class >= 0); // we must be in the class arguments by now
    return m_current_class == I.m_current_class && m_val.first == I.m_val.first;
}

// FIXME figure out some way to use m_current_class to keep from
// having to call the virtual getAttr()/setAttr() in the toplevel class

BaseObjectData::const_iterator::PsuedoElement::operator Message::Element() const
{
    return (m_I.m_I != m_I.m_obj->m_attributes.end()) ?
	m_I.m_I->second : m_I.m_obj->getAttr(m_I.m_val.first);
}

BaseObjectData::iterator BaseObjectData::find(const std::string& name)
{
  iterator I;
  I.m_obj = this;
  I.m_val.first = name;

  I.m_I = m_attributes.find(name);
  if (I.m_I != m_attributes.end()) 
    I.m_current_class = -1;
  else {
    I.m_current_class = getAttrClass(name);
    if(I.m_current_class < 0) { // no such attribute
      I.m_current_class = BASE_OBJECT_NO;
      I.m_val.first = "";
    }
  }

  return I;
}

BaseObjectData::const_iterator BaseObjectData::find(const std::string& name) const
{
  const_iterator I;
  I.m_obj = this;
  I.m_val.first = name;

  I.m_I = m_attributes.find(name);
  if (I.m_I != m_attributes.end()) 
    I.m_current_class = -1;
  else {
    I.m_current_class = getAttrClass(name);
    if(I.m_current_class < 0) { // no such attribute
      I.m_current_class = BASE_OBJECT_NO;
      I.m_val.first = "";
    }
  }

  return I;
}


} } // namespace Atlas::Objects


syntax highlighted by Code2HTML, v. 0.9.1