//Editor-Info: -*- C++ -*-
//
//Subject: pfTestAdapter 
//
//File: taprimitives.cpp
//
//Version: $Revision: 1.24 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/11 18:57:00 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//	Vesa-Matti Puro
//      Jussi Turunen
//
//Description:
//      See corresponding header file
//
//Copyright:
//
//
//Licence:
//
//
//History: 

//class pfState;
//class pfProtocol;

#include <OB/CORBA.h>
#include <OB/Util.h>

#include <stdio.h>

#include <typeinfo>
#include <string>

#include "protocol/usscf/usscfprotocol.h"
#include "protocol/usscf/usscfstate.h"
#include "protocol/usscf/usscfstate1_1.h"
#include "protocol/usscf/usscfstate2_2.h"
#include "protocol/usscf/usscfstate2_5.h"
#include "protocol/usscf/usscfstate3_4.h"
#include "protocol/usscf/usscfstate4_10.h"

#include "protocol/sscop/sscopprotocol.h"
#include "protocol/sscop/sscopstate.h"
#include "protocol/sscop/sscopstate1.h"
#include "protocol/sscop/sscopstate2.h"
#include "protocol/sscop/sscopstate4.h"
#include "protocol/sscop/sscopstate5.h"
#include "protocol/sscop/sscopstate10.h"

//SSCOP
#include "iface/aaif/aadownprimitives.h"
#include "iface/cpcsif/cpcsupprimitives.h"

// USSCF
#include "iface/aaif/aaupprimitives.h"
#include "iface/uaalif/uaaldownprimitives.h"

#include "pf/frame.h"
#include "pf/debug.h"
#include "pf/bitstring.h"
#include "pf/protocol.h"
#include "pf/state.h"
#include "pf/error.h"

#include "taprimitives.h"

//------------------------------------------------------------------------

taMessenger :: taMessenger(void)
    : pfMessenger(),
      _data(),
      _identifier()
{
    return;
}

taMessenger :: taMessenger(const string &identifier_)
    : pfMessenger(),
      _data(),
      _identifier(identifier_)
{
    return;
}

taMessenger :: taMessenger(const string &identifier_,
                           const otMessage::SerializedMessage& message_)
    : pfMessenger(),
      _data(message_),
      _identifier(identifier_)
{
    return;
}

taMessenger :: taMessenger(const taMessenger &other_)
    : pfMessenger(other_),
      _data(other_._data),
      _identifier(other_._identifier)
{
    return;
}

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

void taMessenger :: apply(pfState *state_, pfProtocol *protocol_)
{
    taInputs *state = dynamic_cast<taInputs *> (state_);

    if (state != 0)
    {
        state->taMessengerAct(this, protocol_);
    }
    else 
    {
        doSomething(state_, protocol_);
    }
//    THROW_IF_DYNAMIC_CAST_FAILED(state);
//    state->taMessengerAct(this, protocol_);
    return;
}

void taMessenger :: send(const otMessage_var &receiver_)
{
    try
    {
        debugString("send a message to receiver", _identifier);
        CORBA_String_var identifier = dupString(_identifier);
        otMessage::SerializedMessage message = getData();
        receiver_->send(identifier, message);
    }
    catch(...)
    {
        debugUser("ERROR: couldn't send to tester");
        THROW_TEMPORARY_FAILURE;
    }
    return;
}

const char *taMessenger :: dupString(const string &s_) const
{
    return CORBA_string_dup(s_.c_str());
}


string taMessenger :: getIdentifier(void) const
{
    return _identifier;
}

otMessage::SerializedMessage taMessenger :: getData(void) const
{
    return _data;
}

int taMessenger :: getIntFieldValue(const string &fieldName_) const
{
    pfUlong length = _data.length();
    pfUlong i=0;
    int returnValue=0;
    
    while (i < length)
    {
        string id = _data[i].identifier.in();
        if (id.compare(fieldName_) == 0)
        {
            // maybe kind should be also checked so that
            // value isn't zero... Although this could be
            // checked in the calling method.
            returnValue=_data[i].number;
        }
        i++;
    }
    return returnValue;
}

string taMessenger :: getStringFieldValue(const string &fieldName_) const
{
    pfUlong length = _data.length();
    pfUlong i=0;
    string returnValue("");
    
    while (i < length)
    {
        string id = _data[i].identifier.in();
        if (id.compare(fieldName_) == 0)
        {
            // maybe kind should be also checked so that
            // value isn't zero... Although this could be
            // checked in the calling method.
            returnValue=_data[i].value.in();
        }
        i++;
    }
    
    return returnValue;
}

