//-----------------------------------------------------------------------------------
// <copyright file="FCCMessage_CommHeaderMessageGeneric.h" 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 the CommHeaderMessageGeneric class, which represents a message
//   based on a COMM_HEADER, and which provides methods streaming the data.
// </summary>
//
// <owner>Mike Cookson</owner>
//-----------------------------------------------------------------------------------

#ifndef FCCMESSAGE_COMMHEADERMESSAGEGENERIC_H_
#define FCCMESSAGE_COMMHEADERMESSAGEGENERIC_H_

#include "FCCMessage.h"
#include "CRC16.h"
#include <stdexcept>
#include <string>
#include <boost/shared_ptr.hpp>
#include "LqrSMACByteArray.h"
#include "LqrSMACSharedLib.h"
#include "LqrSMACDeclareException.h"

namespace LiquidRobotics {
namespace PayloadInterface {
namespace FCCMessage {

class SHARED_LIB_PUBLIC CommHeaderConversionBase
{
public:

	typedef SMACFramework::ByteArray::byte byte;
	typedef SMACFramework::ByteArray ByteArray;
	typedef std::string string;

	virtual ~CommHeaderConversionBase()
	{
	}

	virtual CommHeaderConversionBase* Duplicate() const = 0;
	virtual void FromArray(const ByteArray& _data, unsigned offset = 0) = 0;
	virtual void FromArray(const byte* _pData, unsigned dataLength) = 0;
	virtual void ToArray(ByteArray& result, bool includeCRC = true) const = 0;
	virtual string ToString() const = 0;
};

class SHARED_LIB_PUBLIC CommHeaderMessageGeneric : public CommHeaderConversionBase
{
public:

	typedef std::invalid_argument invalid_argument;
	typedef std::runtime_error runtime_error;
	typedef CRC16::CRCType CRCType;

	DECLARE_EXCEPTION(BadCRC, runtime_error);
	DECLARE_EXCEPTION(NotACommand, runtime_error);

	CommHeaderMessageGeneric(unsigned _simpleTypeSize, bool _withCRC, unsigned extraBytes = 0);
	CommHeaderMessageGeneric(unsigned _simpleTypeSize, bool _withCRC, const ByteArray& byteData, unsigned offset);
	CommHeaderMessageGeneric(unsigned _simpleTypeSize, bool _withCRC, const byte* pData, unsigned dataLength);
	CommHeaderMessageGeneric(unsigned _simpleTypeSize, bool _withCRC, uint8_t _type, uint32_t _packetID, const CommHeaderConversionBase& _data);
	CommHeaderMessageGeneric(unsigned _simpleTypeSize, bool _withCRC, uint8_t _type, uint32_t _packetID, const void* _pData, unsigned extraBytes);

	CommHeaderMessageGeneric(const CommHeaderMessageGeneric& right);

	virtual ~CommHeaderMessageGeneric();

	unsigned BaseSize(unsigned extraBytes = 0) const;
	unsigned HeaderSize() const;

	inline bool GetFSReport() const				{ return isFSReport; }
	inline void SetFSReport(bool newValue)		{ isFSReport = newValue; }
	inline uint8_t GetType() const				{ return header.type; }
	inline void SetType(uint8_t newValue) 		{ header.type = newValue; }
	inline ByteArray& GetTypeBuffer()			{ return data; }
	inline const ByteArray& GetTypeBuffer() const { return data; }
	inline byte& GetTypeRef()					{ return data.at(0); }
	inline const byte& GetTypeRef() const		{ return data.at(0); }
	inline uint32_t GetPacketID() const			{ return header.packetID; }
	inline void SetPacketID(uint32_t newValue)	{ header.packetID = newValue; }

	static uint8_t ContainedType(const byte* pData, unsigned dataLength);
	static FCCMessage::COMM_HEADER ContainedHeader(const byte* pData, unsigned dataLength);
	static FCCMessage::CMD_FMT_COMMON ContainedCmd(const byte* pData, unsigned dataLength);

	virtual CommHeaderConversionBase* Duplicate() const;

	void FromArray(const ByteArray& _data, unsigned offset = 0);
	void FromArray(const byte* _pData, unsigned dataLength);

	void ToArray(ByteArray& result, bool includeCRC = true) const;
	std::ostream& ToStream(std::ostream& out) const;
	string ToString() const;

protected:

	CRCType ComputeCRC() const;

	void ZeroHeader();
	void ZeroData();
	void Zero();

	const unsigned simpleTypeSize;
	const bool withCRC;
	bool isFSReport;
	COMM_HEADER header;
	ByteArray data;
	boost::shared_ptr<CommHeaderConversionBase> pConversionBaseData;
};

std::ostream& operator<<(std::ostream& out, const CommHeaderMessageGeneric& message);

}
}
}

#endif /* FCCMESSAGE_COMMHEADERMESSAGEGENERIC_H_ */
