//------------------------------------------------------------------------------
// <copyright file="main.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>
// </summary>
//
// <owner></owner>
//------------------------------------------------------------------------------

#include <iostream>
#include <boost/algorithm/string.hpp>
#include "SBDModem.h"
#include "SBDModemSettings.h"
#include "SSQProgram.h"
#include "SSQProgramSettings.h"
#include "SSQCommunicator.h"
#include "LqrSMACObjectBroker.h"
#include "LqrSMACStdOutLogger.h"
#include "LqrSMACStdOutLoggerSettings.h"
#include "nsextend.h"

using namespace std;
using namespace LiquidRobotics::PPPHostLib;
using namespace LiquidRobotics::SMACFramework;
using namespace LiquidRobotics::SimpleSBDQueue;

struct ListenToMessages
{
	ListenToMessages(int argc, char* argv[])
    : testResult(1)
	, logger("SSQ-Listen", 1, Logger::LT_DEBUG)
    {
        SBDModemSettings modemSettings;
        SerialPortSettings spSettings = modemSettings.GetSerialPortSettings();
        spSettings.SetPortName(argc ? argv[0] : "/dev/ttyUSB0");
        spSettings.SetBaudRate(ISerialPort::BAUD_19200);
        spSettings.SetCharacterSize(ISerialPort::CHAR_SIZE_8);
        spSettings.SetStopBits(ISerialPort::STOP_BITS_2);
        spSettings.SetParity(ISerialPort::PARITY_NONE);
        spSettings.SetFlowControl(ISerialPort::FLOW_CONTROL_NONE);
        spSettings.SetDtr(true);
        spSettings.SetRts(true);

        modemSettings.SetAckTimeoutMS(30000);
        modemSettings.SetLongTransactionTimeoutMS(120000);
        modemSettings.SetShortTransactionTimeoutMS(60000);
        modemSettings.SetMaxTries(3);
        modemSettings.SetSerialPortSettings(spSettings);

		SSQCommunicatorSettings cmqSettings;
		cmqSettings.SetSBDModemSettings(modemSettings);
		cmqSettings.SetMinOffTimeMS(3000);
		cmqSettings.SetMaxOnTimeMS(0);
		cmqSettings.SetDutyCycleModem(false);
		cmqSettings.SetNetworkAvailableTimeoutMS(8*60*1000);
		cmqSettings.SetMailboxCheckIntervalMS(5*60*1000);
		cmqSettings.SetModemPowerOnDelayMS(25*1000);
		cmqSettings.SetNetworkFailDelayMS(8*60*1000);
		cmqSettings.SetCommFailureDelayMS(15*60*1000);
		cmqSettings.SetUseRingIndicator(true);
		cmqSettings.SetPowerOnVerb("echo \"Power ON\"");
		cmqSettings.SetPowerOffVerb("echo \"Power OFF\"");
		cmqSettings.SetNetworkTimeoutSeconds(2*60*1000);
		cmqSettings.SetWaitForNetworkRegistration(true);

		SSQCommunicator communicator(cmqSettings, logger);
		SSQCommunicator::ScopedConnectionPtr sub = communicator.SubscribeReceivedData(boost::bind(&ListenToMessages::OnReceive, this, _1, _2, _3));
		SSQCommunicator::ScopedConnectionPtr sub2 = communicator.SubscribeStatistics(boost::bind(&ListenToMessages::OnStatistics, this, _1, _2));
		communicator.Initialize();

		while(true)
			sleep(60);
    }

	void OnReceive(SSQCommunicator& sender, const ByteArray& packetData, unsigned MTMSN)
	{
		//Logger::Locked(logger)->Debug << "Received data MTMSN=" << MTMSN << " data=" << packetData.ToHexString() << endl;
	}

	void OnStatistics(SSQCommunicator& sender, const SSQCommunicator::Statistics& stats)
	{
		//Logger::Locked(logger)->Debug << "Received stats: " << stats.ToString() << endl;
	}

    operator int() const
    {
        return testResult;
    }

	int testResult;
	StdOutLoggerSettings stdOutLoggerSettings;
	StdOutLogger logger;
};