// used in sscop where information field is needed as a frame
pfFrame taMessenger :: getFieldAsFrame(const string &fieldName_) const
{
    pfFrame frame;

    string tmpStr = getStringFieldValue(fieldName_);

    pfBitString tmpBitString(tmpStr);
    pfUlong octets = tmpBitString.length() / 8;
    
    while (octets > 0)
    {
        frame.putFirst((pfByte)tmpBitString.getLastBits(8));
        --octets;
    }
    
    return frame;
}

void taMessenger :: printData(void) const
{
    debugString("PDU", getIdentifier());
    debugPfUlong("length", _data.length());
    for (pfUlong i = 0; i < _data.length(); ++i)
    {
        otMessage::ElementKind kind = _data[i].kind;
        string id = _data[i].identifier.in();
        string value = _data[i].value.in();
        pfUlong number = _data[i].number;
        string kindStr = printKind(kind);

        // Since user data will be large this probably won't work always
        char buffer[512];
        
        if (kind == otMessage::Integer ||
            kind == otMessage::Boolean)
        {
            string numval = pfDebug::serialnumberToString(number);
            sprintf(buffer, 
                    "%-15s%-15s%s", 
                    (id.c_str()), (kindStr.c_str()), (numval.c_str()));
        }
        else
        {
            if (value.length() > 200)
            {
                string tmp = value.substr(0,200);
                sprintf(buffer, 
                        "%-15s%-15s%s ... first 200 chars, data length %d", 
                        (id.c_str()), (kindStr.c_str()), 
                        (tmp.c_str()), 
                        value.length());
            }
            else
            {
                sprintf(buffer, 
                        "%-15s%-15s%s", 
                        (id.c_str()), (kindStr.c_str()), (value.c_str()));
            }
        }
        debugUser(buffer);
    }
    return;
}

string taMessenger :: printKind(otMessage::ElementKind kind_) const
{
    string kind;

    switch(kind_)
    {
        case 0:
            kind="BegOfElement";
            break;
        case 1:
            kind="EndOfElement";
            break;
        case 2:
            kind="Integer";
            break;
        case 3:
            kind="Boolean";
            break;
        case 4:
            kind="BitString";
            break;
        case 5:
            kind="HexString";
            break;
        case 6:
            kind="OctetString";
            break;
        case 7:
            kind="CharString";
            break;
        default:
            // unknown kind. Shouldn't happen
            kind="Unknown kind";
            break;
    }

    return kind;
}

void taMessenger :: doSomething(pfState *state_, pfProtocol *protocol_)
{
    // protokolla testataan dynamic_castilla
    sscopProtocol *sscop = dynamic_cast<sscopProtocol *>(protocol_);
    usscfProtocol *usscf = dynamic_cast<usscfProtocol *>(protocol_);

    // usscf on ensimmäinen johon protokolla, johon saavutaan
    // jos usscf!=0, niin tyrkätään alaspäin
    
    if (usscf != 0)
    {
        if (dynamic_cast<usscfConnectionReleased_Idle *>(state_) != 0)
        {
            executeUSSCFstate1_1(state_, usscf);
        }
        else if (dynamic_cast<usscfAwaitingEstablish_OutgoingConnectionPending *>(state_) != 0)
        {
            executeUSSCFstate2_2(state_, usscf);
        }
        else if (dynamic_cast<usscfAwaitingEstablish_OutgoingResynchronizationPending *>(state_) != 0)
        {
            executeUSSCFstate2_5(state_, usscf);
        }
        else if (dynamic_cast
            <usscfAwaitingRelease_OutgoingDisconnectionPending *>(state_) != 0)
        {
            executeUSSCFstate3_4(state_, usscf);
        }
        else if (dynamic_cast<usscfConnectionEstablished_DataTransferReady *>(state_) != 0)
        {
            executeUSSCFstate4_10(state_, usscf);
        }
        else
        {
        }
    }
    else if (sscop != 0)
    {
        //   dynamic_castia tilalle ja sen perusteella toiminta
	// jokaisen tilan kohdalla tyrkätään tarkistus PDUn nimelle ja jos
	// PDU on sallittu tilassa, niin tehdään tarkoitettu toiminta
        if (dynamic_cast<sscopIdle *>(state_) != 0)
        {
            executeSSCOPstate1(state_, sscop);
        }
        else if (dynamic_cast<sscopOutgoingConnectionPending *>(state_) != 0)
        {
            executeSSCOPstate2(state_, sscop);
        }
        else if (dynamic_cast<sscopOutgoingDisconnectionPending *>(state_) != 0)
        {
            executeSSCOPstate4(state_, sscop);
        }
        else if (dynamic_cast<sscopOutgoingResynchronizationPending*>(state_) != 0)
        {
            executeSSCOPstate5(state_, sscop);
        }
        else if (dynamic_cast<sscopDataTransferReady *>(state_) != 0)
        {
            executeSSCOPstate10(state_, sscop);
        }
        else
        {
            // Väärä tila
            debugUser("protocol was SSCOP - wrong state");
        }

    }
    else
    {
        // Ollaan ties missä. Lokiin merkintää.
        debugUser("Neither SSCOP nor USSCF - what the hell?");
    }

    return;
}

