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

#include "LqrSMACCmdOutputFormatter.h"
#include "LqrSMACByteArray.h"
#include "nsextend.h"
#include <sstream>
#include <stdexcept>

using namespace std;
using namespace boost::posix_time;

namespace LiquidRobotics {
namespace SMACFramework {

string CmdOutputFormatter::childTag = "child";
string CmdOutputFormatter::childrenTag = "children";
string CmdOutputFormatter::commandTag = "cmd";
string CmdOutputFormatter::createdTimeTag = "create-time";
string CmdOutputFormatter::endTimeTag = "end-time";
string CmdOutputFormatter::outputTag = "output";
string CmdOutputFormatter::startTimeTag = "start-time";
string CmdOutputFormatter::exceptionNameTag = "exception";
string CmdOutputFormatter::exceptionTextTag = "errormsg";
string CmdOutputFormatter::resultTag = "result";
string CmdOutputFormatter::succeededTag = "succeeded";
string CmdOutputFormatter::valueTag = "value";

CmdOutputFormatter::CmdOutputFormatter()
: outputterSink(*this)
, output(outputterSink)
, started(false)
, completed(false)
, caughtException(false)
, succeeded(false)
, parent(NULL)
{
	CreateTime().SetNow();
}

CmdOutputFormatter::~CmdOutputFormatter()
{
}

void CmdOutputFormatter::AppendOutput(const ByteArray& data)
{
	CheckState(true, true);
	AppendOutput(&data.front(), data.size());
}

void CmdOutputFormatter::AppendOutput(const string& data)
{
	CheckState(true, true);
	AppendOutput(data.data(), data.length());
}

void CmdOutputFormatter::AppendResult(const ByteArray& data)
{
	CheckState(true, true);
	AppendResult(&data.front(), data.size());
}

void CmdOutputFormatter::AppendResult(const string& data)
{
	CheckState(true, true);
	AppendResult(data.data(), data.length());
}

void CmdOutputFormatter::FlagCompleted(CORBA::Exception& ex)
{
	CheckState(false, true);
	EndTime().SetNow();
	completed = true;
	succeeded = false;
	caughtException = true;
	exceptionName = ex._name();
	exceptionText = DecodeCORBAException(ex);
}

void CmdOutputFormatter::FlagCompleted(std::exception& ex)
{
	CheckState(false, true);
	EndTime().SetNow();
	completed = true;
	succeeded = false;
	caughtException = true;
	exceptionName = typeid(ex).name();
	exceptionText = ex.what();
}

void CmdOutputFormatter::FlagCompleted(bool success)
{
	CheckState(false, true);
	EndTime().SetNow();
	completed = true;
	succeeded = success;
	caughtException = false;
	exceptionName.clear();
	exceptionText.clear();
}

void CmdOutputFormatter::FlagStarted()
{
	if (started)
		throw runtime_error("Command already started");

	CheckState(false, true);
	StartTime().SetNow();
	started = true;
}

bool CmdOutputFormatter::FlaggedCompleted() const
{
	return completed;
}

bool CmdOutputFormatter::FlaggedStarted() const
{
	return started;
}

void CmdOutputFormatter::GetChildren(PtrArray& _children) const
{
	MutexLock lock(const_cast<Mutex&>(childrenMutex));
	_children.clear();
	_children.assign(children.begin(), children.end());
}

CmdOutputFormatter::string CmdOutputFormatter::GetCommandName() const
{
	string result;
	try { GetTag(commandTag); } catch(...) { }
	return result;
}

CmdOutputFormatterBase* CmdOutputFormatter::GetParent() const
{
	return parent;
}

void CmdOutputFormatter::NewChild(Ptr newChild)
{
	if (!newChild)
		throw invalid_argument("Cannot insert null child formatter into array");

	MutexLock lock(childrenMutex);
	bool existing = false;

	for (PtrArray::const_iterator cit = children.begin(); !existing && (cit != children.end()); ++cit)
		if (cit->get() == newChild.get())
			existing = true;

	if (!existing)
		children.push_back(newChild);
}

void CmdOutputFormatter::NewChild(CmdOutputFormatterBase* newChild)
{
	Ptr ptr;
	ptr.reset(newChild);

	NewChild(ptr);
}

void CmdOutputFormatter::SetCommandName(const string& newValue)
{
	AddAttribute(commandTag, newValue);
}

void CmdOutputFormatter::SetParent(CmdOutputFormatterBase* newValue)
{
	parent = newValue;
}

string CmdOutputFormatter::ToString(unsigned nesting, unsigned maxLength) const
{
	stringstream stm;
	ToStream(stm, nesting, maxLength);

	string result = stm.str();
	return result;
}

void CmdOutputFormatter::CheckState(bool chkStart, bool chkEnd)
{
	if (chkStart && !started)
		throw runtime_error("Command has not started yet");

	if (chkEnd && completed)
		throw runtime_error("Command has completed already");
}


CmdOutputFormatter::TimestampValue& CmdOutputFormatter::CreateTime()
{
	return createTime;
}

const CmdOutputFormatter::TimestampValue& CmdOutputFormatter::CreateTime() const
{
	return createTime;
}

CmdOutputFormatter::TimestampValue& CmdOutputFormatter::EndTime()
{
	return endTime;
}

const CmdOutputFormatter::TimestampValue& CmdOutputFormatter::EndTime() const
{
	return endTime;
}

CmdOutputFormatter::TimestampValue& CmdOutputFormatter::StartTime()
{
	return startTime;
}

const CmdOutputFormatter::TimestampValue& CmdOutputFormatter::StartTime() const
{
	return startTime;
}

CmdOutputFormatter::OutputterStream& CmdOutputFormatter::Output()
{
	return output;
}

const CmdOutputFormatter::OutputterStream& CmdOutputFormatter::Output() const
{
	return output;
}

}
}
