//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project
//
//File: gsmpmessage.cpp 
//
//State: $State: Exp $
//
//Version: $Revision: 1.7 $
//
//Date: $Date: 1998/10/19 18:14:01 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Author:
//      Harri Sunila
//
//Description:
//      See corresponding header file
//
//Copyright:    
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//      
//Licence:
//     
//
//History:
//
//

#include "gsmpmessage.h"
#include "gsmpadjacencyprotocolmessage.h"
#include "gsmpconfigurationmessages.h"
#include "gsmpconnectionmanagementmessages.h"
#include "gsmpeventmessages.h"
#include "gsmpportmanagementmessages.h"
#include "gsmpstatisticsmessages.h"
#include "gsmpexceptions.h"

const pfByte gsmpMessage::LLC_SNAP_HEADER[8] =
{0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00, 0x88, 0x0C};

//
// Function: gsmpMessage :: create()   (static)
//
// Description:  
//     This method tries to decode a correct messenger. It access the frame
//     and tries to identify the message and finally make the right messenger
//     and decodes it 

gsmpMessage *gsmpMessage::create(pfFrame &frame_)
{
    gsmpMessage *message = 0;
    pfByte messageType = GSMP_INVALID_MESSAGE;
    pfByte messageCode = frame_.read(GSMP_MESSAGE_CODE_LOCATION);
    messageType = frame_.read(GSMP_MESSAGE_TYPE_LOCATION);
    pfUlong function = 0;

    switch (messageType)
    {
        case gsmpAdjacencyProtocolMessage::GSMP_ADJACENCY_MESSAGE:
	    switch (messageCode)
	    {
	        case gsmpAdjacencyProtocolMessage::SYN:
		    message = new gsmpAdjacencyProtocolSYNmessage;
		    break;
	        case gsmpAdjacencyProtocolMessage::SYNACK:
		    message = new gsmpAdjacencyProtocolSYNACKmessage;
		    break;
	        case gsmpAdjacencyProtocolMessage::ACK:
		    message = new gsmpAdjacencyProtocolACKmessage;
		    break;
	        case gsmpAdjacencyProtocolMessage::RSTACK:
		    message = new gsmpAdjacencyProtocolRSTACKmessage;
		    break;
                default:
                    throw pfInvalidTypeException(PF_EX_INFO);
                    break;
	    }
	    break;
        case gsmpConnectionManagementMessage::ADD_BRANCH:
            message = new gsmpAddBranchMessage;
            break;
        case gsmpConnectionManagementMessage::DELETE_BRANCH:
            message = new gsmpDeleteBranchMessage;
            break;
        case gsmpConnectionManagementMessage::DELETE_TREE:
            message = new gsmpDeleteTreeMessage;
            break;
        case gsmpConnectionManagementMessage::VERIFY_TREE:
            message = new gsmpVerifyTreeMessage;
            break;
        case gsmpConnectionManagementMessage::DELETE_ALL:
            message = new gsmpDeleteAllMessage;
            break;
        case gsmpConnectionManagementMessage::MOVE_BRANCH:
            message = new gsmpMoveBranchMessage;
            break;
        case gsmpPortManagementMessage::PORT_MANAGEMENT:
            function = (pfUlong)frame_.read(
                GSMP_MESSAGE_FUNCTION_LOCATION) << 8 |
                (pfUlong)frame_.read(GSMP_MESSAGE_FUNCTION_LOCATION+1);
            switch (function)
            {
                case gsmpPortManagementMessage::BRING_UP:
                    message = new gsmpBringUpMessage;
                    break;
                case gsmpPortManagementMessage::TAKE_DOWN:
                    message = new gsmpTakeDownMessage;
                    break;
                case gsmpPortManagementMessage::INTERNAL_LOOPBACK:
                    message = new gsmpInternalLoopbackMessage;
                    break;
                case gsmpPortManagementMessage::EXTERNAL_LOOPBACK:
                    message = new gsmpExternalLoopbackMessage;
                    break;
                case gsmpPortManagementMessage::BOTHWAY_LOOPBACK:
                    message = new gsmpBothwayLoopbackMessage;
                    break;
                case gsmpPortManagementMessage::RESET_INPUT_PORT:
                    message = new gsmpResetInputPortMessage;
                    break;
                case gsmpPortManagementMessage::RESET_EVENT_FLAGS:
                    message = new gsmpResetEventFlagsMessage;
                    break;
                default:
                    throw pfInvalidTypeException(PF_EX_INFO);
                    break;
            }
        case VC_ACTIVITY:
            message = new gsmpVCActivityResponseMessage;
            break;
        case PORT_STATISTICS:
            message = new gsmpPortStatisticsResponseMessage;
            break;
        case VC_STATISTICS:
            message = new gsmpVCStatisticsResponseMessage;
            break;
        case SWITCH_CONFIGURATION:
            message = new gsmpSwitchConfigurationResponseMessage;
            break;
        case PORT_CONFIGURATION:
            message = new gsmpPortConfigurationResponseMessage;
            break;
        case ALL_PORTS_CONFIGURATION:
            message = new gsmpAllPortsConfigurationResponseMessage;
            break;
        case gsmpEventMessage::PORT_UP:
            message = new gsmpPortUpEventMessage;
            break;
        case gsmpEventMessage::PORT_DOWN:
            message = new gsmpPortDownEventMessage;
            break;
        case gsmpEventMessage::INVALID_VPIVCI:
            message = new gsmpInvalidVPIVCIEventMessage;
            break;
        case gsmpEventMessage::NEW_PORT:
            message = new gsmpNewPortEventMessage;
            break;
        case gsmpEventMessage::DEAD_PORT:
            message = new gsmpDeadPortEventMessage;
            break;
        default:
            throw pfInvalidTypeException(PF_EX_INFO);
            break;
    }
    // First remove and check LLC/SNAP-header (8 octets).
    
    pfByte header[sizeof(LLC_SNAP_HEADER)];
    pfUlong i;
    
    for (i = 0; i < sizeof(LLC_SNAP_HEADER); i++)
    {
        header[i] = frame_.getFirst();
    }
    
    if (memcmp(header, LLC_SNAP_HEADER, sizeof(LLC_SNAP_HEADER)) != 0)
    {
        // The LLC/SNAP header has been corrupted or does not match
        delete message;
        throw pfInvalidTypeException(PF_EX_INFO);
    }
    
    // Check the version of the message
    pfByte version = frame_.read(0);
    
    if (version != GSMP_CURRENT_VERSION)
    {
        delete message;
        throw pfInvalidTypeException(PF_EX_INFO);
    }
        
    // Decode the frame
    try
    {
        message->decodeFrame(frame_);
    }
    catch (gsmpInvalidLengthException &exception)
    {
        delete message;
        exception.printInfo();
        throw gsmpInvalidLengthException(PF_EX_INFO);
    }
    return message;
}

