//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SW
//
//File: swatmfcf.cpp
//
//Version: $Revision: 1.13 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/04 16:10:01 $
//
//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/error.h"
#include "ie/connectionidentifier.h"
#include "iface/swif/swportconfig.h"
#include "protocol/cpcs/cpcsaadapter.h"

// GSMP
#include "protocol/gsmp/gsmpadjacencyprotocol.h"
#include "protocol/gsmp/gsmpprotocol.h"
#include "protocol/gsmp/gsmpfactory.h"
#include "protocol/gsmp/gsmpprimitives.h"
#include "protocol/gsmp/gsmpconnectionmanagement.h"
#include "protocol/gsmp/gsmpconfigurationmanagement.h"
#include "protocol/gsmp/gsmpeventmanagement.h"

// SW
#include "swdefs.h"
#include "swconnectid.h"
#include "swport.h"
#include "swswitch.h"
#include "swatmfcf.h"

swATMFCF :: swATMFCF(void)
    : swFCF(),
      _connectSet(),
      _gsmpConnectionMgmt(0),
      _gsmpConfigurationMgmt(0),
      _gsmpEventMgmt(0),
      _gsmpReady(false)
{
    // ++TODO++
    // conf / conn management inside auto_ptr
    // create with factory
    return;
}

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

void swATMFCF :: init(void)
{
    debugUser("------ try to open GSMP connection ----");
    debugPfUlong("PORT", SW_CONTROL_PORT);
    debugPfUlong("VPI", SW_GSMP_VPI);
    debugPfUlong("VCI", SW_GSMP_VCI);

    // ++TODO++
    // Default CPCS_MAX_SDU_SIZE (cpcs.h) is too big to connect system call
    pfUlong SDU_SIZE = 16384;

    pfConduit cpcsProxy =
        cpcsATMAdapter::createATMConnection(SW_CONTROL_PORT,
                                            SW_GSMP_VPI,
                                            SW_GSMP_VCI,
                                            SDU_SIZE);
    
    // GSMP protocol and Adjacency Protocol
    pfConduit gsmpProxy = gsmpProtocol::create();
    pfConduit adjacencyProxy = gsmpAdjacencyProtocol::create(SW_CONTROL_PORT);

    // Connect Conduits
    gsmpProxy.connectToA(adjacencyProxy);
    adjacencyProxy.connectToB(gsmpProxy);
    adjacencyProxy.connectToA(cpcsProxy);
    cpcsProxy.connectToA(adjacencyProxy);
    
    // Start Adjacency Protocol
    gsmpStartAdjacencyProtocol *transporter =
        gsmpStartAdjacencyProtocol::createGsmpStartAdjacencyProtocol();
    assert(transporter != 0);

    debugUser("Starting GSMP Protocol...");
    adjacencyProxy.accept(transporter);

    // Create a gsmpFactory.
    gsmpFactory *factory = gsmpFactory::instance();
    // Initialize the factory to use this GSMP instance
    factory->init(gsmpProxy);


    // Connect manager to GSMP
    debugUser("---------- create GSMP Management objects ----------");
    _gsmpConnectionMgmt = factory->createConnectionManagement(this);
    _gsmpConfigurationMgmt = factory->createConfigurationManagement(this);
    _gsmpEventMgmt = factory->createEventManagement(this);

    return;
}

// swFabricCallback interface

void swATMFCF :: success(pfUlong identifier_)
{
    debugUser("success at swATMFCF");
    swConnectId item(identifier_);
    
    setConstIterType iter = _connectSet.find(item);
    if (iter != _connectSet.end())
    {
        item = *iter;
        bool isReady = item.processSuccess();
        
        _connectSet.erase(iter);
        if (isReady == false)
        {
            _connectSet.insert(iter, item);
        }
    }
    return;
}

void swATMFCF :: failure(pfUlong identifier_, pfUlong cause_)
{
    debugUser("failure at swATMFCF");
    swConnectId item(identifier_);

    setConstIterType iter = _connectSet.find(item);
    if (iter != _connectSet.end())
    {
        item = *iter;
        item.processFailure(cause_);
        _connectSet.erase(iter);
    }
    return;
}


