//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / ILMI
//
//File: ilmiaddressregistration.cpp
//
//Version: $Revision: 1.19 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/16 16:53:02 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Timo Pärnänen
//
//Description:
//      See corresponding header file.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 

#include "pf/debug.h"
#include "pf/error.h"

#include "asn/asnutil.h"
#include "ilmiaddressregistration.h"
#include "ilmiprotocol.h"

#include "ilmiartimers.h"
#include "ilmiarcommand.h"
#include "ilmiarstate_null.h"

#include "mib/mibexception.h"
#include "mgmt/mgmtcommand.h"

//
// Static const variables.
//
const int ilmiAddressRegistration :: VALID = 1;
const string ilmiAddressRegistration :: VALID_STRING("+1");
const int ilmiAddressRegistration :: INVALID = 2;
// In Linux ilmid INVALID value is 0 !?!

const string ilmiAddressRegistration ::
ATM_NETPREFIX_BASE("1.3.6.1.4.1.353.2.7.1.1.3.0.13.");

const int ilmiAddressRegistration :: PORT_INDEX = 12;
const int ilmiAddressRegistration :: LOCAL_UNI = 0;
const int ilmiAddressRegistration :: ADDRESS_INDEX = 13;
const int ilmiAddressRegistration :: ADDRESS_LENGTH = 20;

const int ilmiAddressRegistration :: MAX_POLL_TIMES = 4;

//
//Functions: constructor and destructor
//

ilmiAddressRegistration :: ilmiAddressRegistration(
    ilmiProtocol *protocol_)
    : pfProtocol(),
      mibDestination(),
      _localPrefixTable(),
      _prefixTable(),
      _addressMap(),
      _retries(0),
      _ilmi(protocol_)
{
    // Reference counter is incremented to prevent AR to die
    // when first proxy to it is creted/deleted (by e.g. timers).
    incRefCount();

    defineTimer(ilmiARresponseTimer,
                ilmiARresponseTimeout::create(),
                ilmiARresponseTimeoutValue);
    defineTimer(ilmiARpollTimer,
                ilmiARpollTimeout::create(),
                ilmiARpollTimeoutValue);

    changeState(ilmiARState_Null::instance());
    return;
}

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

// Initialization
void ilmiAddressRegistration :: initialize(void)
{
    //
    // Add ATM address table column (only status comlumn is added).
    //
    string uniNode("1.3.6.1.4.1.353.2");
    string atmfAddressGroup("1.3.6.1.4.1.353.2.6");
    string atmfAddressTable("1.3.6.1.4.1.353.2.6.1");

    // Entry
    string atmfAddressStatus("1.3.6.1.4.1.353.2.6.1.1.3");

    _ilmi->_mibTree.insertNode(uniNode);
    _ilmi->_mibTree.insertNode(atmfAddressGroup);
    _ilmi->_mibTree.insertTable(atmfAddressTable);
    _ilmi->_mibTree.insertAsnIntColumn(atmfAddressStatus, this);

    return;
}

// Timers

void ilmiAddressRegistration :: startResponseTimer(void)
{
    startTimer(ilmiARresponseTimer);
    return;
}

void ilmiAddressRegistration :: stopResponseTimer(void)
{
    _retries = 0;
    stopTimer(ilmiARresponseTimer);
    return;
}

void ilmiAddressRegistration :: startPollTimer(void)
{
    startTimer(ilmiARpollTimer);
    return;
}

void ilmiAddressRegistration :: stopPollTimer(void)
{
    stopTimer(ilmiARpollTimer);
    return;
}

// Inputs from switch

void ilmiAddressRegistration :: start(void)
{
    (getCurrentState())->start(this);
    return;
}

void ilmiAddressRegistration :: stop(void)
{
    (getCurrentState())->stop(this);
    return;
}

void ilmiAddressRegistration :: listPrefixes(mgmtCommand &command_)
{
    (getCurrentState())->listPrefixes(this, command_);
    return;
}

// Inputs from network

void ilmiAddressRegistration :: receiveColdStartTrap(VarBindList &list_)
{
    (getCurrentState())->receiveColdStartTrap(this, list_);
    return;
}

void ilmiAddressRegistration :: receiveGetResponse(PDUInt &errorStatus_,
                                                   AsnInt &errorIndex_,
                                                   VarBindList &list_)
{
    (getCurrentState())->receiveGetResponse(this,
                                            errorStatus_,
                                            errorIndex_,
                                            list_);
    return;
}


// Mibdestination interface

string ilmiAddressRegistration :: getValue(const string &name_)
{
    string result = (getCurrentState())->getValue(this, name_);
    return result;
}

void ilmiAddressRegistration :: setValue(const string &name_,
                                         const string &value_)
{
    (getCurrentState())->setValue(this, name_, value_);
    return;
}

// Send methods

void ilmiAddressRegistration :: sendGetNextRequest(string name_)
{
    ilmiCommand *command = ilmiARCommand::createARCommand(this);
    VarBind *var = command->append();

    try
    {
        _ilmi->_mibTree.stringToVarBind(var, name_);
    }
    catch (mibException &mibexception)
    {
        delete command;
        THROW_INFORMATION_ELEMENT_NON_EXISTENT_OR_NOT_IMPLEMENTED;
    }

    startResponseTimer();
    // pfException are let through.
    _ilmi->sendGetNextRequest(command);
    return;
}

