//------------------------------------------------------------------------------
// <copyright file="VirtualRelayMessage.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>
// 	Contains some utility methods for Virtual Relay messages to WGMS
// </summary>
//
// <owner>Arash Ushani</owner>
//------------------------------------------------------------------------------

#include "VirtualRelayMessage.h"
#include "EventPropertyValue.h"
#include "CRC16.h"
#include "FCCMessage.h"

using namespace LiquidRobotics::PayloadInterface;

namespace LiquidRobotics {
namespace VirtualRelay {

VirtualRelayPacketComparision::Priority VirtualRelayPacketComparision::GetPriority(VR_LEVEL_2_Packet& p)
{
	FCCMessage::PCOMM_MGR_TX_BUFFER msg = (FCCMessage::PCOMM_MGR_TX_BUFFER) &p[0];

	switch (msg->header.type)
	{
		case FCCMessage::SND_TELEMETRY:
		case FCCMessage::SND_NEW_FILE_AVAILABLE:
		case FCCMessage::SND_SINGLE_PAYLOAD_PACKET:
		case FCCMessage::SND_MULTI_PAYLOAD_PACKET:
		case FCCMessage::SND_SINGLE_PAYLOAD_PACKET_RECORD:
		case FCCMessage::SND_PAYLOAD_PACKET:
		case FCCMessage::SND_PAYLOAD_PACKET_91:
		case FCCMessage::SND_PAYLOAD_PACKET_RECORD:
			return TELEMETRY;

		case FCCMessage::ACK_WITH_SETTINGS_WAYPT:
		case FCCMessage::ACK_WITH_SETTINGS:
		case FCCMessage::ACK_WITH_ID:
		case FCCMessage::ACK_BASIC:
		case FCCMessage::ACK_WITH_POWER:
		case FCCMessage::ACK_WITH_RUDDER:
		case FCCMessage::ACK_WITH_DEBUG:
		case FCCMessage::ACK_WITH_SUB_HEADING:
		case FCCMessage::ACK_WITH_INTEGRAL:
		case FCCMessage::ACK_WITH_PID_PARAMS:
		case FCCMessage::ACK_WITH_ALL_PARAMS:
		case FCCMessage::ACK_WITH_LIST_DETAILED:
		case FCCMessage::SND_CONSOLE_OUTPUT:
		case FCCMessage::ACK_WITH_UNIQUE_ID:
		case FCCMessage::ACK_WITH_FILE_AT_DEST:
		case FCCMessage::NAK_CMD_BASIC:
		case FCCMessage::NAK_DATA_BASIC:
		case FCCMessage::NAK_DATA_ENUMERATED:
		case FCCMessage::NACK_CONSOLE_DATA:
		case FCCMessage::ACK_WITH_FILE_LIST:
		case FCCMessage::ACK_CONSOLE_DATA:
		case FCCMessage::SND_IRIDIUM_MT_RESERVED:
		case FCCMessage::SND_SYSTEM_INFO:
			return COMMAND_RESPONSE;

		case FCCMessage::ACK_SECURE_ON:
			return SECURITY;

		case FCCMessage::ACK_WITH_READ_FILE:
			// Check for EOF Flag
			if (msg->ackReadFile.eofFlag)
				return FILE_TRANSFER_EOF;
			else
				return FILE_TRANSFER;

		case FCCMessage::SND_ENCRYPTED_PACKET:
		default:
			return OTHER;
	}
}

void VirtualRelayMessage::GetRelayStatusMessage(VirtualRelayMessage::PVR_RELAYSTATUS relayStatus, bool channelState, bool messageNotSent,
												bool commError, bool channelNotAuth, bool encrypted, uint8_t strength)
{
	relayStatus->type = FCCMessage::SND_RELAY_STATUS;

	relayStatus->notUsed1 = 0;

	relayStatus->size = sizeof(VR_RELAYSTATUS_MSG);

	relayStatus->msg.channelState = channelState ? 1:0;
	relayStatus->msg.messageNotSent = messageNotSent ? 1:0;
	relayStatus->msg.communicationsErrorAlarm = commError ? 1:0;
	relayStatus->msg.channelNotAuthenaticated = channelNotAuth ? 1:0;
	relayStatus->msg.channelEncryptedStatus = encrypted ? 1:0;

	relayStatus->msg.channelRevMajor = 2;
	relayStatus->msg.channelRevMinor = 0;
	relayStatus->msg.channelRevSubMinor = 0;

	time_t rawtime;
	time(&rawtime);

	tm* timestamp = gmtime(&rawtime);

	relayStatus->msg.hour = timestamp->tm_hour;
	relayStatus->msg.minute = timestamp->tm_min;
	relayStatus->msg.second = timestamp->tm_sec;
	relayStatus->msg.month = timestamp->tm_mon+1;
	relayStatus->msg.day = timestamp->tm_mday;
	relayStatus->msg.year = timestamp->tm_year-100;

	relayStatus->msg.signalStrength = strength;

	char* statusChar = reinterpret_cast<char*>(relayStatus);

	CRC16 msgCRC(statusChar, sizeof(VR_RELAYSTATUS)-2, 0);

	relayStatus->crc16 = msgCRC.Get();
}

void VirtualRelayMessage::GetRelayStatusMessage(VirtualRelayMessage::PVR_RELAYSTATUS relayStatus, bool channelState, bool messageNotSent, bool commError, bool channelNotAuth, bool encrypted)
{
	GetRelayStatusMessage(relayStatus, channelState, messageNotSent, commError, channelNotAuth, encrypted, 0xFF);
}

VirtualRelayMessage::UInt8Vector VirtualRelayMessage::GetOutgoingRelayMessage(VirtualRelayMessage::UInt8Vector& msg, byte protocolRevision, int vehicleUniqueID, int sessionID, uint16_t socketBufferSize)
{
	VR_SHIPTOSHOREHADER header;

	// Make sure to convert everything in the header to to big endian

	header.protocolRevision = protocolRevision;
	header.messageLength = bswap16(msg.size() + sizeof(VR_SHIPTOSHOREHADER) - 3);
	header.MOHeaderIEI = 0;
	header.MOHeaderLength = 0;
	header.CDR[0] = 'V';
	header.CDR[1] = 'R';
	header.CDR[2] = 'W';
	header.CDR[3] = 'F';
	header.uniqueID = __builtin_bswap32(vehicleUniqueID);
	header.ipAddr = 0;	 // Send a dummy IP because we cannot rely on this
	//header.ipAddr = __builtin_bswap32(inet_addr("0.0.0.0"));	// Use this for a real IP Address
	header.sessionID = __builtin_bswap32(sessionID);
	header.length = bswap16(socketBufferSize);
	header.spare = 0;
	header.sessionStatus = 0;
	header.MOMSN = 0;
	header.MTMSN = 0;
	header.timeOfSession = __builtin_bswap32(time(NULL));
	header.MOPayloadIEI = 0;
	header.MOPayloadLength = bswap16(msg.size());
	header.MOLocationInfoIEI = 0;
	header.MOLocationInfoLength = 0;
	header.MOLatLng[0] = 0;
	header.MOLatLng[1] = 0;
	header.MOLatLng[2] = 0;
	header.MOLatLng[3] = 0;
	header.MOLatLng[4] = 0;
	header.MOLatLng[5] = 0;
	header.MOLatLng[6] = 0;
	header.CEPRadius = 0;

	char* headerChar = reinterpret_cast<char*>(&header);
	EventLib::EventPropertyValue::UInt8Vector toSend(headerChar, headerChar + sizeof(VR_SHIPTOSHOREHADER));
	toSend.insert(toSend.end(), msg.begin(), msg.end());

	return toSend;
}

VirtualRelayMessage::UInt8Vector VirtualRelayMessage::GetOutgoingRelayMessage(char* msg, int msgLength, byte protocolRevision, int vehicleUniqueID, int sessionID, uint16_t socketBufferSize)
{
	UInt8Vector toSend(msg, msg+msgLength);
	return GetOutgoingRelayMessage(toSend, protocolRevision, vehicleUniqueID, sessionID, socketBufferSize);
}

void VirtualRelayMessage::AppendCRC(UInt8Vector& msg)
{
	uint16_t level2CRC = GetCRC(msg);

	msg.push_back(level2CRC & 0xFF);
	msg.push_back(level2CRC >> 8);
}

uint16_t VirtualRelayMessage::GetCRC(UInt8Vector& msg)
{
	CRC16 crc;

	crc.Reset();
	crc.Add(&msg[0], msg.size());
	return crc.Get();
}

}
}