// swConfigControl interface

swPortConfig *swATMFCF :: getPortConfig(pfUlong portNumber_)
{
    debugPfUlong("ATM FCF: getPortConfig", portNumber_);
    //++TODO++ fix this later..
    // - now if port doesn't exist a new port of type FSR155 is created
    // - this method is called by GSMP configuration management when
    //   port/allPort configuration response is received
    string portType("FSR155");
    swPortConfig *portConfig = 0;
    try
    {
        swPort *port = swSwitch::instance()->findPort(portNumber_);
        portConfig = dynamic_cast<swPortConfig*>(port);
    }
    catch (pfException &ex)
    {
        swSwitch::instance()->addPort(portNumber_, portType);
        swPort *port = swSwitch::instance()->findPort(portNumber_);
        portConfig = dynamic_cast<swPortConfig*>(port);
    }
    if (portConfig == 0)
    {
        THROW_RESOURCE_UNAVAILABILITY_UNSPECIFIED; 
    }
    return portConfig;
}

void swATMFCF :: setSwitchName(const string& switchName_)
{
    swSwitch::instance()->setSwitchName(switchName_);
    return;
}

void swATMFCF :: setSwitchType(pfUlong switchType_)
{
    swSwitch::instance()->setSwitchType(switchType_);
    return;
}

void swATMFCF :: setFirmwareVersionNumber(pfUlong versionNumber_)
{
    swSwitch::instance()->setFirmwareVersionNumber(versionNumber_);
    return;
}

// swConnectIface

pfUlong swATMFCF :: connect(ieConnectionIdentifier *input_,
                            ieConnectionIdentifier *output_,
                            bool bidirection_,
                            swFabricCallback *callback_)
{
    pfUlong inputID = 0;
    pfUlong outputID = 0;

    connectCommon(inputID, outputID, input_, output_, bidirection_);
    swConnectId connectID(inputID, outputID, bidirection_, callback_);
    saveConnectId(connectID);

    return inputID;
}

void swATMFCF :: connect(ieConnectionIdentifier *input_,
                         ieConnectionIdentifier *output_,
                         bool bidirection_,
                         pfConduit &callback_)
{
    pfUlong inputID = 0;
    pfUlong outputID = 0;

    if (SW_FABRIC_USED != 0)
    {
        connectCommon(inputID, outputID, input_, output_, bidirection_);
        swConnectId connectID(inputID,
                              outputID, 
                              input_, 
                              output_,
                              bidirection_,
                              callback_);
        saveConnectId(connectID);
    }
    else
    {
        swConnectId connectID(inputID, 
                              outputID, 
                              input_, 
                              output_,
                              bidirection_, 
                              callback_);
        connectID.processSuccess();
        if (bidirection_ == true)
        {
            connectID.processSuccess();
        }
    }
    return;
}

void swATMFCF :: disconnect(ieConnectionIdentifier *input_,
                            ieConnectionIdentifier *output_,
                            bool bidirection_)
{
    if (SW_FABRIC_USED != 0)
    {
        if (_gsmpReady == false)
        {
            THROW_NETWORK_OUT_OF_ORDER;
        }
        
        pfUlong inPort = input_->getPort();
        pfUlong inVPI = input_->getVPCI();
        pfUlong inVCI = input_->getVCI();
        
        pfUlong outPort = output_->getPort();
        pfUlong outVPI = output_->getVPCI();
        pfUlong outVCI = output_->getVCI();

        debugUser("deleteBranch(es) to GSMP config.Mgmt.");
        debugPfUlong("inPort", inPort);
        debugPfUlong("inVPI", inVPI);
        debugPfUlong("inVCI", inVCI);
        debugPfUlong("outPort", outPort);
        debugPfUlong("outVPI", outVPI);
        debugPfUlong("outVCI", outVCI);

        
        // Return value (transactionIdentifier) of deleteBranch is not saved
        // because is not used in disconnect.
        _gsmpConnectionMgmt->deleteBranch(inPort,
                                          inVPI,
                                          inVCI,
                                          outPort,
                                          outVPI,
                                          outVCI);
        
        if (bidirection_ == true)
        {
            _gsmpConnectionMgmt->deleteBranch(outPort,
                                              outVPI,
                                              outVCI,
                                              inPort,
                                              inVPI,
                                              inVCI);
        }
    }
    return;
}

