//-------------------------------------------------------------------------------------------------
// <copyright file="PayloadManager.cpp" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
// 	Implementation of PayloadManager, the controller class that implements the
//  IPayloadManagerProcess interface.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#include "LqrSMACIdl.h"
#include "omniORB4/CORBA.h"
#include "PayloadManager.h"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/algorithm/string.hpp>
#include "nsextend.h"
#include <errno.h>
#include "LqrSMACGetErrorDescription.h"
#include <time.h>
#include "PayloadRegistrationEvent.h"

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

typedef LiquidRobotics::SMACFramework::PayloadDescription LqrPyldDesc;

PayloadManager::PayloadManager(IObjectBroker& _broker, const PayloadManagerSettings& _settings, Logger& _logger)
: EventTransceiver(_settings.GetEventTransceiverSettings(), _broker, _logger)
, broker(_broker)
, logger(_logger)
, settings(_settings)
{
}

PayloadManager::~PayloadManager()
{
}

void PayloadManager::Initialize(Object_ptr _pServer)
{
	EventTransceiver::Initialize(_pServer);
}

::CORBA::Double PayloadManager::Ping()
{
	using namespace LiquidRobotics::SMACFramework;
	stringstream msg;
	msg << "DemoSensor::Ping: ";

	::CORBA::Double result;
	timeval tv;
	int status = gettimeofday(&tv, NULL);
	int errSave = errno;

	if (!status)
	{
		result = tv.tv_usec;
		result /= 1000000.0;
		result += tv.tv_sec;
	}
	else
	{
		msg << "(gettimeofday returned error #" << errSave << " '" << GetErrorDescription(errSave) << "') ";
		result = 0;
	}

	Logger::Lock logLock(logger);
	logger.Info.precision(15);
	logger.Info << msg.str() << result << endl;
	return result;
}

void PayloadManager::Register(const LiquidRobotics::SMAC::PayloadDescription& payload, ::CORBA::Boolean overwrite)
{
	Desc::Ptr desc;
	desc.reset(new Desc(payload));

	Logger::Lock logLock(logger);
	logger.Info << "Register payload={ " << *desc << " } overwrite=" << overwrite;

	string ior = desc->GetIOR();
	string readableName = desc->GetReadableName();
	Desc::BoardIDType boardID = desc->GetBoardID();
	Desc::TaskIDType taskID = desc->GetTaskID();
	unsigned countErased = 0;

	for (DescList::iterator it = payloadList.begin(); it != payloadList.end(); )
	{
		DescList::iterator it0 = it++;
		DescPtr item = *it0;

		const bool iorDuplicated = !item->GetIOR().compare(ior);
		const bool nameDuplicated = !item->GetReadableName().compare(readableName);
		const bool boardIDDuplicated = (boardID != NO_BOARD_ID) && (boardID == item->GetBoardID());
		const bool taskIDDuplicated = (taskID != NO_TASK_ID) && (taskID == item->GetTaskID());

		if (iorDuplicated || nameDuplicated || (boardIDDuplicated && taskIDDuplicated))
		{
			if (overwrite)
			{
				FireRegistrationEvent(settings.GetDeviceRemovedEventName(), **it0);
				++countErased;
				payloadList.erase(it0);
			}
			else
			{
				logger.Info << endl;
				logger.Error << "Duplicates entry { " << *it0 << " } "
							 << iorDuplicated << nameDuplicated << boardIDDuplicated << taskIDDuplicated << " !" << endl;
				ThrowCORBAException<AlreadyRegistered>();
			}
		}
	}

	if (countErased)
		logger.Info << "replacing " << countErased << " entries";

	payloadList.push_back(desc);
	logger.Info << ": " << payloadList.size() << " entries" << endl;

	FireRegistrationEvent(settings.GetDeviceAddedEventName(), *desc);
}

void PayloadManager::Unregister(const char* ior)
{
	Logger::Lock logLock(logger);
	logger.Info << "Unregister ior=" << ior;

	DescList::iterator it = payloadList.begin();

	while((it != payloadList.end()) && (*it)->GetIOR().compare(ior))
		++it;

	const bool found = (it != payloadList.end());

	if (found)
	{
		FireRegistrationEvent(settings.GetDeviceRemovedEventName(), **it);
		payloadList.erase(it);
		logger.Info << ": " << payloadList.size() << " entries" << endl;
	}
	else
	{
		logger.Info << endl;
		logger.Error << " -- not found!" << endl;
		ThrowCORBAException<NotRegistered>();
	}
}

LiquidRobotics::SMAC::BoardIDType PayloadManager::defaultBoardID()
{
	LiquidRobotics::SMAC::BoardIDType result = settings.GetDefaultBoardID();

	Logger::Lock logLock(logger);
	logger.Info << "defaultBoardID = 0x" << ultos(result, 16, 2) << endl;

	return result;
}

LiquidRobotics::SMAC::PayloadDescriptionList* PayloadManager::payloadDescriptions()
{
	DescList::CORBAEntity_var result = payloadList.GetEntity();

	Logger::Lock logLock(logger);
	logger.Info << "payloadDescriptions = " << payloadList.size() << " entries" << endl;

	return result._retn();
}

void PayloadManager::SendCommand(const ::CORBA::Any& command, const char* requester, const char* transactionID, ::CORBA::Any_OUT_arg result)
{
	const char* cmd0;

	if (!(command >>= cmd0))
	{
		logger.Error << "Incoming command type is not a string" << endl;
		ThrowCORBAException2<BadParameter>();
	}

	string cmd = cmd0;

	logger.Info << "PayloadManager::SendCommand command='" << cmd << "' requester='" << requester << "' transactionID='" << transactionID << "'";
	boost::trim(cmd);

	string resultStr;

	if (boost::iequals(cmd, "DUMP"))
	{
		stringstream stm;
		stm << payloadList;

		resultStr = stm.str();
	}
	else
	{
		logger.Info << endl;
		logger.Error << "Unknown command '" << cmd0 << "'" << endl;
		ThrowCORBAException2<BadParameter>();
	}

	logger.Info << " returning result='" << resultStr << "'" << endl;

	result.ptr() = new Any;
	*result.ptr() <<= resultStr.c_str();
}

void PayloadManager::FireRegistrationEvent(const string& eventName, const Desc& pyldDesc)
{
	EventType eventType(settings.GetCommonEventType(), settings.GetCommonEventDomain());
	PayloadRegistrationEvent regEvent(eventName, eventType, broker);
	regEvent.SetBoardID(pyldDesc.GetBoardID());
	regEvent.SetDescription(pyldDesc.GetDescription());
	regEvent.SetDescriptiveJSON(pyldDesc.GetDescriptiveJSON());
	regEvent.SetReadableName(pyldDesc.GetReadableName());
	regEvent.SetSenderIOR(pyldDesc.GetIOR());
	regEvent.SetTaskID(pyldDesc.GetTaskID());

	TransmitEvent(regEvent);
}
