//----------------------------------------------------------------------------------
// <copyright file="PayloadInterfaceController_Config.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 settings for the PayloadInterface class
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#include "PayloadInterfaceController_Config.h"
#include "LqrSMACBooleanParser.h"
#include "LqrSMACLockableConcept.h"
#include "nsextend.h"
#include <sstream>

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

namespace LiquidRobotics {
namespace PayloadInterface {

inline void ReplaceAll(string& io, const string& replacement, const string& pattern)
{
	size_t loc;

	while((loc = io.find(pattern)) != string::npos)
	{
		stringstream newVal;

		if (loc)
			newVal << io.substr(0, loc);

		newVal << replacement;

		const size_t tailStart = loc+pattern.length();

		if (tailStart < io.length())
			newVal << io.substr(tailStart);

		io = newVal.str();
	}
}

PayloadInterfaceController_Config::PayloadInterfaceController_Config()
: description("Sensor mgmt computer")
, deviceType(136)
, serialNum("000000")
, pollingRate(15)
, wantTelemetry(false)
, wantPowerNotification(true)
, wantEvents(false)
, wantEchoedAcks(false)
, firmwareMajor(1)
, firmwareMinor(0)
, firmwareRev(3)
//, demoServerList
, shutdownVerb("sudo shutdown -h now")
, powerOffDelay(60)
, powerOnDelay(0)
, dateVerb("")				// not typically used since GPSListener sets the date based on GPS
, shutdownEvent("Shutdown", "EventBase", "LiquidR-SMC")
, shutdownDelay(5)
, FCCIncomingMessageEvent("FCCIncomingMessage", "FCCMessageEvent", "LiquidR-SMC")
, FCCOutgoingMessageEvent("FCCOutgoingMessage", "FCCMessageEvent", "LiquidR-SMC")
, commandCompleteEvent("CommandComplete", "SendCommandCompleteEvent", "LiquidR-SMC")
, gpsSampleEvent("GPSSample", "GPSSample", "LiquidR-SMC")
, telemetryChangedEvent("TelemetryChangedEvent", "TelemetryChangedEvent", "LiquidR-SMC")
, addressableMessageEvent("PayloadAddressableMessage", "AddressableMessageEvent", "LiquidR-SMC")
, alarmReportEvent("AlarmMessage", "AlarmMessageEvent", "LiquidR-SMC")
, systemGPSOfflineEvent("SystemGPSOffline", "EventBase", "LiquidR-SMC")
, systemGPSOnlineEvent("SystemGPSOnline", "EventBase", "LiquidR-SMC")
, hostGPSEvent("HostGPSSample", "EventBase", "LiquidR-SMC")
, maxCommandOutput(100000)
, tarProcessingCommand("PyldRunTarCmd")
, maxConsoleOutputBlocks(4)
, distributeGPS(false)
, enableNakWithDescription(true)
{
	newFileAvailableEvent.push_back(EventAndDestination(EventSettings("PyldNewFileAvailable", "DataSetEvent", "LiquidR-SMC"), 0));
	demoServerList.push_back("Example #1");
	setParamInfoList.push_back(SetParamInfo(0x100, "PL1", "Payload1", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x200, "PL2", "Payload2", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x300, "SubPL", "SubPayload", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x400, "iPIB", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x401, "AIS", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x402, "Weather", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x403, "Water", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x20, "Bubble", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x21, "Sub", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0xF, "InvalidGPSCount", "UINT8"));
	setParamInfoList.push_back(SetParamInfo(0x30, "PIDNav", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x31, "PGain", "INT16"));
	setParamInfoList.push_back(SetParamInfo(0x32, "IGain", "INT16"));
	setParamInfoList.push_back(SetParamInfo(0x33, "ISatLimit", "UINT16"));
	setParamInfoList.push_back(SetParamInfo(0xFD, "CamCtrl", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0xFB, "FTReset", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x14, "SBDAlwaysOn", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x15, "SBDDefaultOn", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x16, "XBEEPANID", "UINT16"));
	setParamInfoList.push_back(SetParamInfo(0x17, "XBEECHANNEL", "UINT8"));
	setParamInfoList.push_back(SetParamInfo(0x18, "ENUMDELAY", "UINT16"));
	setParamInfoList.push_back(SetParamInfo(0x19, "FASTMB", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x500, "PL3", "Payload3", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x700, "PEP", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x410, "iPIBRESET", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x19, "MAGDEC", "INT8"));
	setParamInfoList.push_back(SetParamInfo(0x50, "ModemOffRequiredPort", "UINT8"));
	setParamInfoList.push_back(SetParamInfo(0x51, "ModemOffRequiredAddress", "UINT16"));
	setParamInfoList.push_back(SetParamInfo(0x52, "ModemPower", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x53, "ModemPowerAtBoot", "BOOLEAN"));
	setParamInfoList.push_back(SetParamInfo(0x41, "GPSAltitudeFilter", "BOOLEAN"));
}

string PayloadInterfaceController_Config::GetDateVerbGMT(int year, int month, int day, int hour, int minute, int second) const
{
	string verb = GetDateVerb();

	ReplaceAll(verb, utos(year, 10, 4), "{yyyy}");
	ReplaceAll(verb, utos(year % 100, 10, 2), "{yy}");
	ReplaceAll(verb, utos(month, 10, 2), "{mm}");
	ReplaceAll(verb, utos(day, 10, 2), "{dd}");
	ReplaceAll(verb, utos(hour, 10, 2), "{HH}");
	ReplaceAll(verb, utos(((hour == 0) || (hour == 12)) ? 12 : (hour % 12), 10, 2), "{hh}");
	ReplaceAll(verb, utos(minute, 10, 2), "{MM}");
	ReplaceAll(verb, utos(second, 10, 2), "{SS}");

	return verb;
}

const EventSettings& PayloadInterfaceController_Config::GetShutdownEventSettings() const
{
	return shutdownEvent;
}

void PayloadInterfaceController_Config::SetShutdownEventSettings(const EventSettings& newValue)
{
	shutdownEvent = newValue;
}

unsigned PayloadInterfaceController_Config::GetShutdownDelay() const
{
	return shutdownDelay;
}

void PayloadInterfaceController_Config::SetShutdownDelay(unsigned newValue)
{
	shutdownDelay = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetFCCIncomingMessageEventSettings() const
{
	return FCCIncomingMessageEvent;
}

void PayloadInterfaceController_Config::SetFCCIncomingMessageEventSettings(const EventSettings& newValue)
{
	FCCIncomingMessageEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetFCCOutgoingMessageEventSettings() const
{
	return FCCOutgoingMessageEvent;
}

void PayloadInterfaceController_Config::SetFCCOutgoingMessageEventSettings(const EventSettings& newValue)
{
	FCCOutgoingMessageEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetCommandCompleteEventSettings() const
{
	return commandCompleteEvent;
}

void PayloadInterfaceController_Config::SetCommandCompleteEventSettings(const EventSettings& newValue)
{
	commandCompleteEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetGpsSampleEventSettings() const
{
	return gpsSampleEvent;
}

void PayloadInterfaceController_Config::SetGpsSampleEventSettings(const EventSettings& newValue)
{
	gpsSampleEvent = newValue;
}

const PayloadInterfaceController_Config::EventAndDestinationList& PayloadInterfaceController_Config::GetNewFileAvailableEventSettings() const
{
	return newFileAvailableEvent;
}

void PayloadInterfaceController_Config::SetNewFileAvailableEventSettings(const EventAndDestinationList& newValue)
{
	if (&newValue != &newFileAvailableEvent)
	{
		newFileAvailableEvent.clear();

		for (EventAndDestinationList::const_iterator it = newValue.begin(); it != newValue.end(); ++it)
			newFileAvailableEvent.push_back(*it);
	}
}

const EventSettings& PayloadInterfaceController_Config::GetTelemetryChangedEventSettings() const
{
	return telemetryChangedEvent;
}

void PayloadInterfaceController_Config::SetTelemetryChangedEventSettings(const EventSettings& newValue)
{
	telemetryChangedEvent = newValue;
}

const EventTransceiverSettings& PayloadInterfaceController_Config::GetEventTransceiverSettings() const
{
	return etSettings;
}

void PayloadInterfaceController_Config::SetEventTransceiverSettings(const EventTransceiverSettings& newValue)
{
	etSettings = newValue;
}

size_t PayloadInterfaceController_Config::GetMaxCommandOutput() const
{
	return maxCommandOutput;
}

void PayloadInterfaceController_Config::SetMaxCommandOutput(size_t newValue)
{
	if (newValue < 1000)
		throw invalid_argument("Maximum command output must be at least 1000 bytes");
	else
		maxCommandOutput = newValue;
}

string PayloadInterfaceController_Config::GetTarProcessingCommand() const
{
	return tarProcessingCommand;
}

void PayloadInterfaceController_Config::SetTarProcessingCommand(const string& newValue)
{
	tarProcessingCommand = newValue;
}

unsigned PayloadInterfaceController_Config::GetMaxConsoleOutputBlocks() const
{
	return maxConsoleOutputBlocks;
}

void PayloadInterfaceController_Config::SetMaxConsoleOutputBlocks(unsigned newValue)
{
	maxConsoleOutputBlocks = newValue;
}

const PayloadManagerClientSettings& PayloadInterfaceController_Config::GetPayloadManagerClientSettings() const
{
	return pmSettings;
}

void PayloadInterfaceController_Config::SetPayloadManagerClientSettings(const PayloadManagerClientSettings& newValue)
{
	pmSettings = newValue;
}

void PayloadInterfaceController_Config::SetDistributeGPS(const bool newValue)
{
        distributeGPS = newValue;
}

const PayloadInterfaceController_Config::SetParamInfoList& PayloadInterfaceController_Config::GetSetParamInfoList() const
{
	return setParamInfoList;
}

ostream& operator<<(ostream& stm, const PayloadInterfaceController_Config& settings)
{
	stm << " description = '" << settings.GetDescription()  << "'"
	    << " deviceType = 0x" << ltos(settings.GetDeviceType(), 16, 4)
	    << " serialNum = '" << settings.GetSerialNum() << "'"
	    << " pollingRate = " << settings.GetPollingRate()
	    << " wantTelemetry = " << BooleanToString(settings.GetTelemetryRequested())
	    << " wantPowerNotification = " << BooleanToString(settings.GetPowerNotificationRequested())
	    << " wantEvents = " << BooleanToString(settings.GetEventsRequested())
	    << " wantEchoedAcks = " << BooleanToString(settings.GetEchoedAcksRequested())
	    << " firmwareMajor = " << settings.GetFirmwareMajor()
	    << " firmwareMinor = " << settings.GetFirmwareMinor()
	    << " firmwareRev = " << settings.GetFirmwareRev()
	    << " demoServerList = {";

	const PayloadInterfaceController_Config::StringList& serverList = settings.GetDemoServerList();

	for (PayloadInterfaceController_Config::StringList::const_iterator it = serverList.begin(); it != serverList.end(); ++it)
	{
		const string& server = *it;
		stm << " '" << server << "'";
	}

	stm << " }"
	    << " shutdownVerb = '" << settings.GetShutdownVerb() << "'"
	    << " powerOffDelay = " << settings.GetPowerOffDelay()
	    << " powerOnDelay = " << settings.GetPowerOnDelay()
	    << " dateVerb = '" << settings.GetDateVerb() << "'"
	    << " shutdownEvent = " << settings.GetShutdownEventSettings()
	    << " shutdownDelay = " << settings.GetShutdownDelay()
	    << " commandCompleteEvent = " << settings.GetCommandCompleteEventSettings()
	    << " gpsSampleEvent = " << settings.GetGpsSampleEventSettings()
	    << " newFileAvailableEvent = {";


	typedef PayloadInterfaceController_Config::EventAndDestinationList EDList;

	const EDList& nfaList = settings.GetNewFileAvailableEventSettings();

	for (EDList::const_iterator it = nfaList.begin(); it != nfaList.end(); ++it)
		stm << " { { " << it->first << " }, " << it->second << " }";

	stm << " }"
	    << " telemetryChangedEvent = " << settings.GetTelemetryChangedEventSettings()
	    << " addressedMessageEvent = " << settings.GetAddressedMessageEventSettings()
	    << " alarmReportEvent = " << settings.GetAlarmReportEventSettings()
	    << " etSettings = { " << settings.GetEventTransceiverSettings() << " }"
	    << " pmSettings = { " << settings.GetPayloadManagerClientSettings() << " }"
	    << " distributeGPS = " << BooleanToString(settings.GetDistributeGPS())
	    << " setParamInfoList = {";

	const PayloadInterfaceController_Config::SetParamInfoList& spil = settings.GetSetParamInfoList();
	bool spilfirst = true;

	for (PayloadInterfaceController_Config::SetParamInfoList::const_iterator ci = spil.begin(); ci != spil.end(); ++ci)
	{
		if (spilfirst)
		{
			spilfirst = true;
			stm << " ";
		}
		else
			stm << ", ";

		bool first = true;

		stm << " { 0x" << utos(ci->paramID,16,4) << ", {";

		for (PayloadInterfaceController_Config::StringList::const_iterator ci2 = ci->paramNames.begin(); ci2 != ci->paramNames.end(); ++ci2)
		{
			if (first)
			{
				first = false;
				stm << " ";
			}
			else
				stm << ", ";

			stm << *ci2;
		}

		stm << " }," << ci->paramType << " }"
			<< " enableNakWithDescription=" << BooleanToString(settings.GetEnableNakWithDescription());
	}

	return stm;
}

const EventSettings& PayloadInterfaceController_Config::GetAddressedMessageEventSettings() const
{
	return addressableMessageEvent;
}

void PayloadInterfaceController_Config::SetAddressedMessageEventSettings(const EventSettings& newValue)
{
	addressableMessageEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetAlarmReportEventSettings() const
{
	return alarmReportEvent;
}

void PayloadInterfaceController_Config::SetAlarmReportEventSettings(const EventSettings& newValue)
{
	alarmReportEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetSystemGPSOfflineSettings() const
{
	return systemGPSOfflineEvent;
}

void PayloadInterfaceController_Config::SetSystemGPSOfflineSettings(const EventSettings& newValue)
{
	systemGPSOfflineEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetSystemGPSOnlineSettings() const
{
	return systemGPSOnlineEvent;
}

void PayloadInterfaceController_Config::SetSystemGPSOnlineSettings(const EventSettings& newValue)
{
	systemGPSOnlineEvent = newValue;
}

const EventSettings& PayloadInterfaceController_Config::GetHostGPSEventSettings() const
{
	return hostGPSEvent;
}

void PayloadInterfaceController_Config::SetHostGPSEventSettings(const EventSettings& newValue)
{
	hostGPSEvent = newValue;
}

}
}
