//------------------------------------------------------------------------------
// <copyright file="AddressedPacketEvent.cpp" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
//	Implementation of AddressedPacketEvent, the front end for a CORBA event that
//  contains messages or status information with a target and/or source event.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#include "AddressedPacketEvent.h"
#include "nsextend.h"
#include <sstream>

using namespace std;
using namespace LiquidRobotics::SMACFramework;
using namespace LiquidRobotics::EventLib;

namespace LiquidRobotics {
namespace PayloadInterface {

const AddressedPacketEvent::PropertyNameType AddressedPacketEvent::sourceAddressTag = "AddPktEvt-sourceAddress";
const AddressedPacketEvent::PropertyNameType AddressedPacketEvent::destAddressTag = "AddPktEvt-destAddress";
const AddressedPacketEvent::PropertyNameType AddressedPacketEvent::notifyTag = "AddPktEvt-notify";
const AddressedPacketEvent::PropertyNameType AddressedPacketEvent::messageTag = "AddPktEvt-message";
const AddressedPacketEvent::PropertyNameType AddressedPacketEvent::transactionIDTag = "AddPktEvt-transID";

typedef AddressedPacketEvent::PropertyValueType PVT;

inline void AddressToComponents(PVT::UInt16 address, PVT::UInt8& boardID, PVT::UInt8& taskID)
{
	boardID = address >> 8;
	taskID = (PVT::UInt8) address;
}

inline PVT::UInt16 ComponentsToAddress(PVT::UInt8 boardID, PVT::UInt8 taskID)
{
	PVT::UInt16 address = boardID;
	address <<= 8;
	address |= taskID;
	return address;
}


AddressedPacketEvent::AddressedPacketEvent(IObjectBroker& _broker, NotifyType notifyType)
: AddressedPacketEventBase(_broker)
{
	SetNotify(notifyType);
}

AddressedPacketEvent::AddressedPacketEvent(const EventSettings& _eventSettings, IObjectBroker& _broker, NotifyType notifyType)
: AddressedPacketEventBase(_eventSettings, _broker)
{
	SetNotify(notifyType);
}

AddressedPacketEvent::AddressedPacketEvent(const EventName& _eventName, const EventType& _eventType, IObjectBroker& _broker, NotifyType notifyType)
: AddressedPacketEventBase(_eventName, _eventType, _broker)
{
	SetNotify(notifyType);
}

AddressedPacketEvent::AddressedPacketEvent(const EventName& _eventName, const EventType& _eventType, const IOR& _senderIOR, IObjectBroker& _broker, NotifyType notifyType)
: AddressedPacketEventBase(_eventName, _eventType, _senderIOR, _broker)
{

}

AddressedPacketEvent::AddressedPacketEvent(const AddressedPacketEvent& other)
: AddressedPacketEventBase(other)
{
}

ostream& AddressedPacketEvent::Describe(ostream& stm) const
{
	AddressedPacketEventBase::Describe(stm);

	if (HasSourceAddress())
		stm << " source=0x" << ltos(GetSourceAddress(), 16, 4);

	if (HasDestAddress())
		stm << " dest=0x" << ltos(GetDestAddress(), 16, 4);

	stm << " notify=" << AMSB::ToString((AddressableMessageServiceBase::NotifyType) GetNotify());

	if (HasTransactionID())
		stm << " transID=0x" << ltos(GetTransactionID(), 16, 4);

	if (HasMessage())
	{
		ByteArrayPtr msgptr = GetMessage();

		if (msgptr)
			stm << " message=" << msgptr->ToString();
	}

	return stm;
}

IEventBase::Ptr AddressedPacketEvent::Duplicate() const
{
	IEventBase::Ptr result;
	result.reset(new AddressedPacketEvent(*this));
	return result;
}

void AddressedPacketEvent::ClearSourceAddress()
{
	if (ContainsProperty(sourceAddressTag))		ClearProperty(sourceAddressTag);
}

AddressedPacketEvent::PropertyValueType::UInt16 AddressedPacketEvent::GetSourceAddress() const
{
	PropertyValueType::UInt16 result = GetProperty(sourceAddressTag).GetUInt16();
	return result;
}

void AddressedPacketEvent::GetSourceAddress(PropertyValueType::UInt8& boardID, PropertyValueType::UInt8& taskID) const
{
	PropertyValueType::UInt16 addr = GetSourceAddress();
	AddressToComponents(addr, boardID, taskID);
}

bool AddressedPacketEvent::HasSourceAddress() const
{
	return ContainsProperty(sourceAddressTag);
}

void AddressedPacketEvent::SetSourceAddress(PropertyValueType::UInt16 boardAddress)
{
	PropertyValueType prop(broker);
	prop.SetUInt16(boardAddress);
	SetProperty(sourceAddressTag, prop);
}

void AddressedPacketEvent::SetSourceAddress(PropertyValueType::UInt8 boardID, PropertyValueType::UInt8 taskID)
{
	PropertyValueType::UInt16 addr = ComponentsToAddress(boardID, taskID);
	SetSourceAddress(addr);
}

void AddressedPacketEvent::ClearDestAddress()
{
	if (ContainsProperty(destAddressTag))		ClearProperty(destAddressTag);
}

PVT::UInt16 AddressedPacketEvent::GetDestAddress() const
{
	PropertyValueType::UInt16 result = GetProperty(destAddressTag).GetUInt16();
	return result;
}

void AddressedPacketEvent::GetDestAddress(PropertyValueType::UInt8& boardID, PropertyValueType::UInt8& taskID) const
{
	PropertyValueType::UInt16 addr = GetDestAddress();
	AddressToComponents(addr, boardID, taskID);
}

bool AddressedPacketEvent::HasDestAddress() const
{
	return ContainsProperty(destAddressTag);
}

void AddressedPacketEvent::SetDestAddress(PropertyValueType::UInt16 boardAddress)
{
	PropertyValueType prop(broker);
	prop.SetUInt16(boardAddress);
	SetProperty(destAddressTag, prop);
}

void AddressedPacketEvent::SetDestAddress(PropertyValueType::UInt8 boardID, PropertyValueType::UInt8 taskID)
{
	PropertyValueType::UInt16 addr = ComponentsToAddress(boardID, taskID);
	SetDestAddress(addr);
}

AddressedPacketEvent::NotifyType AddressedPacketEvent::GetNotify() const
{
	PropertyValueType::Int16 result = GetProperty(notifyTag).GetInt16();

	if ((result < 0) || (result >= NT_MAX))
	{
		stringstream stm;
		stm << "Unexpected notify type " << result << " extracted from AddressedPacketEvent";
		string s = stm.str();
		throw runtime_error(s);
	}

	return (AddressedPacketEvent::NotifyType) result;
}

void AddressedPacketEvent::SetNotify(NotifyType notifyType)
{
	if ((notifyType < 0) || (notifyType >= NT_MAX))
	{
		stringstream stm;
		stm << "Unexpected notify type " << notifyType << " specified for AddressedPacketEvent";
		string s = stm.str();
		throw invalid_argument(s);
	}

	PropertyValueType prop(broker);
	prop.SetInt16(notifyType);
	SetProperty(notifyTag, prop);
}

void AddressedPacketEvent::ClearMessage()
{
	if (ContainsProperty(messageTag))		ClearProperty(messageTag);
}

AddressableMessageServiceBase::ByteArrayPtr AddressedPacketEvent::GetMessage() const
{
	PropertyValueType::ByteArrayPtr result = GetProperty(messageTag).GetByteArray();
	return result;
}

bool AddressedPacketEvent::HasMessage() const
{
	return ContainsProperty(messageTag);
}

void AddressedPacketEvent::SetMessage(const ByteArray& msg)
{
	PropertyValueType prop(broker);
	prop.SetByteArray(msg);
	SetProperty(messageTag, prop);
}

void AddressedPacketEvent::ClearTransactionID()
{
	if (ContainsProperty(transactionIDTag))		ClearProperty(transactionIDTag);
}

AddressedPacketEvent::PropertyValueType::UInt16 AddressedPacketEvent::GetTransactionID() const
{
	PropertyValueType::UInt16 result = GetProperty(transactionIDTag).GetUInt16();
	return result;
}

bool AddressedPacketEvent::HasTransactionID() const
{
	return ContainsProperty(transactionIDTag);
}

void AddressedPacketEvent::SetTransactionID(PropertyValueType::UInt16 transID)
{
	PropertyValueType prop(broker);
	prop.SetUInt16(transID);
	SetProperty(transactionIDTag, prop);
}

string AddressedPacketEvent::ToString(NotifyType notify)
{
	string result;

	switch(notify)
	{
	case NT_ENCRYPTED:		result = "encrypted";	break;
	case NT_ENQUEUED:		result = "enqueued";	break;
	case NT_PRIMARY:		result = "primary";		break;
	case NT_SECONDARY:		result = "secondary";	break;
	case NT_TRANSMITTED:	result = "transmitted";	break;

	default:
	{
		stringstream msg;
		msg << "unknown" << (int) notify << "?";
		result = msg.str();
		break;
	}

	}

	return result;
}

}
}
