//----------------------------------------------------------------------------------
// <copyright file="SV3NetInterface.h" 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>
// 	Declaration of SV3NetInterface, which implements a web services interface client
//  for each sensor server.
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#ifndef __PAYLOADINTERFACELIB_SV3NetInterface_H__
#define __PAYLOADINTERFACELIB_SV3NetInterface_H__

#include "LqrSMACByteArray.h"
#include "LqrSMACLockableConcept.h"
#include "LqrSMACLogger.h"
#include "LqrSMACScopedConnection.h"
#include "LqrSMACSignalSubscriber.h"
#include "LqrSMACSharedLib.h"
#include "RegulusREST.h"
#include "SV3NetChannel.h"
#include <stdint.h>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>

namespace LiquidRobotics {
namespace PayloadInterface {

class SHARED_LIB_PUBLIC SV3NetInterface : public boost::noncopyable
{
public:

	LOCKABLE_CONCEPT_TYPEDEFS();
	SCOPED_CONNECTION_TYPEDEFS();
	typedef LiquidRobotics::SMACFramework::ByteArray ByteArray;
	typedef LiquidRobotics::SMACFramework::Logger Logger;
	typedef std::string string;
	typedef boost::shared_ptr<SV3NetInterface> Ptr;

	SV3NetInterface(const SV3NetInterfaceSettings& _settings, Logger& _logger);
	virtual ~SV3NetInterface();

	void Initialize();
	void Shutdown();

	string ConformingReferenceToTransactionReference(uint32_t conformingReference);
	SV3NetChannel::Ptr FindChannel(uint16_t boardAddress);
	void GetChannels(SV3NetChannel::PtrArray& result);
	void NoteClient(uint16_t boardAddress, const string& serverName, const string& description, const string& descriptionJSON);
	void NoteGPSRequired(bool required);
	bool RemoveTransactionReference(const string& transactionReference, bool throwOnError = true);
	uint32_t TransactionReferenceToConformingReference(const string& transactionReference, bool addIfNotFound = true);

	typedef boost::signals2::signal<
			void (SV3NetInterface& sender, uint16_t boardAddress, const string& transactionReference, uint32_t conformingReference,
					bool returnFile, const string& cmdLine)
	> RemoteCommandSignal;
private:
	LockableConceptMutex firingMutex;
	RemoteCommandSignal remoteCommandSignal;
public:
	LiquidRobotics::SMACFramework::SignalSubscriber<RemoteCommandSignal> remoteCommandSubscriber;

	typedef boost::signals2::signal<
			void (SV3NetInterface& sender, SV3NetChannel& channel)
	> ShutdownSignal;
private:
	ShutdownSignal shutdownSignal;
public:
	LiquidRobotics::SMACFramework::SignalSubscriber<ShutdownSignal> shutdownSubscriber;

	typedef boost::signals2::signal<
			void (SV3NetInterface& sender, bool commStatus)
	> CommStatusSignal;
private:
	CommStatusSignal commstatusSignal;
public:
	LiquidRobotics::SMACFramework::SignalSubscriber<CommStatusSignal> commstatusSubscriber;

	typedef boost::signals2::signal<
			void (SV3NetInterface& sender, uint16_t boardAddress, time_t timestamp, double latitude, double longitude)
	> GPSSignal;
private:
	GPSSignal gpsSignal;
public:
	LiquidRobotics::SMACFramework::SignalSubscriber<GPSSignal> gpsSubscriber;

private:

	struct ChannelInfo {
		~ChannelInfo();
		SV3NetChannel::Ptr channel;
		SV3NetChannel::ScopedConnectionPtr commandSub;
		SV3NetChannel::ScopedConnectionPtr gpsSub;
		SV3NetChannel::ScopedConnectionPtr shutdownSub;
	};
//gpsSignalSubscriber
	typedef boost::shared_ptr<ChannelInfo> ChannelInfoPtr;
	typedef std::vector<ChannelInfoPtr> ChannelArray;
	typedef boost::shared_ptr<ChannelArray> ChannelArrayPtr;
	typedef LiquidRobotics::SMACFramework::Semaphore Semaphore;

	struct RegistrationLoop {

		RegistrationLoop(SV3NetInterface& net);
		bool IsCommunicating();
		void NoteCommLoss();
		void NoteNewRegistration(ChannelInfo& ci);
		void Start();
		void Stop();

		typedef boost::signals2::signal<void (RegistrationLoop& sender)> AllRegisteredSignal;
	private:
		LockableConceptMutex firingMutex;
		AllRegisteredSignal allRegSignal;
	public:
		LiquidRobotics::SMACFramework::SignalSubscriber<AllRegisteredSignal> allRegSignalSub;

	private:

		typedef boost::shared_ptr<boost::thread> ThreadPtr;
		bool AnyUnregistered();
		void Loop();
		bool RegisterAll(bool purge = false);
		void AnnounceAllRegistered();
		void MarkAllUnregistered();

		SV3NetInterface& net;
		LockableConceptMutex& mutex;
		Semaphore wakeSem;
		bool newReg;
		bool commLost;
		bool threadExited;
		ThreadPtr threadp;
	};

	typedef boost::shared_ptr<RegistrationLoop> RegistrationLoopPtr;
	friend struct RegistrationLoop;

	struct PollingLoop {

		PollingLoop(SV3NetInterface& net);
		bool IsPolling();
		void NoteAllRegistered();
		void NoteGPSRequired(bool required);
		void Start();
		void Stop();

		typedef boost::signals2::signal<void (PollingLoop& sender, bool goodPoll)> PollingStatusSignal;
	private:
		LockableConceptMutex firingMutex;
		PollingStatusSignal pollingStatusSignal;
	public:
		LiquidRobotics::SMACFramework::SignalSubscriber<PollingStatusSignal> pollingStatusSignalSub;

	private:

		typedef boost::shared_ptr<boost::thread> ThreadPtr;
		void Loop();

		SV3NetInterface& net;
		LockableConceptMutex& mutex;
		Semaphore wakeSem;
		double upTimeAtPollingStatus;
		bool polling;
		bool pollForGPS;
		bool threadExited;
		ThreadPtr threadp;
	};

	typedef boost::shared_ptr<PollingLoop> PollingLoopPtr;
	friend struct PollingLoop;
	typedef std::map<std::string,uint32_t> StringToUInt32Map;
	typedef std::map<uint32_t,std::string> UInt32ToStringMap;

	ChannelInfoPtr FindChannelInfo(uint16_t boardAddress, bool throwOnError = true);
	void OnHostGPS(SV3NetChannel& sender, time_t timestamp, double latitude, double longitude);
	void OnPLStatus(PollingLoop& sender, bool goodPoll);
	void OnRemoteCommand(SV3NetChannel& sender, uint16_t boardAddress, const string& transactionReference, bool returnFile, const string& cmdLine);
	void OnRLAllRegistered(RegistrationLoop& sender);
	void OnShutdown(SV3NetChannel& sender);

	LockableConceptMutex mutex;
	LockableConceptMutex localMutex;
	const SV3NetInterfaceSettings& settings;
	Logger& logger;
	uint32_t nextConformingID;
	bool goodComm;
	bool gpsRequired;
	StringToUInt32Map mapTransactionIDToConformingID;
	UInt32ToStringMap mapConformingIDToTransactionID;
	ChannelArray channels;
	RegistrationLoopPtr regLoop;
	PollingLoopPtr pollingLoop;
	ScopedConnectionPtr plStatusSubPtr;
	ScopedConnectionPtr rlAllRegSubPtr;
};

}
}


#endif
