//------------------------------------------------------------------------------
// <copyright file="LqrSMACCmdOutputFormatter.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 CmdOutputFormatter virtual class, which captures the
//   operations implemented by formatters of command output.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#ifndef __LqrSMACCmdOuptutFormatter_h__
#define __LqrSMACCmdOuptutFormatter_h__

#include "LqrSMACSharedLib.h"
#include "LqrSMACIdl.h"
#include <ostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/recursive_mutex.hpp>

namespace LiquidRobotics {
namespace SMACFramework {

class ByteArray;

class SHARED_LIB_PUBLIC CmdOutputFormatterBase : boost::noncopyable
{
public:

	// StreamSink
	//
	// This template class is part of the boost pattern for
	// output streaming; it provides a standardized interface
	// to the container (T)
	template<typename T, typename C = char>
	class StreamSink
	{
	public:

		typedef C char_type;
		typedef boost::iostreams::sink_tag category;

		StreamSink(T& container) : container_(container) { }

		std::streamsize write(const char_type* s, std::streamsize n)
		{
			container_.AppendOutput(s, n);
			return n;
		}

		T& container() { return container_; }

	private:

		T& container_;
	};

	typedef StreamSink<CmdOutputFormatterBase> OutputterSink;			// concrete StreamSink instance used to store output
	typedef boost::iostreams::stream<OutputterSink> OutputterStream;	// a stream instance that outputs here

	class TimestampValue
	{
	public:

		typedef std::string string;
		typedef boost::posix_time::ptime Timestamp;

		TimestampValue(bool _localTime = true);
		TimestampValue(const TimestampValue& rhs);

		TimestampValue& operator=(const TimestampValue& rhs);
		bool operator==(const TimestampValue& rhs) const;

		const Timestamp& Get() const;
		void Get(timeval& tv) const;
		void Set(const Timestamp& newValue);
		void Set(const timeval& newValue);
		void SetNow();
		string ToString() const;
		bool WasSet() const;

	private:

		// determines whether the values are considered in local time
		const bool localTime;

		// the timestamp that was set, initialized to "not a date/time"
		Timestamp timeStamp;
	};

	typedef boost::posix_time::time_duration Duration;
	typedef std::ostream OutputStream;
	typedef boost::shared_ptr<CmdOutputFormatterBase> Ptr;
	typedef std::vector<Ptr> PtrArray;
	typedef std::string string;

	class Runtime
	{
	public:

		Runtime(Ptr _formatter)
		: formatter(_formatter)
		{
			formatter->FlagStarted();
		}

		virtual ~Runtime()
		{
			if (!formatter->FlaggedCompleted())
				formatter->FlagCompleted();
		}

	private:

		Ptr formatter;
	};

	virtual void AddAttribute(const string& attributeName, const string& attributeValue) = 0;
	virtual void AppendOutput(const void* pStart, unsigned len) = 0;
	virtual void AppendResult(const void* pStart, unsigned len) = 0;
	virtual void AppendTag(const string& tagName, const void* pStart, unsigned len) = 0;
	virtual string GetAttribute(const string& attributeName) const = 0;
	virtual string GetOutput() const = 0;
	virtual string GetTag(const string& tagName) const = 0;
	virtual Ptr NewChild() = 0;
	virtual OutputStream& ToStream(OutputStream& stm, unsigned nesting = 0, unsigned maxLength = ~0) const = 0;

