//-------------------------------------------------------------------------------
// <copyright file="FCCMessage.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 some utility functions relative to formatting and decoding
//   Float C&C message set types
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------

#include "FCCMessage.h"
#include "CRC16.h"
#include "LqrSMACByteStream.h"
#include "LqrSMACPrintfBuffer.h"
#include "LqrSMACZeroObject.h"
#include "nsextend.h"
#include <sstream>

using namespace std;
using namespace LiquidRobotics::SMACFramework;

namespace LiquidRobotics {
namespace PayloadInterface {
namespace FCCMessage {

SHARED_LIB_PUBLIC ostream& operator<<(ostream& out, const COMM_HEADER& header)
{
	out << "type=0x" << ultos(header.type, 16, 2) << " (";

	PacketType_ToStream(out, header.type);

	out << ") packetID=0x" << ultos(header.packetID, 16, 8)
	    << " size=" << header.size;

	return out;
}

SHARED_LIB_PUBLIC ostream& Cmd_ToStream(ostream& out, uint16_t cmdNum)
{
	switch(cmdNum)
	{
	case CMD_HOLD_CURRENT_POS:				out << "Hold Current Pos";		break;
	case CMD_HOLD_AT_WAYPT:					out << "Hold At Waypoint";		break;
	case CMD_SEQUENTIAL_COURSE:				out << "Sequential Course";		break;
	case CMD_CUSTOM_COURSE:					out << "Custom Course";			break;
	case CMD_CUSTOM_COURSE_PLUS:			out << "Custom Course+";		break;
	case CMD_RIGHT_RUDDER:					out << "Right Rudder";			break;
	case CMD_LEFT_RUDDER:					out << "Left Rudder";			break;
	case CMD_CENTER_RUDDER:					out << "Center Rudder";			break;
	case CMD_CUSTOM_RUDDER:					out << "Custom Rudder";			break;
	case CMD_FOLLOW_HEADING:				out << "Follow Heading";		break;
	case CMD_SET_DEFAULT_COMM_CHANNEL:		out << "Set Default Comm Channel"; break;
	case CMD_READ_UNIQUE_ID:				out << "Read Unique ID";		break;
	case CMD_SET_UNIQUE_ID:					out << "Set Unique ID";			break;
	case CMD_LIGHT_OFF:						out << "Light Off";				break;
	case CMD_LIGHT_ON:						out << "Light On";				break;
	case CMD_IR_LIGHT_OFF:					out << "IR Off";				break;
	case CMD_IR_LIGHT_ON:					out << "IR On";					break;
	case CMD_RF_OFF:						out << "RF Off";				break;
	case CMD_RF_ON:							out << "RF On";					break;
	case CMD_GET_STATUS:					out << "Get Status";			break;
	case CMD_SET_STATUS:					out << "Set Status";			break;
	case CMD_GET_IDENTIFICATION:			out << "Get Identification";	break;
	case CMD_GET_POWER_STATUS:				out << "Get Power Status";		break;
	case CMD_GET_RUDDER_PARAMS:				out << "Get Rudder Params";		break;
	case CMD_SET_RUDDER_PARAMS:				out << "Set Rudder Params";		break;
	case CMD_SET_PARAMETERS:				out << "Set Parameters";		break;
	case CMD_GET_ERROR_COUNTS:				out << "Get Error Counts";		break;
	case CMD_GET_SUB_HEADING:				out << "Get Sub Heading";		break;
	case CMD_GET_INTEGRAL:					out << "Get Integral";			break;
	case CMD_GET_PID_PARAMS:				out << "Get PID Params";		break;
	case CMD_NEW_WAYPT:						out << "New Waypoint";			break;
	case CMD_NEW_WAYPTS:					out << "New Waypoints";			break;
	case CMD_CLR_WAYPTS:					out << "Clear Waypoints";		break;
	case CMD_READ_WAYPT:					out << "Read Waypoint";			break;
	case CMD_INIT_FILE_WRITE:				out << "Init File Write";		break;
	case CMD_WRITE_FILE_BLOCK:				out << "Write File Block";		break;
	case CMD_DELETE_FILE:					out << "Delete File";			break;
	case CMD_READ_FILE_REQUEST:				out << "Read File Request";		break;
	case CMD_GET_DETAILED_FILE_LIST:		out << "Get Detailed File List"; break;
	case CMD_SELECT_PROGRAM:				out << "Select Program";		break;
	case CMD_WRITE_CONSOLE_INPUT:			out << "Write Console Input";	break;
	case CMD_READ_FILE_TRANSACTION_CLOSED:	out << "Read File Transaction Closed"; break;
	case CMD_GET_FILE_LIST:					out << "Get File List";			break;
	case CMD_WRITE_CNSL_CMD_ASCII:			out << "Write Console (ASCII)";	break;
	case CMD_WRITE_CNSL_CMD_BINARY:			out << "Write Console (binary)"; break;
	case CMD_FLOAT_REBOOT:					out << "Float Reboot";			break;
	case CMD_BINARY_BLOCK:					out << "Binary Block";			break;
	case CMD_PAYLOAD_CMD:					out << "Payload Command";		break;

	default:

		out << "0x" << ultos(cmdNum, 16, 4) << "?";
		break;
	}

	return out;
}

SHARED_LIB_PUBLIC ostream& CmdStructID_ToStream(ostream& out, uint8_t structID)
{
	switch(structID)
	{
	case FMT_RCV_CMD:				out << "Command";			break;
	case FMT_RCV_SETTINGS:			out << "Settings";			break;
	case FMT_RCV_CRS_LST:			out << "CRS List";			break;
	case FMT_RCV_SET_PARAMS:		out << "Set Params";		break;
	case FMT_RCV_STEER:				out << "Steering";			break;
	case FMT_RCV_INIT_FILE_WRITE:	out << "Init File Write";	break;
	case FMT_RCV_WRITE_FILE_BLK:	out << "Write File Block";	break;
	case FMT_RCV_DELETE_FILE:		out << "Delete File";		break;
	case FMT_RCV_READ_FILE_REQ:		out << "Read File Request";	break;
	case FMT_RCV_GET_FILE_LIST_D:	out << "Get File List (Detailed)"; break;
	case FMT_RCV_CONSOLE_INPUT:		out << "Console Input";		break;
	case FMT_RCV_GET_FILE_LIST:		out << "Get File List";		break;

	default:

		out << "0x" << ultos(structID, 16, 2) << "?";
		break;
	}

	return out;
}

SHARED_LIB_PUBLIC ostream& PacketType_ToStream(ostream& out, uint8_t packetType)
{
	stringstream stm1;
	RxPacketType_ToStream(out, packetType);

	stringstream stm2;
	TxPacketType_ToStream(out, packetType);

	const bool good1 = (stm1.str().find('?') == string::npos);
	const bool good2 = (stm2.str().find('?') == string::npos);

	if (good1 == good2)
		out << "Rx:" << stm1.str() << "/Tx:" << stm2.str();
	else if (good1)
		out << stm1.str();
	else
		out << stm2.str();

	return out;
}

SHARED_LIB_PUBLIC ostream& RxPacketType_ToStream(ostream& out, uint8_t packetType)
{
	switch(packetType)
	{
	case RCV_CMD:					out << "Command";				break;
	case RCV_SETTINGS:				out << "Received Settings";		break;

	default:

		out << "0x" << ultos(packetType) << "?";
		break;
	}

	return out;
}

SHARED_LIB_PUBLIC ostream& TxPacketType_ToStream(ostream& out, uint8_t packetType)
{
	switch(packetType)
	{
	case SND_TELEMETRY:				out << "Telemetry6";			break;
	case ACK_WITH_SETTINGS_WAYPT:	out << "Ack w/ Settings & Waypoint"; break;
	case ACK_WITH_SETTINGS:			out << "Ack w/ Settings";		break;
	case ACK_WITH_ID:				out << "Ack w/ ID";				break;
	case ACK_BASIC:					out << "Ack";					break;
	case ACK_WITH_POWER:			out << "Ack w/ Power";			break;
	case ACK_WITH_RUDDER:			out << "Ack w/ Rudder";			break;
	case ACK_WITH_DEBUG:			out << "Ack w/ Debug";			break;
	case ACK_WITH_SUB_HEADING:		out << "Ack w/ Sub Heading";	break;
	case ACK_WITH_INTEGRAL:			out << "Ack w/ Integral";		break;
	case ACK_WITH_PID_PARAMS:		out << "Ack w/ PID Params";		break;
	case ACK_WITH_ALL_PARAMS:		out << "Ack w/ All Params";		break;
	case ACK_WITH_READ_FILE:		out << "Read File Ack";			break;
	case ACK_WITH_LIST_DETAILED:	out << "Ack w/ Detailed File List"; break;
	case ACK_WITH_UNIQUE_ID:		out << "Ack w/ Unique ID";		break;
	case ACK_WITH_FILE_AT_DEST:		out << "File at Destination";	break;
	case NAK_CMD_BASIC:				out << "Nak Command";			break;
	case NAK_DATA_BASIC:			out << "Nak Data";				break;
	case NAK_DATA_ENUMERATED:		out << "Nak Enumerated";		break;
	case NACK_CONSOLE_DATA:			out << "Nak Console";			break;
	case NACK_WITH_DESCRIPTION:		out << "Nak w/ Description";	break;
	case ACK_WITH_FILE_LIST:		out << "Ack w/ File List";		break;
	case ACK_CONSOLE_DATA:			out << "Ack Console";			break;
	case SND_NEW_FILE_AVAILABLE:	out << "New File Available";	break;

	default:

		out << "0x" << ultos(packetType) << "?";
		break;
	}

	return out;
}

Level2Wrapper::Level2Wrapper(CRCInclusion _crcInclusion)
: crc(0)
, fsReport(false)
, fsReportIncludeLength(true)
, crcInclusion(_crcInclusion)
{
	ZeroObject(commHeader);
}

Level2Wrapper::Level2Wrapper(uint8_t _type, const ByteArray& _level1Data, CRCInclusion _crcInclusion, bool _fsReportIncludeLength)
: level1Data(_level1Data.begin(), _level1Data.end())
, crc(0)
, fsReport(true)
, fsReportIncludeLength(_fsReportIncludeLength)
, crcInclusion(_crcInclusion)
{
	ZeroObject(commHeader);
	commHeader.type = _type;
	commHeader.size = _level1Data.size();
	crc = CalculateCRC();
}

Level2Wrapper::Level2Wrapper(uint8_t _type, uint32_t _packetID, const ByteArray& _level1Data, CRCInclusion _crcInclusion)
: level1Data(_level1Data.begin(), _level1Data.end())
, crc(0)
, fsReport(false)
, fsReportIncludeLength(true)
, crcInclusion(_crcInclusion)
{
	commHeader.type = _type;
	commHeader.packetID = _packetID;
	commHeader.size = _level1Data.size();
	crc = CalculateCRC();
}

uint16_t Level2Wrapper::CalculateCRC() const
{
	uint16_t result = 0;

	if (crcInclusion == WITH_CRC)
	{
		CRC16 crc16;

		if (fsReport) {
			crc16.AddSimpleType(commHeader.type);
			if (fsReportIncludeLength)
				crc16.AddSimpleType(commHeader.size);
		}
		else
			crc16.AddSimpleType(commHeader);

		crc16.Add(level1Data);
		result = crc16.Get();
	}

	return result;
}

ConsoleAck::ConsoleAck(uint8_t _format, uint8_t _disposition, const ByteArray& _additionalData)
: additionalData(_additionalData.begin(), _additionalData.end())
{
	format = _format;
	disposition = _disposition;
	len = _additionalData.size();
}

NakWithDescription::NakWithDescription(uint8_t _sequenceNumber, const ByteArray& _additionalData)
: additionalData(_additionalData.begin(), _additionalData.end())
{
	descriptionLength = additionalData.size();
	sequenceNumber = _sequenceNumber;
}

ForwardInfo::ForwardInfo(uint8_t _msgFormat, uint16_t _destination, uint16_t _source)
{
	msgFormat = _msgFormat;
	destination = _destination;
	source = _source;
}

NewFileAvailable::NewFileAvailable()
{
	ZeroObject(*this);
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, COMM_HEADER& data)
{
	bs >> data.type
	   >> data.packetID
	   >> data.size;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const COMM_HEADER& data)
{
	bs << data.type
	   << data.packetID
	   << data.size;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, Level2Wrapper& data)
{
	bs >> (COMM_HEADER&) data;

	const ByteArray& payloadData = bs.GetRemaining(sizeof(data.crc));
	data.level1Data.assign(payloadData.begin(), payloadData.end());

	if (data.crcInclusion == Level2Wrapper::WITH_CRC)
		bs >> data.crc;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const Level2Wrapper& data)
{
	if (data.fsReport)
	{
		bs << ((COMM_HEADER&) data).type;

		if (data.fsReportIncludeLength)
		   bs << ((COMM_HEADER&) data).size;
	}
	else
		bs << (COMM_HEADER&) data;

	bs << ContainerOut<ByteArray>(data.level1Data);

	if (data.crcInclusion == Level2Wrapper::WITH_CRC)
	   bs << data.crc;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, ConsoleAck& data)
{
	ContainerIn<ByteArray> container(data.additionalData, data.len);

	bs >> data.format
	   >> data.disposition
	   >> data.len
	   >> container;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const ConsoleAck& data)
{
	CONSOLE_ACK temp;
	temp.len = data.additionalData.size();

	const CONSOLE_ACK& temp2 = temp;

	bs << data.format
	   << data.disposition
	   << temp2.len
	   << ContainerOut<ByteArray>(data.additionalData);

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, NakWithDescription& data)
{
	ContainerIn<ByteArray> container(data.additionalData, data.descriptionLength);

	bs >> data.descriptionLength
	   >> data.sequenceNumber
	   >> container;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const NakWithDescription& data)
{
	NAK_WITH_DESCRIPTION temp;
	temp.descriptionLength = data.additionalData.size();

	const NAK_WITH_DESCRIPTION& temp2 = temp;

	bs << temp2.descriptionLength
	   << data.sequenceNumber
	   << ContainerOut<ByteArray>(data.additionalData);

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, LRI_FORWARD_INFO& data)
{
	bs >> data.msgFormat
	   >> data.destination
	   >> data.source;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const LRI_FORWARD_INFO& data)
{
	bs << data.msgFormat
	   << data.destination
	   << data.source;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, NewFileAvailable& data)
{
	bs >> data.storageControllerAddress;
	bs.GetRaw(data.fileName);
	bs >> data.fileSize
	   >> data.creationDate
	   >> data.modifiedDate
	   >> data.fileAttributes
	   >> data.providedCRC;
	bs.GetRaw(data.providedMD5);
	bs >> data.calculatedCRC;
	bs.GetRaw(data.calculatedMD5);
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const NewFileAvailable& data)
{
	bs << data.storageControllerAddress;
	bs.PutRaw(data.fileName);
	bs << data.fileSize
	   << data.creationDate
	   << data.modifiedDate
	   << data.fileAttributes
	   << data.providedCRC;
	bs.PutRaw(data.providedMD5);
	bs << data.calculatedCRC;
	bs.PutRaw(data.calculatedMD5);
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, DATE_TIME& data)
{
	bs >> data.hour
	   >> data.minute
	   >> data.second
	   >> data.month
	   >> data.day
	   >> data.year;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const DATE_TIME& data)
{
	bs << data.hour
	   << data.minute
	   << data.second
	   << data.month
	   << data.day
	   << data.year;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, LRI_COMMAND_REQUEST_ACK_WRAPPER& data)
{

	bs >> data.commandFormat;
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const LRI_COMMAND_REQUEST_ACK_WRAPPER& data)
{

	bs << data.commandFormat;
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, TELEMETRY_91& data)
{
	bs >> data.payloadType
	   >> data.payloadBoardID
	   >> data.payloadTaskID
	   >> data.formatInfo
	   >> data.repeatCount;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const TELEMETRY_91& data)
{
	bs << data.payloadType
	   << data.payloadBoardID
	   << data.payloadTaskID
	   << data.formatInfo
	   << data.repeatCount;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, TELEMETRY_91_SAMPLE0& data)
{
	bs >> data.latitude
	   >> data.longitude
	   >> data.sampleTime_T;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const TELEMETRY_91_SAMPLE0& data)
{
	bs << data.latitude
	   << data.longitude
	   << data.sampleTime_T;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_COMMON& data)
{
	bs >> data.fmtID
	   >> data.command;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_COMMON& data)
{
	bs << data.fmtID
	   << data.command;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_10& data)
{
	bs >> data.field3
	   >> data.field4
	   >> data.field5
	   >> data.field6
	   >> data.field7
	   >> data.latitude
	   >> data.longitude
	   >> data.longitude;
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_10& data)
{
	bs << data.field3
	   << data.field4
	   << data.field5
	   << data.field6
	   << data.field7
	   << data.latitude
	   << data.longitude
	   << data.longitude;
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_18& data)
{
	bs >> data.storageControllerAddress;
	bs.GetRaw(data.fileName);
	bs >> data.fileID
	   >> data.fileSize
	   >> data.creationDate
	   >> data.modifiedDate
	   >> data.fileAttributes
	   >> data.overwriteFile
	   >> data.contentCRC;
	bs.GetRaw(data.contentMD5);
	bs >> data.numDataBytes;
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_18& data)
{
	bs << data.storageControllerAddress;
	bs.PutRaw(data.fileName);
	bs << data.fileID
	   << data.fileSize
	   << data.creationDate
	   << data.modifiedDate
	   << data.fileAttributes
	   << data.overwriteFile
	   << data.contentCRC;
	bs.PutRaw(data.contentMD5);
	bs << data.numDataBytes;
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_19& data)
{
	bs >> data.storageControllerAddress
	   >> data.fileID
	   >> data.offset
	   >> data.numDataBytes;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_19& data)
{
	bs << data.storageControllerAddress
	   << data.fileID
	   << data.offset
	   << data.numDataBytes;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_1A& data)
{
	bs >> data.storageControllerAddress;
	bs.GetRaw(data.fileName);
	bs >> data.fileID
	   >> data.overrideReadOnly;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_1A& data)
{
	bs << data.storageControllerAddress;
	bs.PutRaw(data.fileName);
	bs << data.fileID
	   << data.overrideReadOnly;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_1B& data)
{
	bs >> data.storageControllerAddress;
	bs.GetRaw(data.fileName);
	bs >> data.originalTransactionID
	   >> data.offset
	   >> data.readLength
	   >> data.maxResponseSize;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_1B& data)
{
	bs << data.storageControllerAddress;
	bs.PutRaw(data.fileName);
	bs << data.originalTransactionID
	   << data.offset
	   << data.readLength
	   << data.maxResponseSize;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_1C& data)
{
	bs >> data.storageControllerAddress;
	bs.GetRaw(data.fileName);
	bs >> data.fileID
	   >> data.actionFlags
	   >> data.maxResponseSize;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_1C& data)
{
	bs << data.storageControllerAddress;
	bs.PutRaw(data.fileName);
	bs << data.fileID
	   << data.actionFlags
	   << data.maxResponseSize;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, CMD_FMT_1E& data)
{
	bs >> data.dest
	   >> data.function
	   >> data.dataLength;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const CMD_FMT_1E& data)
{
	bs << data.dest
	   << data.function
	   << data.dataLength;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, ACK_READ_FILE& data)
{
	bs >> data.storageControllerAddress
	   >> data.seekOffset
	   >> data.eofFlag
	   >> data.numDataBytes;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const ACK_READ_FILE& data)
{
	bs << data.storageControllerAddress
	   << data.seekOffset
	   << data.eofFlag
	   << data.numDataBytes;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, ACK_FILE_LIST_COMMON_HEADER& data)
{
	bs >> data.storageControllerAddress
	   >> data.offset
	   >> data.lastReport
	   >> data.numFiles;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const ACK_FILE_LIST_COMMON_HEADER& data)
{
	bs << data.storageControllerAddress
	   << data.offset
	   << data.lastReport
	   << data.numFiles;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, ACK_FILE_LIST_RECORD& data)
{
	bs.GetRaw(data.fileName);
	bs >> data.fileID
	   >> data.fileSize
	   >> data.fileAttributes;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const ACK_FILE_LIST_RECORD& data)
{
	bs.PutRaw(data.fileName);
	bs << data.fileID
	   << data.fileSize
	   << data.fileAttributes;

	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator>>(ByteStream& bs, ACK_FILE_LIST_DETAILED_RECORD& data)
{
	bs.GetRaw(data.fileName);
	bs >> data.fileID
	   >> data.fileSize
	   >> data.creationDate
	   >> data.modifiedDate
	   >> data.fileAttributes
	   >> data.providedCRC;
	bs.GetRaw(data.providedMD5);
	bs >> data.calculatedCRC;
	bs.GetRaw(data.calculatedMD5);
	return bs;
}

SHARED_LIB_PUBLIC ByteStream& operator<<(ByteStream& bs, const ACK_FILE_LIST_DETAILED_RECORD& data)
{
	bs.PutRaw(data.fileName);
	bs << data.fileID
	   << data.fileSize
	   << data.creationDate
	   << data.modifiedDate
	   << data.fileAttributes
	   << data.providedCRC;
	bs.PutRaw(data.providedMD5);
	bs << data.calculatedCRC;
	bs.PutRaw(data.calculatedMD5);
	return bs;
}

XBeeDataRequestWrapper::XBeeDataRequestWrapper(uint16_t _size, uint8_t _debugLevel, EOutputFormat _outputFormat)
{
	size = _size;
	debugLevel = _debugLevel;
	outputFormat = _outputFormat;
}

XBeeDataRequestWrapper::XBeeDataRequestWrapper(const std::string& data, uint8_t _debugLevel, EOutputFormat _outputFormat)
{
	if (data.length() >= (sizeof(size) << 8))
	{
		size = (sizeof(size) << 8)-1;
		savedData.reset(new string(data.substr(0,size)));
	}
	else
	{
		size = data.length();
		savedData.reset(new string(data));
	}

	debugLevel = _debugLevel;
	outputFormat = _outputFormat;
}

SHARED_LIB_PUBLIC LiquidRobotics::SMACFramework::ByteStream& operator>>(LiquidRobotics::SMACFramework::ByteStream& bs, XBeeDataRequestWrapper& data)
{
	bs >> data.outputFormat >> data.debugLevel >> data.size;

	if (data.size)
	{
		data.savedData.reset(new string);
		ContainerIn<string> cont(*data.savedData, data.size);
		bs >> cont;
	}
	else
		data.savedData.reset();

	return bs;
}

SHARED_LIB_PUBLIC LiquidRobotics::SMACFramework::ByteStream& operator<<(LiquidRobotics::SMACFramework::ByteStream& bs, const XBeeDataRequestWrapper& data)
{
	bs << data.outputFormat << data.debugLevel << data.size;

	if (data.size && data.savedData)
		bs << ContainerOut<string>(*data.savedData);

	return bs;
}

}
}
}