//
// Function: gsmpMessage :: gsmpMessage()
//
// Description:  
//     Constructor
//


gsmpMessage :: gsmpMessage(void)
    : pfMessenger(),
      _frame(),
      _version(GSMP_CURRENT_VERSION),
      _messageType(0),
      _result(0),
      _code(0),
      _transactionIdentifier(0)
{
    return;
}

//
// Function: gsmpMessage :: gsmpMessage()
//
// Description:  
//     Copy constructor
//

gsmpMessage :: gsmpMessage(const gsmpMessage &other_)
    : pfMessenger(other_),
      _frame(other_._frame),
      _version(other_._version),
      _messageType(other_._version),
      _result(other_._result),
      _code(other_._code),
      _transactionIdentifier(other_._transactionIdentifier)
{
    return;
}

//
// Function: gsmpMessage :: ~gsmpMessage()
//
// Description:  
//     Destructor
//

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

//
// Function: gsmpMessage :: operator=()
//
// Description:  
//     Overloaded assingment operator

const gsmpMessage &gsmpMessage :: operator=(const gsmpMessage &other_)
{
    if (this != &other_)
    {
        _frame = other_._frame;
        _version = other_._version;
        _result = other_._result;
        _code = other_._code;
        _transactionIdentifier = other_._transactionIdentifier;
    }
    return *this;
}

// Functions: Get 
//
// Description: 
//     These methods return the data of the message.
//

pfByte gsmpMessage :: getMessageType(void) const
{
    return _messageType;
}

pfByte gsmpMessage :: getResult(void) const
{
    return _result;
}

pfByte gsmpMessage :: getCode(void) const
{
    return _code;
}

pfUlong gsmpMessage :: getTransactionIdentifier(void) const
{
    return _transactionIdentifier;
}

// Function: Set 
//
// Description: 
//     These methods sets the information of the message
//

void gsmpMessage :: setResult(pfByte result_)
{
    _result = result_;
    return;
}

void gsmpMessage :: setCode(pfByte code_)
{
    _code = code_;
    return;
}

void gsmpMessage :: setTransactionIdentifier(pfUlong transactionIdentifier_)
{
    _transactionIdentifier = transactionIdentifier_;
    return;
}

//
// Function: setFailureResponse
//
// Description:  
//     This method sets FAILURE for the result field and a specific error code
//     for the code field.
//

void gsmpMessage :: setFailureResponse(pfByte code_)
{
    _result = GSMP_FAILURE;
    _code = code_;
    return;
}

//
// Function: isResponse
//
// Description:  
//     This method checks if the message is a response message from the switch.
//     The result field has either value GSMP_SUCCESS (3) or GSMP_FAILURE (4),
//     if the message is a response. Otherwise it is a request. 
//     If the message is an Adjacency Protocol message, isResponse() returns
//     always FALSE, since result field in an Adjacency Protocol message is
//     always zero.
//

bool gsmpMessage :: isResponse(void)
{
    bool returnValue = 0;
    if ((_result != GSMP_NO_SUCCESS_ACK) && (_result != GSMP_ACK_ALL))
    {
        returnValue = 1;
    }
    return returnValue;
}