void swATMFCF :: connectCommon(pfUlong &inputId_,
                               pfUlong &outputId_,
                               ieConnectionIdentifier *input_,
                               ieConnectionIdentifier *output_,
                               bool bidirection_)
{
    if (_gsmpReady == false)
    {
        THROW_NETWORK_OUT_OF_ORDER;
    }
    pfUlong inPort = input_->getPort();
    pfUlong inVPI = input_->getVPCI();
    pfUlong inVCI = input_->getVCI();

    pfUlong outPort = output_->getPort();
    pfUlong outVPI = output_->getVPCI();
    pfUlong outVCI = output_->getVCI();

    debugUser("addBranch(es) to GSMP config.Mgmt.");
    debugPfUlong("inPort", inPort);
    debugPfUlong("inVPI", inVPI);
    debugPfUlong("inVCI", inVCI);
    debugPfUlong("outPort", outPort);
    debugPfUlong("outVPI", outVPI);
    debugPfUlong("outVCI", outVCI);

    inputId_ = _gsmpConnectionMgmt->addBranch(inPort,
                                              inVPI,
                                              inVCI,
                                              outPort,
                                              outVPI,
                                              outVCI);

    debugPfUlong("inputId", inputId_);

    if (bidirection_ == true)
    {
        outputId_ = _gsmpConnectionMgmt->addBranch(outPort,
                                                   outVPI,
                                                   outVCI,
                                                   inPort,
                                                   inVPI,
                                                   inVCI);
        debugPfUlong("outputId", outputId_);
    }
    return;
}

void swATMFCF :: saveConnectId(swConnectId &connectId_)
{
    pair<setIterType, bool> p;
    p = _connectSet.insert(connectId_);
    
    if (p.second == 0)
    {
        THROW_RESOURCE_UNAVAILABILITY_UNSPECIFIED;
    }
    return;
}

// swEventIf
// ++TODO++

void swATMFCF :: fabricReady(void)
{
    debugUser("-- fabricReady - send switch/allport config messages --");
 
    // Switch and AllPorts configuration
    _gsmpConfigurationMgmt->sendSwitchConfigurationRequest();
    _gsmpConfigurationMgmt->sendAllPortsConfigurationRequest();

    _gsmpReady = true;

    //++TODO++
    // flag to indicate gsmp status, used with add/deleteBranch commands.
    return;
}

void swATMFCF :: fabricUnavailable(void)
{
    debugUser("ATM FCF: fabricUnavailable");
    _gsmpReady = false;
    return;
}

void swATMFCF :: fabricDisconnected(void)
{
    debugUser("ATM FCF: fabricDisconnected");
    return;
}

void swATMFCF :: portUp(pfUlong port_)
{
    debugUser("ATM FCF: portUp");
    (void)port_;
    return;
}

void swATMFCF :: portDown(pfUlong port_)
{
    debugUser("ATM FCF: portDown");
    (void)port_;
    return;
}

void swATMFCF :: invalidVPIVCI(pfUlong port_,
                               pfUlong VPI_,
                               pfUlong VCI_)
{
    debugUser("ATM FCF: invalidVPIVCI");
    (void)port_;
    (void)VPI_;
    (void)VCI_;
    return;
}

void swATMFCF :: newPort(pfUlong port_)
{
    debugUser("ATM FCF: newPort");
    (void)port_;
    return;
}

void swATMFCF :: deadPort(pfUlong port_)
{
    debugUser("ATM FCF: deadPort");
    (void)port_;
    return;
}
