//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / 
//
//File: mibobject.cpp
//
//Version: $Revision: 1.37 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/16 16:58:20 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Sami Raatikainen
//
//Description:
//      See corresponding header file.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 

#include "mibobject.h"
#include "mibexception.h"
#include "pf/error.h"
#include "pf/exception.h"
#include "pf/debug.h"
#include "protocol/ilmi/asn/rfc1155-smi.h"
#include "protocol/ilmi/asn/rfc1157-snmp.h"


mibObject :: mibObject(void)
{
    return;
}

mibObject :: ~mibObject(void)
{
    return;
}

bool mibObject :: isNode(void) const
{
    return 0;
}

bool mibObject :: isTable(void) const
{
    return 0;
}

mibObject *mibObject :: createNode(void)
{
    mibObject *newObject = new mibNode();
    return newObject;
}

mibObject *mibObject :: createTable(void)
{
    mibObject *newObject = new mibTable();
    return newObject;
}

mibObject *mibObject :: createAsnIntColumn(const string &objectID_,
                                           mibDestination *destination_)
{
    convert *convertType = new asnIntConvert;
    mibObject *newObject = new mibColumn(objectID_, destination_, convertType);
    return newObject;
}

mibObject *mibObject :: createAsnOctsColumn(const string &objectID_,
                                            mibDestination *destination_)
{
    convert *convertType = new asnOctsConvert;
    mibObject *newObject = new mibColumn(objectID_, destination_, convertType);
    return newObject;
}

mibObject *mibObject :: createAsnOidColumn(const string &objectID_,
                                           mibDestination *destination_)
{
    convert *convertType = new asnOidConvert;
    mibObject *newObject = new mibColumn(objectID_, destination_, convertType);
    return newObject;
}

mibObject *mibObject :: createAsnIntLeaf(const string &objectID_,
                                         mibDestination *destination_,
                                         bool writeAccess_)
{
    convert *convertType = new asnIntConvert;
    mibObject *newObject = new mibLeaf(objectID_,
                                       destination_,
                                       convertType,
                                       writeAccess_);
    return newObject;
}

mibObject *mibObject :: createAsnOctsLeaf(const string &objectID_,
                                         mibDestination *destination_,
                                         bool writeAccess_)
{
    convert *convertType = new asnOctsConvert;
    mibObject *newObject = new mibLeaf(objectID_,
                                       destination_,
                                       convertType,
                                       writeAccess_);
    return newObject;
}

mibObject *mibObject :: createAsnOidLeaf(const string &objectID_,
                                         mibDestination *destination_,
                                         bool writeAccess_)
{
    convert *convertType = new asnOidConvert;
    mibObject *newObject = new mibLeaf(objectID_,
                                       destination_,
                                       convertType,
                                       writeAccess_);
    return newObject;
}


//
//Functions: default methods
//
//Description:
//      Throws an exception if trying some method with kind of
//      mibObject, except get returns NULL and getNext returns 0.
//

mibObject *mibObject :: get(asnUtil::oidList &) const
{
    mibObject *nullValue = NULL;
    return nullValue;
}

bool mibObject :: getNext(asnUtil::oidList &, VarBind *) const
{
    return 0;
}

bool mibObject :: getFirstInstance(VarBind *) const
{
    throw pfException(PF_EX_INFO);
    return 0;
}

void mibObject :: getValue(VarBind *) const
{
    throw mibException(PDUInt::noSuchName);
    return;
}

void mibObject :: setValue(VarBind *) const
{
    throw mibException(PDUInt::noSuchName);
    return;
}

void mibObject :: stringToVarBind(VarBind *, const string &,
                                  asnUtil::oidList &) const
{
    throw mibException(PDUInt::noSuchName);
    return;
}

void mibObject :: varBindToString(VarBind *, string &,
                                  asnUtil::oidList &) const
{
    throw mibException(PDUInt::noSuchName);
    return;
}

void mibObject :: add(pfUlong, mibObject *)
{
    throw pfException(PF_EX_INFO);
    return;
}


// class mibNode ----------------------------
//
//Function: constructor /destructor
//
//Description:
//
//

mibNode :: mibNode(void)
    : mibObject(),
      _objectMap()
{
    return;
}

mibNode :: ~mibNode(void)
{
    objIterator mapIter = _objectMap.begin();
    while(mapIter != _objectMap.end())
    {
        delete (*mapIter).second;
        mapIter++;
    }
    return;
}

bool mibNode :: isNode(void) const
{
    return 1;
}

//
//Function: get
//
//Description:
//    Returns a pointer to the object (recursively) or this node,
//    if none of the subnodes are correct.
//    NOTE! removes this object (current number) from the oidList.
//

