//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / 
//
//File: mibtree.cpp
//
//Version: $Revision: 1.33 $
//
//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 "mibtree.h"
#include "mibexception.h"
#include "pf/error.h"
#include "protocol/ilmi/asn/asnutil.h"
#include "protocol/ilmi/asn/rfc1155-smi.h"
#include "protocol/ilmi/asn/rfc1157-snmp.h"

//
//Functions: constructor / destructor
//
//Description:
//      _root is set to be just a node to give access to objects
//      in the tree.
//

mibTree :: mibTree(void)
    : _root(0)
{
    _root = mibObject::createNode();
    return;
}

mibTree :: ~mibTree(void)
{
    delete _root;
    _root = 0;
    return;
}

//
//Function: getObject
//
//Description:
//    Finds the mibObject traversing the tree from the root.
//    Removes oids from the oidList along traversing, because
//    if the object is in a table, we need to save the information
//    about the column and index (row) in the table.
//

mibObject *mibTree :: getObject(asnUtil::oidList &oidList_) const
{
    mibObject *obj = _root->get(oidList_);
    checkObject(obj);
    return obj;
}

//
//Function: checkObject
//
//Description:
//      If no such object in the tree, noSuchName error. (RFC1157)
//

void mibTree :: checkObject(mibObject *object_) const
{
    if(object_ == NULL)
    {
        throw mibException(PDUInt::noSuchName);
    }
    return;
}
   

//
//Functions: get methods
//
//Description:
//    Gets the mibObject (other than leaf/table will throw an
//    exception) to set the varBind's value
//

void mibTree :: get(VarBind *varBind_) const
{
    asnUtil::oidList idList;
    asnUtil::asnOidToList(varBind_->name, idList);
    mibObject *obj = getObject(idList);
    if(obj->isTable())
    {
        mibTable *tableObj = dynamic_cast<mibTable *>(obj);
        THROW_IF_DYNAMIC_CAST_FAILED(tableObj);
        tableObj->getValue(varBind_, idList);
    }
    else
    {
        obj->getValue(varBind_);
    }
    return;
}

//
//Functions: set
//
//Description:
//      Gets the object from mibTree and calls the corresponding
//      setValue-method. mibTable needs separete method for extra parameter
//      and to give a possibility for a totally different implementation.
//

void mibTree :: set(VarBind *varBind_)
{
    asnUtil::oidList idList;
    asnUtil::asnOidToList(varBind_->name, idList);
    mibObject *obj = getObject(idList);
    if(obj->isTable())
    {
        mibTable *tableObj = dynamic_cast<mibTable *>(obj);
        THROW_IF_DYNAMIC_CAST_FAILED(tableObj);
        tableObj->setValue(varBind_, idList);
    }
    else
    {
        obj->setValue(varBind_);
    }
    return;
}


//
//Functions: getNext
//
//Description:
//      Traverses the tree recursively to the next object (instance)
//      after varBind_'s name (object ID), and calls object's
//      getValue()-method. NOTE! mibObject::getNext() returns 1 if
//      value received and put into the varBind_, else 0.
//

void mibTree :: getNext(VarBind *varBind_) const
{
    asnUtil::oidList idList;
    asnUtil::asnOidToList(varBind_->name, idList);
    if(_root->getNext(idList, varBind_) == 0)
    {
        throw mibException(PDUInt::noSuchName);
    }
    return;
}


//Function: stringToVarBind
//
//Description:
//      Finds a similar object (leaf/column) from the tree
//      specified by sname_, and calls the object's
//      corresponding method. IF svalue_ is empty, value in the
//      varBind_ has to be AsnNull.
//      Throws mibException(noSuchName) if no similar object found.
//

void mibTree :: stringToVarBind(VarBind *varBind_,
                                const string &sname_,
                                const string &svalue_)
{
    AsnOid asnOid = asnUtil::stringToAsnOid(sname_);
    if(svalue_.empty())
    {
        SimpleSyntax *simple = new SimpleSyntax();
        simple->choiceId = SimpleSyntax::emptyCid;
        simple->empty = new AsnNull();
        ObjectSyntax *value = new ObjectSyntax();
        value->choiceId = ObjectSyntax::simpleCid;
        value->simple = simple;
        varBind_->value = value;
    }
    else
    {
        asnUtil::oidList idList;
        asnUtil::asnOidToList(asnOid, idList);
        mibObject *obj = getObject(idList);
        obj->stringToVarBind(varBind_, svalue_, idList);
    }
    varBind_->name = asnOid;
    return;
}


