//-------------------------------------------------------------------------------
// <copyright file="SV3NetInterface_PollingLoop.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 SV3NetInterface PollingLoop inner class, which implements
//  polling for commands, optional reading of GPS data from the Host and optional
//  shutdown detection.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------

#include "SV3NetInterface.h"

using namespace std;
using namespace LiquidRobotics::SMACFramework;

namespace LiquidRobotics {
namespace PayloadInterface {

SV3NetInterface::PollingLoop::PollingLoop(SV3NetInterface& _net)
: pollingStatusSignalSub(pollingStatusSignal, firingMutex)
, net(_net)
, mutex(_net.mutex)
, wakeSem(0)
, upTimeAtPollingStatus(0.0)
, polling(false)
, pollForGPS(false)
, threadExited(true)
{
}

bool SV3NetInterface::PollingLoop::IsPolling()
{
	ILockableConceptLock lock(mutex);
	return polling;
}

void SV3NetInterface::PollingLoop::NoteAllRegistered()
{
	ILockableConceptLock lock(mutex);
	polling = true;
	wakeSem.Post();
}

void SV3NetInterface::PollingLoop::NoteGPSRequired(bool required)
{
	ILockableConceptLock lock(mutex);
	pollForGPS = required;
	wakeSem.Post();
}

void SV3NetInterface::PollingLoop::Start()
{
	if (threadExited) {
		Logger::Locked(net.logger)->Info << "(PollingLoop) Starting thread" << endl;
		ILockableConceptLock lock(mutex);
		wakeSem.SetCount(0);
		upTimeAtPollingStatus = GetSystemUpTimeSecondsDouble();
		threadExited = false;
		threadp.reset(new boost::thread(boost::bind(&SV3NetInterface::PollingLoop::Loop, this)));
	}
	else
		Logger::Locked(net.logger)->Info << "(PollingLoop) Already started" << endl;
}

void SV3NetInterface::PollingLoop::Stop()
{
	if (threadExited)
		Logger::Locked(net.logger)->Info << "(PollingLoop) Not started" << endl;
	else {
		Logger::Locked(net.logger)->Info << "(PollingLoop) Stopping thread" << endl;
		threadExited = false;
		threadp->join();
		threadp.reset();
	}
}

void SV3NetInterface::PollingLoop::Loop()
{
	Logger::Locked(net.logger)->Info << "(PollingLoop) Begin background thread" << endl;

	while(!threadExited) {

		while(!threadExited && !polling) {
			Logger::Locked(net.logger)->Debug << "(PollingLoop) Waiting for polling to be enabled" << endl;
			wakeSem.Wait();
		}

		SV3NetChannel::PtrArray pollList;

		{
			ILockableConceptLock lock(net.mutex);
			pollList.reserve(net.channels.size());
			for (ChannelArray::const_iterator ci = net.channels.begin(); ci != net.channels.end(); ++ci)
				if ((*ci)->channel->GetRegistered())
					pollList.push_back((*ci)->channel);

			if (!pollList.size()) {
				Logger::Locked(net.logger)->Info << "(PollingLoop) No registered channels, disabling polling" << endl;
				polling = false;
				continue;
			}
		}

		try {
			Logger::Locked(net.logger)->Debug << "(PollingLoop) Starting poll" << endl;
			SV3NetChannel::PollStatusFireEvents(pollList, pollForGPS, true, 1, 15000);
		}
		catch(exception& ex) {
			Logger::Locked(net.logger)->Warning << "(PollingLoop) Caught " << typeid(ex).name() << " during Poll(): " << ex.what() << endl;
			{ ILockableConceptLock lock(firingMutex); pollingStatusSignal(*this, false); }
			while(!threadExited && wakeSem.TimedWait(15000)) { }
		}
	}

	Logger::Locked(net.logger)->Info << "(PollingLoop) Thread exiting" << endl;
}

}
}
