//------------------------------------------------------------------------------
// <copyright file="TelemetryFieldLibrary.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 TelemetryFieldLibrary, a dictionary of string keys and
//   EPVFormatConfig shared pointer values.
// </summary>
//
// <owner>Mike Cookson</owner>
//------------------------------------------------------------------------------

#include "TelemetryFieldLibrary.h"
#include "EPVFormatConfig.h"
#include <sstream>
#include <utility>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/foreach.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/utility.hpp>

using namespace std;
using namespace LiquidRobotics::SMACFramework;
using namespace LiquidRobotics::SMACFramework::XmlDOM;

namespace LiquidRobotics {
namespace PayloadInterface {

TelemetryFieldLibrary::TelemetryFieldLibrary(IObjectBroker& _broker)
: broker(_broker)
{
}

TelemetryFieldLibrary::TelemetryFieldLibrary(const TelemetryFieldLibrary& other)
: broker(other.broker)
{
	BOOST_FOREACH(const LibType::value_type& item, other.lib)
		lib[item.first].reset(new EPVFormatConfig(*item.second));
}

TelemetryFieldLibrary& TelemetryFieldLibrary::operator=(const TelemetryFieldLibrary& other)
{
	if (this != &other)
	{
		lib.clear();

		BOOST_FOREACH(const LibType::value_type& item, other.lib)
			lib[item.first].reset(new EPVFormatConfig(*item.second));
	}

	return *this;
}

bool TelemetryFieldLibrary::operator==(const TelemetryFieldLibrary& other) const
{
	LibType::const_iterator i1 = lib.begin();
	LibType::const_iterator i2 = other.lib.begin();
	bool end1, end2, equal;

	while(end1 = (i1 == lib.end()),
		  end2 = (i2 == other.lib.end()),
		  equal = (end1 == end2) && (!end1 || (!i1->first.compare(i2->first) && (*i1->second == *i2->second))),
		  equal && !end1)
	{
		++end1;
		++end2;
	}

	return equal;
}

void TelemetryFieldLibrary::Add(const string& key, const EPVFormatConfig& val, bool overwrite)
{
	LibType::iterator it = lib.find(key);

	if ((it != lib.end()) && !overwrite)
	{
		stringstream msg;
		msg << "TelemetryFieldLibrary::Add: key '" << key << "' exists";
		throw invalid_argument(msg.str());
	}

	EPVFormatConfigPtr ptr;
	ptr.reset(new EPVFormatConfig(val));
	lib[key] = ptr;
}

bool TelemetryFieldLibrary::ContainsKey(const string& key) const
{
	return lib.find(key) != lib.end();
}

void TelemetryFieldLibrary::Load(xml_iarchive& arch)
{
	size_t count;
	arch >> BOOST_SERIALIZATION_NVP(count);

	lib.clear();

	while(count--)
	{
		string key;
		EPVFormatConfigPtr val;

		val.reset(new EPVFormatConfig(broker));

		std::pair<string&,EPVFormatConfig&> item(key,*val);
		arch >> BOOST_SERIALIZATION_NVP(item);

		lib[key] = val;
	}
}

const TelemetryFieldLibrary::EPVFormatConfig& TelemetryFieldLibrary::Lookup(const string& key) const
{
	LibType::const_iterator it = lib.find(key);

	if (it == lib.end())
	{
		stringstream msg;
		msg << "TelemetryFieldLibrary::Lookup no such key '" << key << "'";
		throw invalid_argument(msg.str());
	}

	return *it->second;
}

void TelemetryFieldLibrary::Remove(const string& key)
{
	LibType::iterator it = lib.find(key);

	if (it == lib.end())
	{
		stringstream msg;
		msg << "TelemetryFieldLibrary::Remove no such key '" << key << "'";
		throw invalid_argument(msg.str());
	}

	lib.erase(it);
}

void TelemetryFieldLibrary::Save(xml_oarchive& arch) const
{
	size_t count = lib.size();
	arch << BOOST_SERIALIZATION_NVP(count);

	for (LibType::const_iterator it = lib.begin(); it != lib.end(); ++it)
	{
		std::pair<const string&,const EPVFormatConfig&> item(it->first, *it->second);
		arch << BOOST_SERIALIZATION_NVP(item);
	}
}

void TelemetryFieldLibrary::Load(XmlNode& node, const XmlNVPBase& nvp)
{
	lib.clear();

	XmlNode libNode = GetChildNode("lib", node);

	for (XmlNode elementNode = libNode.first_child(); elementNode; elementNode = elementNode.next_sibling())
	{
		if (!boost::iequals(StandardElementTag, elementNode.name()))
			throw runtime_error("Unexpected tag found in TelemetryFieldLibrary map");

		string key;
		EPVFormatConfigPtr val;

		elementNode >> XmlNVP<string>("key", key);

		XmlNode valueNode = GetChildNode("value", elementNode);
		XmlAttribute nilAttr = GetAttribute("nil", valueNode, false);

		if (!nilAttr || !nilAttr.as_bool())
		{
			val.reset(new EPVFormatConfig(broker));
			LoadFromNode(valueNode, XmlNVPBase("value"), *val);
		}

		lib[key] = val;
	}
}

void TelemetryFieldLibrary::Save(XmlNode& node, const XmlNVPBase& nvp) const
{
	XmlNode libNode = AppendChildNode("lib", node);

	for (LibType::const_iterator it = lib.begin(); it != lib.end(); ++it)
	{
		const string& key = it->first;
		const EPVFormatConfigPtr& val = it->second;

		XmlNode elementNode = AppendChildNode(StandardElementTag.c_str(), libNode);

		elementNode << XmlNVP<string>("key", key);

		XmlNode valueNode = AppendChildNode("value", elementNode);

		if (val)
			SaveToNode(valueNode, XmlNVPBase("value"), *val);
		else
		{
			XmlAttribute nilAttr = AppendAttribute("nil", valueNode);
			nilAttr.set_value(false);
		}
	}
}

}
}