	virtual void AppendOutput(const ByteArray& data) = 0;
	virtual void AppendOutput(const string& data) = 0;
	virtual void AppendResult(const ByteArray& data) = 0;
	virtual void AppendResult(const string& data) = 0;
	virtual TimestampValue& CreateTime() = 0;
	virtual const TimestampValue& CreateTime() const = 0;
	virtual TimestampValue& EndTime() = 0;
	virtual const TimestampValue& EndTime() const = 0;
	virtual void FlagCompleted(CORBA::Exception& ex) = 0;
	virtual void FlagCompleted(std::exception& ex) = 0;
	virtual void FlagCompleted(bool success = true) = 0;
	virtual void FlagStarted() = 0;
	virtual bool FlaggedCompleted() const = 0;
	virtual bool FlaggedStarted() const = 0;
	virtual void GetChildren(PtrArray& _children) const = 0;
	virtual string GetCommandName() const = 0;
	virtual CmdOutputFormatterBase* GetParent() const = 0;
	virtual void NewChild(Ptr newChild) = 0;
	virtual void NewChild(CmdOutputFormatterBase* newChild) = 0;
	virtual OutputterStream& Output() = 0;
	virtual const OutputterStream& Output() const = 0;
	virtual void SetCommandName(const string& newValue) = 0;
	virtual void SetParent(CmdOutputFormatterBase* newValue) = 0;
	virtual TimestampValue& StartTime() = 0;
	virtual const TimestampValue& StartTime() const = 0;
	virtual string ToString(unsigned nesting = 0, unsigned maxLength = ~0) const = 0;
};

class SHARED_LIB_PUBLIC CmdOutputFormatter : public CmdOutputFormatterBase
{
public:

	typedef CmdOutputFormatterBase Base;

	CmdOutputFormatter();
	virtual ~CmdOutputFormatter();

	virtual void AppendOutput(const void* pStart, unsigned len) = 0;
	virtual void AppendResult(const void* pStart, unsigned len) = 0;

	virtual void AppendOutput(const ByteArray& data);
	virtual void AppendOutput(const string& data);
	virtual void AppendResult(const ByteArray& data);
	virtual void AppendResult(const string& data);
	virtual TimestampValue& CreateTime();
	virtual const TimestampValue& CreateTime() const;
	virtual TimestampValue& EndTime();
	virtual const TimestampValue& EndTime() const;
	virtual void FlagCompleted(CORBA::Exception& ex);
	virtual void FlagCompleted(std::exception& ex);
	virtual void FlagCompleted(bool success = true);
	virtual void FlagStarted();
	virtual bool FlaggedCompleted() const;
	virtual bool FlaggedStarted() const;
	virtual void GetChildren(PtrArray& _children) const;
	virtual string GetCommandName() const;
	virtual CmdOutputFormatterBase* GetParent() const;
	virtual void NewChild(Ptr newChild);
	virtual void NewChild(CmdOutputFormatterBase* newChild);
	virtual OutputterStream& Output();
	virtual const OutputterStream& Output() const;
	virtual void SetCommandName(const string& newValue);
	virtual void SetParent(CmdOutputFormatterBase* newValue);
	virtual TimestampValue& StartTime();
	virtual const TimestampValue& StartTime() const;
	virtual string ToString(unsigned nesting = 0, unsigned maxLength = ~0) const;

protected:

	OutputterSink outputterSink;			// Sink for streaming interface
	TimestampValue createTime;				// Time when command was created (initialized automatically)
	TimestampValue startTime;				// Time when command started executing
	TimestampValue endTime;					// Time when command completed
	OutputterStream output;					// Output stream variable

	typedef boost::recursive_mutex Mutex;
	typedef boost::unique_lock<Mutex> MutexLock;

	void CheckState(bool chkStart, bool chkEnd);

	bool started;					// true if FlagStarted is
	bool completed;
	bool caughtException;
	bool succeeded;
	string commandName;
	string exceptionName;
	string exceptionText;
	CmdOutputFormatterBase* parent;
	Mutex childrenMutex;
	PtrArray children;
	static string childTag;
	static string childrenTag;
	static string commandTag;
	static string createdTimeTag;
	static string endTimeTag;
	static string exceptionNameTag;
	static string exceptionTextTag;
	static string outputTag;
	static string resultTag;
	static string succeededTag;
	static string startTimeTag;
	static string valueTag;
};

CmdOutputFormatter::OutputStream& operator<<(CmdOutputFormatter::OutputStream& stm, const CmdOutputFormatter& formatter);

}
}

#endif
