//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SW
//
//File: swatmport.cpp
//
//Version: $Revision: 1.25 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/11 16:05:41 $
//
//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 <autoptr.h>
#include "pf/debug.h"
#include "pf/error.h"
#include "protocol/saal/saalunilink.h"
#include "protocol/saal/saalnnilink.h"
#include "ie/connectionidentifier.h"
#include "swdefs.h"
#include "swilmiimpl.h"
#include "swunilink.h"
#include "swnnilink.h"
#include "swswitch.h"
#include "swatmport.h"

swAtmPort :: swAtmPort(const string &portType_)
    : swPort(portType_),
      _ilmi(0),
      _saalMap(),
      _idMap(),
      _vciSet(),
      _nextVCI(0),
      _minVPI(SW_MIN_VPI),
      _maxVPI(SW_MAX_VPI),
      _minVCI(SW_MIN_VCI),
      _maxVCI(SW_MAX_VCI)
{
    return;
}

swAtmPort :: swAtmPort(const swAtmPort &other_)
    : swPort(other_),
      _ilmi(0),
      _saalMap(),
      _idMap(),
      _vciSet(),
      _nextVCI(0),
      _minVPI(other_._minVPI),
      _maxVPI(other_._maxVPI),
      _minVCI(other_._minVCI),
      _maxVCI(other_._maxVCI)
{
    return;
}

swAtmPort :: ~swAtmPort(void)
{
    // Start ILMI
    if (_ilmi != 0)
    {
        _ilmi->sendStop();
        delete _ilmi;
    }
    return;
}

swPort *swAtmPort :: clone(pfUlong portNumber_)
{
    swPort *newPort = new swAtmPort(*this);
    newPort->setPortNumber(portNumber_);
    return newPort;
}

//
//Function: addUNIlink
//
//Description:
//

void swAtmPort :: addUNIlink(pfUlong linkNumber_)
{    
    // Create linkIdentifier
    pfUlong linkIdentifier = 
        swSwitch::buildLinkIdentifier(_portNumber, linkNumber_);
    
    // Reserve vpi/vci    
    pfUlong vpi = swSwitch::instance()->reserveSignalingVPI(linkIdentifier);
    pfUlong vci = swSwitch::instance()->reserveSignalingVCI(linkIdentifier);

    if (SW_FABRIC_USED != 0)
    {
        // Fabric connection
        makeFabricConnection(vpi, vci, linkNumber_);
    }

    // Create UNI stack    
    auto_ptr<swUNIlink> link(
        swSwitch::instance()->createUNIlink(linkIdentifier));

    // Create SAAL UNI link
    auto_ptr<saalUNIlink> saal(
        swSwitch::instance()->createSAALUNIlink(linkIdentifier, vpi, vci));

    // Store UNI stack
    insertLink(linkNumber_, link.get());

    try
    {
        pfConduit coOrdConduit = link->getCoOrdConduit();
        pfConduit saalConduit = saal->getConduit();
        
        // Connect UNI stack and SAAL link together
        coOrdConduit.connectToA(saalConduit);
        saalConduit.connectToB(coOrdConduit);

        // Store SAAL link
        pair<saalMapIterType, bool> p;
        p = _saalMap.insert(saalMapType::value_type(linkNumber_,
                                                    saal.get()));
        if (p.second == 0)
        {
            THROW_RESOURCE_UNAVAILABILITY_UNSPECIFIED;
        }
    }
    catch (pfException &ex)
    {
        // Free a pointer from auto_ptr management and remove
        // pointer/delete object from map.
        link.release();
        removeLink(linkNumber_);
        // ++TODO++ delete branch
        throw;
    }

    if (SW_ILMI_USED != 0)
    {
        // Create and connect ILMI
        _ilmi = swIlmiImpl::createILMI(_portNumber, linkNumber_);
        _ilmi->sendListPrefixes(swSwitch::instance()->getNetPrefix());
        _ilmi->makeFabricConnection();

        // ++TODO++
        // If ILMI creation or starting failed, should a link
        // be deleted ??
    }

    // Free pointers from auto_ptr management.
    link.release();
    saal.release();

    return;
}