mibObject* mibNode :: get(asnUtil::oidList &oidList_) const
{
    mibObject *value = (mibObject *)this;
    if(oidList_.front() > 0)
    {
        objIterator mapIter = _objectMap.find(oidList_.front());
        if(mapIter != _objectMap.end())
        {
            oidList_.pop_front();
            value = ((*mapIter).second)->get(oidList_);
        }
    }
    return value;
}

//
//Function: getNext
//
//Description:
//    Iterate recursively to the correct instance.
//

bool mibNode :: getNext(asnUtil::oidList &oidList_,
                             VarBind *varBind_) const
{
    bool nextFound = 0;
    objIterator mapIter = _objectMap.begin();
    while(mapIter != _objectMap.end() && nextFound == 0)
    {
        if(oidList_.empty() || (*mapIter).first > oidList_.front())
        {
            nextFound = ((*mapIter).second)->getFirstInstance(varBind_);
        }
        else if((*mapIter).first == oidList_.front())
        {
            oidList_.pop_front();
            nextFound = ((*mapIter).second)->getNext(oidList_, varBind_);
        }
        mapIter++;
    }
    return nextFound;
}

//
//Function: getFirstInstance
//
//Description:
//    Returns a pointer to the first leaf (recursively) or NULL.
//    Used via getNext-method.
//

bool mibNode :: getFirstInstance(VarBind *varBind_) const
{
    bool nextFound = 0;
    objIterator mapIter = _objectMap.begin();
    while(mapIter != _objectMap.end() && nextFound == 0)
    {
        nextFound = ((*mapIter).second)->getFirstInstance(varBind_);
        mapIter++;
    }
    return nextFound;
}    


//
//Function: add
//
//Description:
//      Adds new object into this object's map with the given key.
//

void mibNode :: add(pfUlong key_, mibObject *newObject_)
{
    if(_objectMap.insert(objMapType::value_type(key_, newObject_)).second == 0)
    {
        throw pfException(PF_EX_INFO);
    }
    return;
}


// class mibTable -----------------------------
//
//Function: constructor /destructor
//
//Description:
//
//

mibTable :: mibTable(void)
    : mibObject(),
      _colMap(),
      _rowMap()
{
    return;
}

mibTable :: ~mibTable(void)
{
    colIterator colIter = _colMap.begin();
    while(colIter != _colMap.end())
    {
        delete (*colIter).second;
        colIter++;
    }
    clear();
    return;
}

bool mibTable :: isTable(void) const
{
    return 1;
}

//
//Functions: checkOid / popOidFromList
//
//Description:
//      Throws an exception if oidList is empty.
//

void mibTable :: checkOid(const asnUtil::oidList &oidList_) const
{
    if(oidList_.empty())
    {
        throw mibException(PDUInt::noSuchName);
    }
    return;
}

void mibTable :: popOidFromList(asnUtil::oidList &oidList_) const
{
    checkOid(oidList_);
    oidList_.pop_front();
    return;
}


//
//Function: get
//
//Description:
//      Returns always else returns itself.
//

mibObject* mibTable :: get(asnUtil::oidList &) const
{
    mibObject *obj = (mibObject *)this;
    return obj;
}

//
//Function: getNext
//
//Description:
//      Iterate recursively to the correct instance (col/row, or
//      the next instance after this table). There are so many
//      if-else statements because you have to check entries too.
//

bool mibTable :: getNext(asnUtil::oidList &oidList_,
                              VarBind *varBind_) const
{
    bool nextFound = 0;
    if(oidList_.empty() || oidList_.front() < 1)
    {
        nextFound = getFirstInstance(varBind_);
    }
    else if(oidList_.front() == 1)
    {
        popOidFromList(oidList_);   // entry
        if(oidList_.empty())
        {
            nextFound = getFirstInstance(varBind_);
        }
        else
        {
            string rowIndex, searchIndex;
            rowIterator rowIter;
            pfUlong colIndex = oidList_.front();
            popOidFromList(oidList_);   // column index
            searchIndex = asnUtil::listToString(oidList_);
            colIterator colIter = _colMap.begin();
            while(colIter != _colMap.end() && rowIndex.empty())
            {
                if((*colIter).first > colIndex)
                {
                    nextFound = getFirstInstance(varBind_);
                }
                else if((*colIter).first == colIndex)
                {
                    rowIter = _rowMap.begin();
                    while(rowIter != _rowMap.end() && nextFound == 0)
                    {
                        if(((*rowIter).first).compare(searchIndex) > 0)
                        {
                            rowIndex = (*rowIter).first;
                            (*colIter).second->getRow(varBind_, rowIndex);
                            nextFound = 1;
                        }
                        rowIter++;
                    }
                }
                colIter++;
            }
        }
    }
    return nextFound;
}

