//-------------------------------------------------------------------------------
// <copyright file="LqrSMACBasicCmdOutputFormatter.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>
// </summary>
//
// <owner></owner>
//-------------------------------------------------------------------------------

#include "LqrSMACBasicCmdOutputFormatter.h"
#include "LqrSMACBooleanParser.h"
#include <boost/algorithm/string.hpp>

using namespace std;

namespace LiquidRobotics {
namespace SMACFramework {

BasicCmdOutputFormatter::BasicCmdOutputFormatter(const string& _options)
: options(_options)
{
}

BasicCmdOutputFormatter::~BasicCmdOutputFormatter()
{
}

void BasicCmdOutputFormatter::AddAttribute(const string& attributeName, const string& attributeValue)
{
	MutexLock lock(collectionsMutex);
	if (!attributeName.length()) throw "Cannot add an attribute with an empty name";
	attributes[attributeName] = attributeValue;
}

void BasicCmdOutputFormatter::AppendOutput(const void* pStart, unsigned len)
{
	AppendTag(outputTag, pStart, len);
}

void BasicCmdOutputFormatter::AppendResult(const void* pStart, unsigned len)
{
	results.push_back(string((const char*) pStart, len));
}


void BasicCmdOutputFormatter::AppendTag(const string& tagName, const void* pStart, unsigned len)
{
	MutexLock lock(collectionsMutex);
	StringStreamMap::iterator it = tags.find(tagName);
	streamstreamptr ssp;

	if (it == tags.end())
	{
		ssp.reset(new stringstream);
		tags[tagName] = ssp;
	}
	else
		ssp = it->second;

	(*ssp).write((const char*) pStart, len);
}

string BasicCmdOutputFormatter::GetAttribute(const string& attributeName) const
{
	MutexLock lock(const_cast<Mutex&>(collectionsMutex));
	StringStringMap::const_iterator ci = attributes.find(attributeName);
	string attributeValue;

	if (ci != attributes.end())
		attributeValue = ci->second;

	return attributeValue;
}

string BasicCmdOutputFormatter::GetOutput() const
{
	return GetTag(outputTag);
}

string BasicCmdOutputFormatter::GetTag(const string& tagName) const
{
	MutexLock lock(const_cast<Mutex&>(collectionsMutex));
	StringStreamMap::const_iterator ci = tags.find(tagName);
	string tagValue;

	if (ci != tags.end())
		tagValue = ci->second->str();

	return tagValue;
}

BasicCmdOutputFormatter::Ptr BasicCmdOutputFormatter::NewChild()
{
	Ptr result;
	result.reset(new BasicCmdOutputFormatter(options));
	result->SetParent(this);
	Base::NewChild(result);
	return result;
}

BasicCmdOutputFormatter::OutputStream& BasicCmdOutputFormatter::ToStream(OutputStream& stm, unsigned nesting, unsigned maxLength) const
{
	WriteNode(stm, nesting, maxLength);
	if (!stm.tellp()) stm.put(' ');
	return stm;
}

void BasicCmdOutputFormatter::WriteAttribute(OutputStream& stm, const string& attributeName, const string& attributeValue) const
{
	//stm << attributeName << ":" << attributeValue << endl;
	if (boost::equals(attributeName, BasicCmdOutputFormatter::exceptionNameTag)
	 || boost::equals(attributeName, BasicCmdOutputFormatter::exceptionTextTag)
	 || boost::equals(attributeName, BasicCmdOutputFormatter::succeededTag))
	{
		stm << "[" << attributeName << ":" << attributeValue << "]" << endl;
	}
}

void BasicCmdOutputFormatter::WriteTag(OutputStream& stm, const string& attributeName, const string& attributeValue) const
{
	//stm << attributeName << ":" << attributeValue << endl;
	if (boost::equals(attributeName,BasicCmdOutputFormatter::outputTag))
		stm << attributeValue;
}

void BasicCmdOutputFormatter::WriteNode(OutputStream& stm, unsigned nesting, unsigned maxLength) const
{
	MutexLock lock(const_cast<Mutex&>(collectionsMutex));

	if (commandName.length())
		WriteAttribute(stm, commandTag, commandName);

	if (!succeeded)
		WriteAttribute(stm, succeededTag, BooleanToString(false));

	if (caughtException)
	{
		WriteAttribute(stm, exceptionNameTag, exceptionName);
		WriteAttribute(stm, exceptionTextTag, exceptionText);
	}

	if (CreateTime().WasSet())
	{
		string tempStr = CreateTime().ToString();
		WriteAttribute(stm, createdTimeTag, tempStr);
	}

	if (StartTime().WasSet())
	{
		string tempStr = StartTime().ToString();
		WriteAttribute(stm, startTimeTag, tempStr);
	}

	if (EndTime().WasSet())
	{
		string tempStr = EndTime().ToString();
		WriteAttribute(stm, endTimeTag, tempStr);
	}

	for (StringStringMap::const_iterator ci = attributes.begin(); ci != attributes.end(); ++ci)
	{
		string attrName = ci->first;
		string attrVal = ci->second;

		WriteAttribute(stm, attrName, attrVal);
	}

	for (StringStreamMap::const_iterator ci = tags.begin(); ci != tags.end(); ++ci)
	{
		string nodeName = ci->first;
		string nodeValue = ci->second->str();

		WriteTag(stm, nodeName, nodeValue);
	}

	for (StringList::const_iterator ci = results.begin(); ci != results.end(); ++ci)
	{
		string nodeName = resultTag;
		string nodeValue = *ci;

		WriteTag(stm, nodeName, nodeValue);
	}

	lock.unlock();

	MutexLock lock2(const_cast<Mutex&>(childrenMutex));

	if (children.size())
	{
		for (PtrArray::const_iterator ci = children.begin(); ci != children.end(); ++ci)
		{
			Ptr ptr = *ci;

			try
			{
				const BasicCmdOutputFormatter& child = dynamic_cast<BasicCmdOutputFormatter&>(*ptr);

				child.WriteNode(stm, nesting+1, maxLength);
			}
			catch(bad_cast& bcx)
			{
				stringstream childStm;
				ptr->ToStream(childStm, nesting+1, maxLength);

				string addition = childStm.str();
				stm << addition;
			}
		}
	}
}

}
}