void taMessenger :: executeSSCOPstate1(pfState *state_, sscopProtocol *sscop_)
{
    sscopIdle *state = dynamic_cast<sscopIdle *>(state_);

    debugString("sscop state 1 - impl send PDU", _identifier);
    
    // _idenfierissä PDUn nimi
    if (_identifier.compare("BGN") == 0)
    {
        // Done in states 1, 4
        aaESTABLISHreq messenger;

        // SSCOP_UU read from taMessenger
        pfFrame SSCOP_UU = getFieldAsFrame("UU");
        messenger.setSSCOP_UU(SSCOP_UU);
            
        // aaESTABLISHreqAct(&messenger, protocol_);
        state->aaESTABLISHreqAct(&messenger, sscop_);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }

    return;
}

void taMessenger :: executeSSCOPstate2(pfState *state_, sscopProtocol *sscop_)
{
    sscopOutgoingConnectionPending *state = 
        dynamic_cast<sscopOutgoingConnectionPending *>(state_);

    debugString("sscop state 2 - impl send PDU", _identifier);

    if (_identifier.compare("END1") == 0)
    {
        // Done in states 2, 5, 10
        aaRELEASEreq messenger;

        // SSCOP_UU read from taMessenger
        pfFrame SSCOP_UU = getFieldAsFrame("UU");
        messenger.setSSCOP_UU(SSCOP_UU);

        //aaRELEASEreqAct(&messenger, protocol_);
        state->aaRELEASEreqAct(&messenger, sscop_);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }

    return;
}

void taMessenger :: executeSSCOPstate4(pfState *state_, sscopProtocol *sscop_)
{
    sscopOutgoingDisconnectionPending *state = 
        dynamic_cast<sscopOutgoingDisconnectionPending *>(state_);

    debugString("sscop state 4 - impl send PDU", _identifier);
    
    if (_identifier.compare("BGN") == 0)
    {
        // Done in states 1, 4
        aaESTABLISHreq messenger;

        // SSCOP_UU read from taMessenger
        pfFrame SSCOP_UU = getFieldAsFrame("UU");
        messenger.setSSCOP_UU(SSCOP_UU);
            
        //aaESTABLISHreqAct(&messenger, protocol_);
        // the following actions are copied directly from the method above
        state->aaESTABLISHreqAct(&messenger, sscop_);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }

    return;
}

void taMessenger :: executeSSCOPstate5(pfState *state_, sscopProtocol *sscop_)
{
    sscopOutgoingResynchronizationPending *state = 
        dynamic_cast<sscopOutgoingResynchronizationPending *>(state_);

    debugString("sscop state 5 - impl send PDU", _identifier);

    if (_identifier.compare("END1") == 0)
    {
        // Done in states 2, 5, 10
        aaRELEASEreq messenger;

        // SSCOP_UU read from taMessenger
        pfFrame SSCOP_UU = getFieldAsFrame("UU");
        messenger.setSSCOP_UU(SSCOP_UU);

        //aaRELEASEreqAct(&messenger, protocol_);
        state->aaRELEASEreqAct(&messenger, sscop_);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }

    return;
}

void taMessenger :: executeSSCOPstate10(pfState *state_, sscopProtocol *sscop_)
{
    sscopDataTransferReady *state = 
        dynamic_cast<sscopDataTransferReady *>(state_);

    debugString("sscop state 10 - impl send PDU", _identifier);
    
    if (_identifier.compare("RS") == 0)
    {
        // Done in state 10
        aaRESYNCreq messenger;

        // SSCOP_UU read from taMessenger
        pfFrame SSCOP_UU = getFieldAsFrame("UU");
        messenger.setSSCOP_UU(SSCOP_UU);
            
        //aaRESYNCreqAct(&messenger, protocol_);
        state->aaRESYNCreqAct(&messenger, sscop_);
    }
    else if (_identifier.compare("POLL") == 0)
    {
        // Done in state 10
        //sscopPOLLtimeoutAct(sscop);
        state->sscopPOLLtimeoutAct(sscop_);
    }
    else if (_identifier.compare("END1") == 0)
    {
        // Done in states 2, 5, 10
        aaRELEASEreq messenger;

        // SSCOP_UU read from taMessenger
        pfFrame SSCOP_UU = getFieldAsFrame("UU");
        messenger.setSSCOP_UU(SSCOP_UU);

        //aaRELEASEreqAct(&messenger, protocol_);
        state->aaRELEASEreqAct(&messenger, sscop_);
    }
    else if (_identifier.compare("SD") == 0)
    {
        // Done in state 10
        aaDATAreq messenger;

        // SSCOP_UNITDATA read from taMessenger
        pfFrame SSCOP_UNITDATA = getFieldAsFrame("Information");

        messenger.setMessageUnit(SSCOP_UNITDATA);
        state->aaDATAreqAct(&messenger, sscop_);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }

    return;
}