struct Test
{
    Test(int argc, char* argv[])
    : testResult(1)
    {
        StdOutLoggerSettings stdOutLoggerSettings;
        stdOutLoggerSettings.SetFileDescriptor(1);
        stdOutLoggerSettings.SetProcessName("SimpleSBDModem-Test");
        stdOutLoggerSettings.SetLogLevel(Logger::LT_DEBUG);

        StdOutLogger logger(stdOutLoggerSettings);

        SBDModemSettings settings;
	settings.SetAckTimeoutMS(30000);
	settings.SetLongTransactionTimeoutMS(120000);
	settings.SetShortTransactionTimeoutMS(60000);
	settings.SetMaxTries(3);

        SerialPortSettings spSettings = settings.GetSerialPortSettings();
        spSettings.SetPortName(argc ? argv[0] : "/dev/ttyUSB0");
        spSettings.SetBaudRate(ISerialPort::BAUD_19200);
        spSettings.SetCharacterSize(ISerialPort::CHAR_SIZE_8);
        spSettings.SetStopBits(ISerialPort::STOP_BITS_2);
        spSettings.SetParity(ISerialPort::PARITY_NONE);
        spSettings.SetFlowControl(ISerialPort::FLOW_CONTROL_NONE);
        spSettings.SetDtr(true);
        spSettings.SetRts(true);
        settings.SetSerialPortSettings(spSettings);

	using namespace boost::posix_time;

	SerialListenerSettings slSettings;
	slSettings.SetMaxReadSize(4096);
	slSettings.SetIntercharacterGapTimeout(millisec(50));
	settings.SetSerialListenerSettings(slSettings);

        SBDModem modem(logger, settings);
        modem.Intialize();
        modem.ConfigureInterface();

	for (unsigned n = 1; n--; )
	{
		cout << "=== Cycles left = " << (n+1) << " ===" << endl;
		modem.Ping();
		const string manufacturer = modem.GetManufacturer();
		cout << "manufacturer = " << manufacturer << endl;
		const string model = modem.GetModel();
		cout << "model = " << model << endl;
		const string serialNumber = modem.GetSerialNumber();
		cout << "serialNumber = " << serialNumber << endl;
		const string simNumber = modem.GetSIMCardNumber();
		cout << "simNumber = " << simNumber << endl;
		const SBDModem::IndicatorEventReporting indicators = modem.GetIndicatorEventReporting();
                cout << "indicators={ " << indicators.ToString() << " }" << endl;
		modem.SetIndicatorEventReporting(indicators);
                const SBDModem::NetworkRegistration netreg = modem.GetNetworkRegistration();
                cout << "netreg={ " << netreg.ToString() << " }" << endl;
                modem.SetNetworkRegistration(netreg.notification);
                SBDModem::StringArrayPtr revisions = modem.GetRevisions();
                const bool ri = modem.GetRingIndication();
                modem.SetRingIndication(!ri);
                modem.SetRingIndication(ri);
		const string mtBuffer = modem.ReadMTBuffer();
		cout << "mtBuffer = " << mtBuffer.length() << " bytes: ";
		for (string::const_iterator ci = mtBuffer.begin(); ci != mtBuffer.end(); ++ci)
		{
			unsigned char b = (unsigned char) *ci;
			cout << utos(b, 16, 2);
		}
		cout << endl;
	}

        modem.Shutdown();

        testResult = 0;
    }

    operator int() const
    {
        return testResult;
    }

    int testResult;
};

struct CORBAServer
{
	CORBAServer(int argc, char** argv)
	: retcode(0)
	{
                using namespace LiquidRobotics::EventLib;
                        using namespace LiquidRobotics::SimpleSBDQueue;
                using namespace CORBA;

                tzset();

		try
		{
			SSQProgramSettings progSettings;
			progSettings.Load("SimpleSBDQueue-config.xml");

			const SensorProgramSettings& settings = progSettings.GetSensorProgramSettings();

			const string serverName = settings.GetServerName();
			const string serverKind = settings.GetServerKind();
			StdOutLogger logger(settings.GetLoggerSettings());

			ObjectBroker broker(true);
			broker.Initialize(argc, argv);

                        SSQProgram prog(broker, progSettings, logger);

			PortableServer::ServantBase_var theServer = 0;
			theServer = new ::POA_LiquidRobotics::SMAC::ISensorWithEvents_tie<SSQProgram>(prog);

			Object_var serverRef = broker.AdvertizeInstance(theServer.in(), serverName.c_str(), serverKind.c_str());
			const PayloadManagerClientSettings& pmClientSettings = settings.GetPayloadManagerClientSettings();
			PayloadManagerClient::Ptr pmClient;
			pmClient.reset(new PayloadManagerClient(pmClientSettings, broker, prog));

			prog.Initialize(serverRef.in(), pmClient);
			pmClient->Register(serverRef.in());

			broker.ListenForRequests();
		}
		catch(CORBA::Exception& ex)
		{
			cerr << "Caught CORBA exception: " << DecodeCORBAException(ex) << endl;
			retcode = 1;
		}
		catch(exception& ex)
		{
			cerr << "Caught " << typeid(ex).name() << ": " << ex.what() << endl;
			retcode = 1;
		}
	}

	operator int()
	{
		return retcode;
	}

	int retcode;
};

int main(int argc, char* argv[])
{
    int result = 0;

    if ((argc >= 2) && boost::iequals(argv[1], "test"))
        result = Test(argc-2, argv+2);
    else if ((argc >= 2) && boost::iequals(argv[1], "listen"))
    	result = ListenToMessages(argc-2, argv+2);
    else
        result = CORBAServer(argc-2, argv+2);

    return result;
}