//
//Function: getFirstInstance
//
//Description:
//      Uses the first instance (first col, first row) to get the value.
//      Returns true if value found.

bool mibTable :: getFirstInstance(VarBind *varBind_) const
{
    bool nextFound = 0;
    if(_rowMap.empty() == 0 && _colMap.empty() == 0)
    {
        colIterator colIter = _colMap.begin();
        rowIterator rowIter = _rowMap.begin();
        string rowIndex = (*rowIter).first;
        (*colIter).second->getRow(varBind_, rowIndex);
        nextFound = 1;
    }
    return nextFound;
}

//
//Functions: getValue, setValue
//
//Description:
//      Doesn't add any rows, or check if requested instance is in the
//      table. Checks only if there is a column where the instance could
//      be and then passes the request through to the mibDestination.
//

void mibTable :: getValue(VarBind *varBind_, asnUtil::oidList &oidList_) const
{
    mibObject *colObj = getColumn(oidList_);
    popOidFromList(oidList_);  // remove column index
    if(oidList_.empty())
    {
        throw mibException(PDUInt::noSuchName);
    }
    string key = asnUtil::listToString(oidList_);
    rowIterator rowIter = _rowMap.find(key);
    if(rowIter == _rowMap.end())
    {
        throw mibException(PDUInt::noSuchName);
    }
    colObj->getValue(varBind_);
    return;
}

void mibTable :: setValue(VarBind *varBind_, asnUtil::oidList &oidList_) const
{
    mibObject *colObj = getColumn(oidList_);
    colObj->setValue(varBind_);
    return;
}

//
//Functions: stringToVarBind, varBindToString
//
//Description:
//      Uses the column's corresponding method.
//

void mibTable :: stringToVarBind(VarBind *varBind_,
                                 const string &svalue_,
                                 asnUtil::oidList &oidList_) const
{
    mibObject *colObj = getColumn(oidList_);
    colObj->stringToVarBind(varBind_, svalue_, oidList_);
    return;
}

void mibTable :: varBindToString(VarBind *varBind_,
                                 string &svalue_,
                                 asnUtil::oidList &oidList_) const
{
    mibObject *colObj = getColumn(oidList_);
    colObj->varBindToString(varBind_, svalue_, oidList_);
    return;
}


//
//Functions: methods to handle columns and rows.
//
//Description:
//      getColumn returns the column indexed by *oidIter_. Have to
//      check the list (is it empty) before removing items from it.
//

mibObject *mibTable :: getColumn(asnUtil::oidList &oidList_) const
{
    popOidFromList(oidList_);   // ignore table entry
    checkOid(oidList_);
    colIterator colIter = _colMap.find(oidList_.front());
    if(colIter == _colMap.end())
    {
        throw mibException(PDUInt::noSuchName);
    }
    return (*colIter).second;
}

void mibTable :: add(pfUlong key_, mibObject *newObject_)
{
    mibColumn *colObject = dynamic_cast<mibColumn *>(newObject_);
    THROW_IF_DYNAMIC_CAST_FAILED(colObject);
    if(_colMap.insert(colMapType::value_type(key_, colObject)).second == 0)
    {
        throw pfException(PF_EX_INFO);
    }
    return;
}

void mibTable :: addRow(const string &index_)
{
    bool valid = 1;
    if(_rowMap.insert(rowMapType::value_type(index_, valid)).second == 0)
    {
        throw pfException(PF_EX_INFO);
    }
    debugString("added row: ", index_);
    return;
}

void mibTable :: deleteRow(const string &index_)
{
    if(_rowMap.erase(index_) == 0)
    {
        throw mibException(PDUInt::noSuchName);
    }
    debugString("deleted row: ", index_);
    return;
}

void mibTable :: clear(void)
{
#ifndef NON_STD_STL   
    _rowMap.clear();
#else
    _rowMap.erase(_rowMap.begin(), _rowMap.end());
#endif // NON_STD_STL    
    return;
}


// class mibColumn ------------------------------
//
//Function: constructor /destructor
//
//Description:
//
//

mibColumn :: mibColumn(const string &objectID_,
                       mibDestination *destination_,
                       convert *convert_)
    : mibObject(),
      _stringOid(objectID_),
      _destination(destination_),
      _convert(convert_)
{
    return;
}

mibColumn :: ~mibColumn(void)
{
    delete _convert;
    _convert = 0;
    _destination = 0;
    return;
}

//
//Function: getRow 
//
//Description:
//      Puts the new objectID into the varBind_, and calls getValue-
//      method to the object. This method is used with getNext-request.
//