void taMessenger :: executeUSSCFstate1_1(pfState *state_, usscfProtocol *protocol_)
{
//    usscfProtocol *usscf = dynamic_cast<usscfProtocol *>(protocol_);
//    THROW_IF_DYNAMIC_CAST_FAILED(usscf);

    debugString("usscf state 1_1 - impl send PDU", _identifier);

    if (_identifier.compare("BGN") == 0)
    {
        // sscop states 1 and 4
        // usscf in state 1_1 or 3_4
        protocol_->toUsscfAwaitingEstablish_OutgoingConnectionPending();
        protocol_->toA(this);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }
    return;
}

void taMessenger :: executeUSSCFstate2_2(pfState *state_, usscfProtocol *protocol_)
{
//    usscfProtocol *usscf = dynamic_cast<usscfProtocol *>(protocol_);
//    THROW_IF_DYNAMIC_CAST_FAILED(usscf);

    debugString("usscf state 2_2 - impl send PDU", _identifier);
    
    if (_identifier.compare("END1") == 0)
    {
        // SSCOP UU? From taMessenger?
        //uaalRELEASEreq messenger;
        // Act seems to ignore messenger and just sends aa-primitive
        // to side A, except in state 1_1 when it sends conf to side B.
        protocol_->toUsscfAwaitingRelease_OutgoingDisconnectionPending();
        protocol_->toA(this);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }
    return;
}

void taMessenger :: executeUSSCFstate2_5(pfState *state_, usscfProtocol *protocol_)
{
//    usscfProtocol *usscf = dynamic_cast<usscfProtocol *>(protocol_);
//    THROW_IF_DYNAMIC_CAST_FAILED(usscf);

    debugString("usscf state 2_5 - impl send PDU", _identifier);
    
    if (_identifier.compare("END1") == 0)
    {
        protocol_->toUsscfAwaitingRelease_OutgoingDisconnectionPending();
        protocol_->toA(this);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }
    return;
}

void taMessenger :: executeUSSCFstate3_4(pfState *state_, usscfProtocol *protocol_)
{
//    usscfProtocol *usscf = dynamic_cast<usscfProtocol *>(protocol_);
//    THROW_IF_DYNAMIC_CAST_FAILED(usscf);

    debugString("usscf state 2_2 - impl send PDU", _identifier);
    
    if (_identifier.compare("BGN") == 0)
    {
        // sscop states 1 and 4
        // usscf in state 1_1 or 3_4
        protocol_->toUsscfAwaitingEstablish_OutgoingConnectionPending();
        protocol_->toA(this);
    }
    else
    {
        // Exception notImplemented? or something
        debugString("impl send failure: PDU", _identifier);
    }
    return;
}

void taMessenger :: executeUSSCFstate4_10(pfState *state_, usscfProtocol *protocol_)
{
//    usscfProtocol *usscf = dynamic_cast<usscfProtocol *>(protocol_);
//    THROW_IF_DYNAMIC_CAST_FAILED(usscf);

    debugString("usscf state 4_10 - impl send PDU", _identifier);

    if (_identifier.compare("RS") == 0)
    {
        // sscop in state 10 when it reacts correctly to
        // aaESTABLISHreq by sending a RS.
        // usscf in state 4_10
        protocol_->toUsscfAwaitingEstablish_OutgoingResynchronizationPending();
        protocol_->toA(this);
    }
    else if (_identifier.compare("END1") == 0)
    {
        // Act seems to ignore messenger and just sends aa-primitive
        // to side A, except in state 1_1 when it sends conf to side B.
        // Should this be handled here?
        protocol_->toUsscfAwaitingRelease_OutgoingDisconnectionPending();
        protocol_->toA(this);
    }
    else if (_identifier.compare("POLL") == 0)
    {
        protocol_->toA(this);
    }
    else if (_identifier.compare("SD") == 0)
    {
        // usscf in state 4_10. Sends aaDATAreq to side A.
        protocol_->toA(this);
    }
    else
    {
        debugString("impl send failure: PDU", _identifier);
    }
    return;
}
