//------------------------------------------------------------------------------
// <copyright file="ProxySC.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 ProxySC, which encapsulates the work of maintaining a storage
//  controller for a particular destination address and then forwarding calls to
//  the target CORBA implementation.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#ifndef __PAYLOADINTERFACELIB_PROXYSC_H__
#define __PAYLOADINTERFACELIB_PROXYSC_H__

#include "LqrSMACByteArray.h"
#include "LqrSMACIObjectBroker.h"
#include "LqrSMACLogger.h"
#include "LqrSMACSharedLib.h"
#include "EventPropertyValue.h"
#include "PayloadManagerClient.h"
#include "StorageController.h"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/utility.hpp>
#include <list>
#include <stdint.h>
#include <string>

namespace LiquidRobotics {
namespace EventLib {
	class DataSetEvent;
	class SendCommandCompleteEvent;
}
}

namespace LiquidRobotics {
namespace SMACFramework {
	class TempFile;
}
}

namespace LiquidRobotics {
namespace PayloadInterface {

class SHARED_LIB_PUBLIC ProxySC : public boost::noncopyable
{
public:

	typedef unsigned short AddressType;
	typedef LiquidRobotics::SMACFramework::ByteArray ByteArray;
	typedef LiquidRobotics::SMACFramework::ByteArrayPtr ByteArrayPtr;
	typedef LiquidRobotics::SMACFramework::ByteArray::byte byte;
	typedef uint16_t BoardAddressType;
	typedef byte BoardIDType;
	typedef CORBA::Any CORBAAny;
	typedef CORBA::Any_var CORBAAny_var;
	typedef LiquidRobotics::EventLib::DataSetEvent DataSetEvent;
	typedef LiquidRobotics::EventLib::EventPropertyValue EventPropertyValue;
	typedef LiquidRobotics::SMACFramework::IObjectBroker IObjectBroker;
	typedef LiquidRobotics::SMACFramework::Logger Logger;
	typedef LiquidRobotics::EventLib::PayloadManagerClient PayloadManagerClient;
	typedef LiquidRobotics::EventLib::SendCommandCompleteEvent SendCommandCompleteEvent;
	typedef byte TaskIDType;
	typedef boost::shared_ptr<ProxySC> Ptr;
	typedef std::string string;
	typedef LiquidRobotics::SMACFramework::TempFile TempFile;
	typedef boost::shared_ptr<TempFile> TempFilePtr;
	typedef boost::signals2::signal<void (ProxySC& sender, const ByteArray& data, long transNumber, unsigned destID, bool complete, unsigned resultNum, bool success)> CompleteSignalType;
	typedef CompleteSignalType::slot_type CompleteSignalSlotType;
	typedef boost::signals2::signal<void (ProxySC& sender, const string& actualFileName, string& remoteName, unsigned destID)> NewFileSignalType;
	typedef NewFileSignalType::slot_type NewFileSlotType;

	ProxySC(BoardIDType _boardID, TaskIDType _taskID, const StorageController::Config& _scConfig, IObjectBroker& _broker,
			PayloadManagerClient& _pmClient, Logger& _logger, size_t _maxFileSize);
	virtual ~ProxySC();

	void ExecuteCommand(const ByteArray& command, long transNumber, unsigned destID, bool ascii, bool retFile, bool wantsAck);
	void ExecuteFileCompletion(StorageController::FileIDType fileID);
	AddressType GetAddress() const;
	BoardIDType GetBoardID() const;
	StorageController& GetSC();
	TaskIDType GetTaskID() const;
	void ProcessCommandCompletion(const SendCommandCompleteEvent& completion);
	void ProcessNewFileDataSet(const DataSetEvent& dataSet, BoardAddressType destinationID);
	void Subscribe(const CompleteSignalSlotType& slot); // assumes that connection will last until ProxySC is destroyed
	void Subscribe(const NewFileSlotType& slot); 		// assumes that connection will last until ProxySC is destroyed

	static string GetIORForTarget(AddressType address, IObjectBroker& broker, PayloadManagerClient& pmClient);
	static bool HaveCommandableTarget(AddressType address, IObjectBroker& broker, PayloadManagerClient& pmClient);
	static bool HaveCommandableTarget(AddressType address, bool& implementsSC, IObjectBroker& broker, PayloadManagerClient& pmClient);

private:

	class TransactionInfo
	{
	public:

		typedef std::string string;

		TransactionInfo(bool _fileOriented, string _transID, unsigned _destID, StorageController::FileIDType _fileID, bool _retFile = false, long _transNumber = 0);
		TransactionInfo(const TransactionInfo& other);

		bool FileOriented() const;
		void GetContent(ByteArray& result);
		const string& TransID() const;
		long TransNumber() const;
		unsigned DestID() const;
		StorageController::FileIDType FileID() const;
		unsigned ResultNum(unsigned offset);
		bool ReturnFile() const;
		void Write(const ByteArray& data);

	private:

		const bool fileOriented;
		const string transID;
		const long transNumber;
		const unsigned destID;
		const StorageController::FileIDType fileID;
		const bool retFile;
		TempFilePtr tempFile;
		unsigned resultNum;
	};

	typedef boost::shared_ptr<TransactionInfo> TransactionInfoPtr;
	typedef boost::signals2::scoped_connection ScopedConnection;
	typedef boost::shared_ptr<ScopedConnection> ScopedConnectionPtr;
	typedef std::list<ScopedConnectionPtr> ScopedConnectionList;
	typedef std::map<string, TransactionInfoPtr> TransactionMap;

	CORBAAny_var GetFileContent(StorageController::FileIDType fileID);
	bool StartRemoteCommand(const CORBAAny& command, CORBAAny_var& result, string& ext, TransactionInfoPtr transInfo, bool saveResponse = true);
	string StoreResponseFile(StorageController::FileIDType cmdFileID, const ByteArray& responseContent, const string& ext);

	ByteArrayPtr AnyToBytes(const CORBAAny& anyVal) const;
	ByteArrayPtr AnyToBytes(const EventPropertyValue& anyVal) const;
	void BytesToAny(const ByteArray& bytes, CORBAAny& result) const;
	void ClearTransInfo(const string& transID);
	TransactionInfoPtr GetTransInfo(const string& transID);
	void HandleCommandCompletion(TransactionInfo& transInfo, const ByteArray& bytes, bool complete, bool success);
	string MakeTransactionID();
	void StoreTransInfo(TransactionInfoPtr info);
	void StringToAny(const string& str, CORBAAny& result) const;

	static AddressType GetAddress(BoardIDType bid, TaskIDType tid);
	static StorageController::Config Reconfig(const StorageController::Config& _scConfig, AddressType address, IObjectBroker& broker);

	const BoardIDType boardID;
	const TaskIDType taskID;
	const size_t maxFileSize;
	StorageController::Config scConfig;
	IObjectBroker& broker;
	PayloadManagerClient& pmClient;
	Logger& logger;
	StorageController sc;
	string cmdSrcID;
	unsigned long transID;
	CompleteSignalType completeSignal;
	NewFileSignalType newFileSignal;
	ScopedConnectionList connectionList;  // for new file subscriptions
	ScopedConnectionList connectionListComplete; // for complete subscriptions
	TransactionMap transactionMap;
};

}
}

#endif