//Function: varBindToString
//
//Description:
//      Finds a similar object (leaf/column) from the tree
//      specified by varBind_'s name, and calls the object's
//      corresponding method.
//      Throws mibException(noSuchName) is no similar object found.
//

void mibTree :: varBindToString(VarBind *varBind_,
                                string &sname_,
                                string &svalue_)
{
    asnUtil::oidList idList;
    asnUtil::asnOidToList(varBind_->name, idList);
    mibObject *obj = getObject(idList);
    obj->varBindToString(varBind_, svalue_, idList);
    sname_ = asnUtil::asnOidToString(varBind_->name);
    return;
}


//
//Function: insertNode
//
//Description:
//      Creates and inserts new node under the "closest node". Creates
//      all necessary nodes (eg. insertNode(1.2.3) results
//      nodes "1", "1.2" and "1.2.3").
//

void mibTree :: insertNode(const string &oid_)
{
    asnUtil::oidList idList;
    asnUtil::stringToList(oid_, idList);
    mibObject *obj = _root->get(idList);  // returns root if empty
    if(obj->isNode() == 0)
    {
        throw pfException(PF_EX_INFO);
    }
    while(idList.empty() == 0)
    {
        mibObject *tempObj = mibObject::createNode();
        try
        {
            obj->add(idList.front(), tempObj);
            debugPfUlong("Node added", idList.front());
            obj = tempObj;
            idList.pop_front();
        }
        catch(...)
        {
            debugUser("Insertion to tree failed!");
            delete tempObj;
            throw pfException(PF_EX_INFO);
        }
    }
    return;
}

//
//Function: insertTable
//
//Description:
//      Inserts new table. Write_ defines if row addition to tables
//      is allowed by setRequest. A table may be added only under a node.
//

void mibTree :: insertTable(const string &oid_)
{
    asnUtil::oidList idList;
    asnUtil::stringToList(oid_, idList);
    mibObject *obj = _root->get(idList);
    if(obj->isNode() && idList.size() == 1)
    {
        mibObject *tableObj = mibObject::createTable();
        try
        {
            obj->add(idList.front(), tableObj);
            debugString("Table added", oid_);
        }
        catch(...)
        {
            debugUser("Insertion to tree failed!");
            delete tableObj;
            throw pfException(PF_EX_INFO);
        }
    }
    return;
}


//
//Function: insertColumn/Leaf
//
//Description:
//      Inserts different types of columns/leafs into the tree.
//      Columns may added under a table, and leafs under a node.
//      MibDestination defines the interface to call when an object
//      receives a getRequest/setRequest.
//

void mibTree :: insertAsnIntColumn(const string &oid_, mibDestination *dest_)
{
    mibObject *colObj = mibObject::createAsnIntColumn(oid_, dest_);
    insertColumn(oid_, colObj);
    return;
}

void mibTree :: insertAsnOctsColumn(const string &oid_, mibDestination *dest_)
{
    mibObject *colObj = mibObject::createAsnOctsColumn(oid_, dest_);
    insertColumn(oid_, colObj);
    return;
}

void mibTree :: insertAsnOidColumn(const string &oid_, mibDestination *dest_)
{
    mibObject *colObj = mibObject::createAsnOidColumn(oid_, dest_);
    insertColumn(oid_, colObj);
    return;
}

void mibTree :: insertColumn(const string &oid_,
                             mibObject *newObj_)
{
    try
    {
        asnUtil::oidList idList;
        asnUtil::stringToList(oid_, idList);
        mibObject *obj = _root->get(idList);
        if(obj->isTable() == 0 || idList.empty())
        {
            throw pfException(PF_EX_INFO);
        }
        idList.pop_front();   // table entry
        obj->add(idList.front(), newObj_);
        debugString("Column added", oid_);
    }
    catch(...)
    {
        debugUser("Insertion to tree failed!");
        delete newObj_;
        throw pfException(PF_EX_INFO);
    }
    return;
}


