//------------------------------------------------------------------------------
// <copyright file="PayloadInterface.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 the main() function for the PayloadInterface process
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#include <iostream>
#include "PayloadInterface.h"
#include "PayloadManagerClient.h"
#include "PayloadMessage.h"
#include "PayloadInterfaceSettings.h"
#include "HDLCParser.h"
#include "HDLCPort.h"
#include "PayloadInterfaceController.h"
#include "SV3RemoteController.h"
#include "LqrSMACObjectBroker.h"
#include "LqrSMACIObjectBroker-inline.h"
#include "LqrSMACObjectBroker-inline.h"
#include "LqrSMACStdOutLogger.h"
#include <boost/algorithm/string.hpp>
#include <boost/thread.hpp>
#include "nsextend.h"

using namespace std;
using namespace LiquidRobotics::PayloadInterface;
using namespace LiquidRobotics::SMACFramework;
using namespace CORBA;

struct Program
{
	static int main(int argc, char* argv[])
	{
		if ((argc == 3) && boost::iequals(argv[1], "decode"))
		{
			ByteArray data;

			ImportString(argv[2], data);
			DecodeMessage(data);
			return 0;
		}
		else
			return main0(argc, argv);
	}

	static int main0(int argc, char* argv[])
	{
		int retcode = 0;					// program exit code

		// init time zone variables
		tzset();

		try
		{
			// load the settings from the XML file
			PayloadInterfaceSettings settings;
			settings.Load("PayloadInterface-config.xml");

			// get the server name and kind from the setings, initialize the logger
			const string serverName = settings.GetServerName();
			const string serverKind = settings.GetServerKind();
			StdOutLogger logger(settings.GetLoggerSettings());

			// initialize the object broker (enable event reception)
			ObjectBroker broker(true);
			broker.Initialize(argc, argv);

			// if the SV3 communications interface is enabled ...
			if (settings.GetSV3Enabled())
			{
				// construct the sv3 controller, which communicates to Regulus
				SV3RemoteController sv3(
						broker,
						logger,
						"PayloadInterface-telemetry-config.xml",
						settings.GetSV3NetInterfaceSettings(),
						settings.GetControllerSettings(),
						settings.GetSCSettings());

				// construct the tie class that will convey CORBA calls to the payload interface controller
				//
				// Note: I would use a Servant_var here, but that would destroy the derived class *after*
				//       the ORB is already shut down, which causes a crash
				PortableServer::Servant pServer = new ::POA_LiquidRobotics::SMAC::IPingableCommandableWithEvents_tie<SV3RemoteController>(&sv3);

				// advertize the server and initialize the event transceiver
				Object_var serverRef = broker.AdvertizeInstance(pServer, serverName.c_str(), serverKind.c_str());
				sv3.Initialize(serverRef.in());

				// listen for incoming CORBA requests
				broker.ListenForRequests();
			}
			else		// otherwise, assuming SV2 style payload protocol
			{
				// construct the payload interface controller, which communicates to a master
				PayloadInterfaceController pic(settings.GetPortSettings(), settings.GetSCSettings(),
						settings.GetControllerSettings(), broker, logger,
						"PayloadInterface-telemetry-config.xml");
				HDLCPort::Ptr pHDLC = pic.InitHDLC();

				// construct the tie class that will convey CORBA calls to the payload interface controller
				//
				// Note: I would use a Servant_var here, but that would destroy the derived class *after*
				//       the ORB is already shut down, which causes a crash
				PortableServer::Servant pServer = new ::POA_LiquidRobotics::SMAC::IPingableCommandableWithEvents_tie<PayloadInterfaceController>(&pic);

				// advertize the server and initialize the event transceiver
				Object_var serverRef = broker.AdvertizeInstance(pServer, serverName.c_str(), serverKind.c_str());
				pic.Initialize(serverRef.in());

				// start the protocol asynchronously
				boost::thread runThread(boost::bind(&PayloadInterfaceController::Run, &pic, pHDLC));

				// listen for incoming CORBA requests
				broker.ListenForRequests();
			}
		}
		catch(CORBA::Exception& ex)
		{
			cerr << argv[0] << " caught CORBA exception: " << DecodeCORBAException(ex) << " (program will exit)" << endl;
			retcode = 1;
		}
		catch(exception& ex)
		{
			cerr << argv[0] << " caught exception " << typeid(ex).name() << ": " << ex.what() << " (program will exit)" << endl;
			retcode = 1;
		}

		return retcode;
	}

	static void DecodeMessage(const ByteArray& data)
	{
		ByteArray temp(data.begin(), data.end());
		ByteArray output;
		HDLCParser parser(output, 400);

		const ByteArray::size_type countUsed = parser.Feed(temp);

		if (countUsed != temp.size())
			cout << temp.size() << " bytes were fed in, but " << countUsed << " were used" << endl;

		cout << "Parsing operation was " << (parser.Succeeded() ? "" : "not ") << "successful" << endl;
		cout << "Resulting data = " << output.ToStringSplitView() << endl;

		PayloadMessage::Ptr message;

		try
		{
			message.reset(new PayloadMessage(output));
		}
		catch(exception& ex)
		{
			cout << "Failed to create message from resulting data: " << ex.what() << endl;
		}

		if (message)
			cout << "Resulting message = " << message->ToString() << endl;
	}

	static void ImportString(const string& str, ByteArray& data)
	{
		using namespace boost;

		StringArray tokens;
		split(tokens, str, is_any_of(" \t"), token_compress_on );
		data.reserve(tokens.size());

		for (StringArray::const_iterator ci = tokens.begin(); ci != tokens.end(); ++ci)
		{
			string s;

			if (boost::istarts_with(*ci, "0x"))
				s = ci->substr(2);
			else
				s = *ci;

			char* pEnd = NULL;
			long val = strtol(s.c_str(), &pEnd, 16);

			if (pEnd && *pEnd)
				throw "Invalid byte conversion";

			data.push_back((ByteArray::byte) val);
		}
	}

	typedef vector<string> StringArray;
};

int main(int argc, char* argv[])
{
	return Program::main(argc, argv);
}