//
//Function: addNNIlink
//
//Description:
//

void swAtmPort :: addNNIlink(pfUlong linkNumber_,
                             pfUlong destinationPointCode_)
{
    pfUlong linkIdentifier = 
        swSwitch::buildLinkIdentifier(_portNumber, linkNumber_);

    // Reserve vpi/vci        
    pfUlong vpi = swSwitch::instance()->reserveSignalingVPI(linkIdentifier);
    pfUlong vci = swSwitch::instance()->reserveSignalingVCI(linkIdentifier);

    if (SW_FABRIC_USED != 0)
    {
        // Fabric connection
        makeFabricConnection(vpi, vci, linkNumber_);
    }

    // Create NNI stack
    auto_ptr<swNNIlink> link(
        swSwitch::instance()->createNNIlink(linkIdentifier,
                                            destinationPointCode_));
    
    // Store NNI stack
    insertLink(linkNumber_, link.get());

    try
    {
        pfConduit coOrdConduit = link->getCoOrdConduit();
        swSwitch::instance()->connectBISUPtoMTP3(linkIdentifier, coOrdConduit);

        // Create and store SAAL link
        addNNISAALlink(linkNumber_, vpi, vci);
    }
    catch (pfException &ex)
    {
        // Free a pointer from auto_ptr management and remove
        // pointer/delete object from map.
        link.release();
        removeLink(linkNumber_);

        // ++TODO++ delete branch
        // and uninstaller to mtp3 muxes ?!?
        throw;
    }
    // Free a pointer from auto_ptr management.
    link.release();
    return;
}

void swAtmPort :: addNNISAALlink(pfUlong linkNumber_,
                                 pfUlong vpi_,
                                 pfUlong vci_)
{
    saalMapConstIterType iter = _saalMap.find(linkNumber_);
    if (iter == _saalMap.end())
    {
        pfUlong linkIdentifier = 
            swSwitch::buildLinkIdentifier(_portNumber, linkNumber_);
        auto_ptr<saalNNIlink> saal(
            swSwitch::instance()->createSAALNNIlink(linkIdentifier,
                                                    vpi_,
                                                    vci_));
        _saalMap.insert(saalMapType::value_type(linkNumber_, saal.get()));
            
        pfConduit saalConduit = saal->getConduit();
        swSwitch::instance()->connectSAALtoMTP3(linkIdentifier,
                                                saalConduit);

        saal->setN1(1);
        swSwitch::instance()->startNNIlink(saalConduit);
        // Free a pointer from auto_ptr management.
        saal.release();
    }
    else
    {
        //++TODO++ BadCastException ?!?
        saalNNIlink *saal = dynamic_cast<saalNNIlink*>((*iter).second);
        if (saal == 0)
        {
            // There is already SAAL-UNI with same linkIdentifier
            THROW_RESOURCE_UNAVAILABILITY_UNSPECIFIED;
        }
        // Else SAAL-NNI link already present, do nothing.
    }
    return;
}

void swAtmPort :: removeSaalLink(pfUlong linkNumber_)
{
    saalMapIterType iter = _saalMap.find(linkNumber_);
    if (iter != _saalMap.end())
    {
        saalLink *link = ((*iter).second);
        delete link;
        link = 0;
        (void)_saalMap.erase(linkNumber_);
        debugUser("Saal link deleted");
    }
    return;
}

void swAtmPort :: success(pfUlong identifier_)
{
    debugUser("success at ATM port");

    idMapConstIterType iter = _idMap.find(identifier_);
    if (iter == _idMap.end())
    {
        //Do nothing ?!?
        //THROW_RESOURCE_UNAVAILABILITY_UNSPECIFIED;
    }
    pfUlong linkNumber = ((*iter).second);

    (void)linkNumber;
    //++TODO++ start link (lower and upper layers)
    // - openAtmConnection
    // - make crossconnection
    // - now already started
    return;
}