void mibTree :: insertAsnIntLeaf(const string &oid_,
                                 mibDestination *dest_,
                                 bool write_)
{
    mibObject *newObj = mibObject::createAsnIntLeaf(oid_, dest_, write_);
    insertLeaf(oid_, newObj);    
    return;
}

void mibTree :: insertAsnOctsLeaf(const string &oid_,
                                 mibDestination *dest_,
                                 bool write_)
{
    mibObject *newObj = mibObject::createAsnOctsLeaf(oid_, dest_, write_);
    insertLeaf(oid_, newObj);
    return;
}

void mibTree :: insertAsnOidLeaf(const string &oid_,
                                 mibDestination *dest_,
                                 bool write_)
{
    mibObject *newObj = mibObject::createAsnOidLeaf(oid_, dest_, write_);
    insertLeaf(oid_, newObj);
    return;
}

void mibTree :: insertLeaf(const string &oid_,
                           mibObject *newObj_)
{
    try
    {
        asnUtil::oidList idList;
        asnUtil::stringToList(oid_, idList);
        mibObject *obj = _root->get(idList);
        if(obj->isNode() == 0 && idList.size() != 1)
        {
            throw pfException(PF_EX_INFO);
        }
        obj->add(idList.front(), newObj_);
        debugString("Leaf added", oid_);
    }
    catch(...)
    {
        debugUser("Insertion to tree failed!");
        delete newObj_;
        throw pfException(PF_EX_INFO);
    }
    return;
}

//
//Functions: insertRow, deleteRow
//
//Description:
//      Inserts a new / deletes an old index (row) into/from a table.
//

void mibTree :: insertRow(const string &tableOid_, const string &rowOid_)
{
    asnUtil::oidList idList;
    asnUtil::stringToList(tableOid_, idList);
    mibObject *obj = getObject(idList);
    if(obj->isTable() == 0)
    {
        throw mibException(PDUInt::noSuchName);
    }
    mibTable *tableObj = dynamic_cast<mibTable *>(obj);
    THROW_IF_DYNAMIC_CAST_FAILED(tableObj);
    tableObj->addRow(rowOid_);
    return;
}

void mibTree :: insertRow(const string &objectID_)
{
    asnUtil::oidList idList;
    asnUtil::stringToList(objectID_, idList);
    mibObject *obj = getObject(idList);
    if(obj->isTable() == 0 || idList.size() < 3)
    {
        throw mibException(PDUInt::noSuchName);
    }
    mibTable *tableObj = dynamic_cast<mibTable *>(obj);
    THROW_IF_DYNAMIC_CAST_FAILED(tableObj);
    idList.pop_front();  // remove entry
    idList.pop_front();  // remove column
    string rowOid = asnUtil::listToString(idList);
    tableObj->addRow(rowOid);
    return;
}

void mibTree :: deleteRow(const string &objectID_)
{
    asnUtil::oidList idList;
    asnUtil::stringToList(objectID_, idList);
    mibObject *obj = getObject(idList);
    if(obj->isTable() == 0 || idList.size() < 3)
    {
        throw mibException(PDUInt::noSuchName);
    }
    mibTable *tableObj = dynamic_cast<mibTable *>(obj);
    THROW_IF_DYNAMIC_CAST_FAILED(tableObj);
    idList.pop_front();  // remove entry
    idList.pop_front();  // remove column
    string rowOid = asnUtil::listToString(idList);
    tableObj->deleteRow(rowOid);
    return;
}

//
//Function: clearTable
//
//Description:
//      Removes all indexes (rows) from the table.
//

void mibTree :: clearTable(const string &tableOid_)
{
    asnUtil::oidList idList;
    asnUtil::stringToList(tableOid_, idList);
    mibObject *obj = getObject(idList);
    if(obj->isTable() == 0)
    {
        throw mibException(PDUInt::noSuchName);
    }
    mibTable *tableObj = dynamic_cast<mibTable *>(obj);
    THROW_IF_DYNAMIC_CAST_FAILED(tableObj);
    tableObj->clear();
    return;
}