void mibColumn :: getRow(VarBind *varBind_, const string &rowID_) const
{
    string objectID;
    int length = _stringOid.length();
    if(_stringOid.compare(".", length) && rowID_.compare(".", 0))
    {
        objectID = _stringOid + '.' + rowID_;
    }
    else
    {
        objectID = _stringOid + rowID_;
    }
    debugString("mibColumn::getNext  instance:", objectID);
    AsnOid oid = asnUtil::stringToAsnOid(objectID);
    varBind_->name = oid;
    getValue(varBind_);
    return;
}

//
//Function: getValue
//
//Description:
//      Gets value from mibDestination, converts it to corresponding
//      AsnType, and sets it to varBind_. ObjectID (wanted instance)
//      consists from the whole objectID (col+index)
//

void mibColumn :: getValue(VarBind *varBind_) const
{
    AsnOid oid = varBind_->name;
    string objectID = asnUtil::asnOidToString(oid);
    debugString("mibColumn :: getValue", objectID);
    string value = _destination->getValue(objectID);
    debugString("value received: ", value);
    _convert->setStringValue(varBind_, value);
    return;
}

//
//Function: setValue
//
//Description:
//      Converts the AsnTyped value from varBind_ to a string,
//      and calls the mibDestination's setValue w/ objectID
//      (column+row).
//

void mibColumn :: setValue(VarBind *varBind_) const
{
    AsnOid oid = varBind_->name;
    string objectID = asnUtil::asnOidToString(oid);
    debugString("mibColumn :: setValue", objectID);
    string svalue = _convert->getStringValue(varBind_);
    _destination->setValue(objectID, svalue);
    return;
}

//
//Function: stringToVarBind, varBindToString
//
//Description:
//      Uses object's convert functions to convert a string
//      to a AsnType value and vice versa.
//

void mibColumn :: stringToVarBind(VarBind *varBind_,
                                  const string &svalue_,
                                  asnUtil::oidList &) const
{
    _convert->setStringValue(varBind_, svalue_);
    return;
}

void mibColumn :: varBindToString(VarBind *varBind_,
                                  string &svalue_,
                                  asnUtil::oidList &) const
{
    svalue_ = _convert->getStringValue(varBind_);
    return;
}


// class mibLeaf ----------------------------
//
//Function: constructor /destructor
//
//Description:
//      WriteAccess, mibDestination, and convert will be set as they are,
//      and Object ID will be set as a string.
//      At destructor convert will be deleted, but mibDestination may not
//      be deleted.
//

mibLeaf :: mibLeaf(const string &objectID_,
                   mibDestination *destination_,
                   convert *convert_,
                   bool writeAccess_)
    : mibObject(),
      _stringOid(objectID_),
      _destination(destination_),
      _convert(convert_),
      _writeAccess(writeAccess_)
{
    return;
}

mibLeaf :: ~mibLeaf(void)
{
    delete _convert;
    _convert = 0;
    _destination = 0;
    return;
}

mibObject *mibLeaf :: get(asnUtil::oidList &oidList_) const
{
    mibObject *value = NULL;
    if(oidList_.empty())
    {
        value = (mibObject *)this;
    }
    return value;
}

//
//Function: getFirstInstance
//
//Description:
//      Gets value for this object, and sets new name to varBind_.
//      This method will be called eventually from mibNode::getNext().
//

bool mibLeaf :: getFirstInstance(VarBind *varBind_) const
{
    getValue(varBind_);
    AsnOid oid = asnUtil::stringToAsnOid(_stringOid);
    varBind_->name = oid;
    return 1;
}

//
//Function: getValue
//
//Description:
//      Gets value from mibDestination, converts it to corresponding
//      AsnType, and sets it to varBind_.
//

void mibLeaf :: getValue(VarBind *varBind_) const
{
    string value = _destination->getValue(_stringOid);
    _convert->setStringValue(varBind_, value);
    return;
}

//
//Function: setValue
//
//Description:
//      Converts the AsnTyped value from varBind_ to a string,
//      and calls the mibDestination's setValue w/ own objectID
//      and converted stringvalue.
//

void mibLeaf :: setValue(VarBind *varBind_) const
{
    if(_writeAccess == 0)
    {
        throw mibException(PDUInt::noSuchName);
    }
    string svalue = _convert->getStringValue(varBind_);
    _destination->setValue(_stringOid, svalue);
    return;
}

//
//Functions: stringToVarBind, varBindToString
//
//Description:
//      See mibColumn's corresponding methods.
//

void mibLeaf :: stringToVarBind(VarBind *varBind_,
                                const string &svalue_,
                                asnUtil::oidList &) const
{
    _convert->setStringValue(varBind_, svalue_);
    return;
}

void mibLeaf :: varBindToString(VarBind *varBind_,
                                string &svalue_,
                                asnUtil::oidList &) const
{
    svalue_ = _convert->getStringValue(varBind_);
    return;
}