void ilmiAddressRegistration :: sendColdStartTrap(void)
{    
    VarBindList list;
    _ilmi->sendColdStartTrap(list);
    return;
}

void ilmiAddressRegistration :: sendListAddresses(void)
{
    mgmtCommand command("listAddresses");
    mapIterType i;
    for (i = _addressMap.begin(); i != _addressMap.end(); i++)
    {
        string port("0"); // LOCAL UNI
        command.addParameter(port, (*i).second);
    }
    _ilmi->sendExecute(command);
    return;
}

// Action methods called by states

bool ilmiAddressRegistration :: isResending(void)
{
    _retries++;
    bool result = 0;
    if (_retries < MAX_POLL_TIMES)
    {
        result = 1;
    }
    return result;
}

void ilmiAddressRegistration :: clearCommandMap(void)
{
    _ilmi->clearCommandMap();
    return;
}

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

void ilmiAddressRegistration :: clearTables(void)
{
#ifndef NON_STD_STL
    _prefixTable.clear();
    _addressMap.clear();
#else
    _prefixTable.erase(_prefixTable.begin(), _prefixTable.end());
    _addressMap.erase(_addressMap.begin(), _addressMap.end());
#endif // NON_STD_STL

    // Clear table from mibTree
    static string atmfAddressTable("1.3.6.1.4.1.353.2.6.1");
    _ilmi->_mibTree.clearTable(atmfAddressTable);
    return;
}

void ilmiAddressRegistration :: storeLocalPrefixes(mgmtCommand &command_)
{
    clearLocalPrefixes();    
    for (pfUlong i=0; i<command_.getCommandLength(); i++)
    {
        string port;   // This is reserved for port, now unused
        string prefix;
        string type;   // Type is is unused variable
        command_.getNextParameter(port, prefix, type);
        _localPrefixTable.push_back(prefix);
    }
    return;
}

string ilmiAddressRegistration :: getAddressStatus(const string &name_) const
{
    mapConstIterType mapKey = _addressMap.find(name_);
    if (mapKey == _addressMap.end())
    {
        throw mibException(PDUInt::noSuchName);
    }
    return VALID_STRING;
}

void ilmiAddressRegistration :: addAddress(const string &name_)
{
    asnUtil::oidList list;
    asnUtil::stringToList(name_, list);

    if ((list.size() != (ADDRESS_INDEX + ADDRESS_LENGTH + 1)) ||
        (list[PORT_INDEX] != LOCAL_UNI) ||
        (list[ADDRESS_INDEX ] != ADDRESS_LENGTH))
    {
        debugString("INVALID Address", name_);
        throw mibException(PDUInt::noSuchName);
    }
    
    // Insert row to address table in MibTree.
    _ilmi->_mibTree.insertRow(name_);

    for (int i=0; i<ADDRESS_INDEX + 1; i++)
    {
        list.pop_front();
    }
    string address = asnUtil::listToString(list);    
    
    pair<mapIterType, bool> p;
    p = _addressMap.insert(mapType::value_type(name_, address));
    if (p.second == 0)
    {
        debugUser("Address already present, did not change");
    }
    else
    {
        debugString("Address registered", address);
    }
    return;
}

void ilmiAddressRegistration :: removeAddress(const string &name_)
{
    // Delete row from address table in MibTree.
    _ilmi->_mibTree.deleteRow(name_);

    int isAny = _addressMap.erase(name_);
    if (isAny == 0)
    {
        throw mibException(PDUInt::noSuchName);
    }
    else
    {
        debugUser("Address de-registered");
    }
    return;
}

void ilmiAddressRegistration :: registerPrefixes(void)
{
    ilmiCommand *command = ilmiARCommand::createARCommand(this);
    
    tableConstIterator i;
    for (i = _localPrefixTable.begin(); i != _localPrefixTable.end(); i++)
    {
        VarBind *var = command->append();
        string netPrefix = ATM_NETPREFIX_BASE + (*i);
        fillVarBind(var, netPrefix);
    }
    
    // pfException are let through.
    _ilmi->sendSetRequest(command);
    return;
}

// Private methods

ilmiARState *ilmiAddressRegistration :: getCurrentState(void) const
{
    ilmiARState *state = dynamic_cast<ilmiARState *>(getState());
    THROW_IF_DYNAMIC_CAST_FAILED(state);

    return state;        
}


//
//Function: fillVarBind
//
//Description:
//    Fill VarBind with a given name and value.
//    The name string is converted to AsnOid. 
//

void ilmiAddressRegistration :: fillVarBind(VarBind *var_,
                                            string &name_,
                                            int value_)
{
    AsnOid name = asnUtil::stringToAsnOid(name_);
    SimpleSyntax *simple = new SimpleSyntax();
    simple->choiceId = SimpleSyntax::numberCid;
    simple->number = new AsnInt(value_);
    ObjectSyntax *value = new ObjectSyntax();
    value->choiceId = ObjectSyntax::simpleCid;
    value->simple = simple;
    var_->name = name;
    var_->value = value;

    return;
}