void swAtmPort :: failure(pfUlong identifier_, pfUlong cause_)
{
    debugPfUlong("failure at ATM port with cause", cause_);
    //++TODO++ remove link
    // - remove saalLink
    // - remove swLink
    return;
}

// swPortConfig interface
void swAtmPort :: setMinVPI(pfUlong VPI_)
{
    if (VPI_ > _minVPI)
    {
        _minVPI = VPI_;
    }
    debugPfUlong("minVPI", _minVPI);
    return;
}

void swAtmPort :: setMaxVPI(pfUlong VPI_)
{
    if (VPI_ < _maxVPI)
    {
        _maxVPI = VPI_;
    }
    debugPfUlong("maxVPI", _maxVPI);
    return;
}

void swAtmPort :: setMinVCI(pfUlong VCI_)
{
    if (VCI_ > _minVCI)
    {
        _minVCI = VCI_;
    }
    debugPfUlong("minVCI", _minVCI);
    return;
}

void swAtmPort :: setMaxVCI(pfUlong VCI_)
{
    if (VCI_ < _maxVCI)
    {
        _maxVCI = VCI_;
    }
    debugPfUlong("maxVCI", _maxVCI);
    return;
}

ieConnectionInfo *swAtmPort :: reserveResources(void)
{
    //NOTE: Only VPI=0 is supported in this version !!
    //
    // ++TODO++
    // - VPI allocation
    // - VCI values to depend on VPI values

    pfUlong vpi = 0;
    
    if ((_nextVCI < _minVCI) || (_nextVCI > _maxVCI))
    {
        _nextVCI = _minVCI;
    }
    pfUlong start = _nextVCI;
    
    debugPfUlong("startVCI", start);
    
    pair<vciIterator, bool> p;
    p = _vciSet.insert(_nextVCI);
    
    while (p.second == 0)
    {
        _nextVCI++;
        if (_nextVCI > _maxVCI)
        {
            _nextVCI = _minVCI;
        }
        if (_nextVCI == start)
        {
            THROW_NO_VPCI_VCI_AVAILABLE;
        }
        p = _vciSet.insert(_nextVCI);
    }

    debugPfUlong("reserveResources", _nextVCI);
    ieConnectionIdentifier *ie = new ieConnectionIdentifier();
    ie->setVPCI(vpi);
    ie->setVCI(_nextVCI);
    _nextVCI++;
    return ie;
}

ieConnectionInfo *swAtmPort :: reserveResources(
    ieConnectionInfo *suggestedInfo_)
{
    // ++TODO++ !!
    (void)suggestedInfo_;
    return 0;
}

void swAtmPort :: freeResources(ieConnectionInfo *info_)
{
    ieConnectionIdentifier *ie = 
        dynamic_cast<ieConnectionIdentifier *>(info_);
    THROW_IF_DYNAMIC_CAST_FAILED(ie);
    // ++TODO++
    // Check where possibly exception will be catched !?!

    pfUlong vci = ie->getVCI();
    debugPfUlong("freeResources", vci);
    _vciSet.erase(vci);
    return;
}

void swAtmPort :: makeFabricConnection(pfUlong vpi_, 
                                       pfUlong vci_, 
                                       pfUlong linkNumber_)
{
    ieConnectionIdentifier input;

    input.setPort(_portNumber);
    input.setVPCI(SW_SIGNALING_VPI);
    input.setVCI(SW_SIGNALING_VCI);
    
    ieConnectionIdentifier output;
    
    output.setPort(SW_FABRIC_CONTROL_PORT);
    output.setVPCI(vpi_);
    output.setVCI(vci_);

    pfUlong id = swSwitch::instance()->connect(&input, &output, true, this);
    
    debugPfUlong("swAtmPort :: makeFabricConnection, id", id);
    
    pair<idMapIterType, bool> p;
    p = _idMap.insert(idMapType::value_type(id, linkNumber_));
    
    if (p.second == 0)
    {
        THROW_RESOURCE_UNAVAILABILITY_UNSPECIFIED;
    }
    return;
}
