// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\brief Implements the k3d::container class, which automatically serializes / deserializes a group of data objects
		\author Tim Shead (tshead@k-3d.com)
*/

#include "persistence.h"
#include "result.h"
#include "utility.h"

#include <iostream>

namespace k3d
{

namespace persistence
{

/////////////////////////////////////////////////////////////////////////////
// container

container::~container()
{
	clear();
}

void container::load(sdpxml::Document& Document, sdpxml::Element& Element)
{
	// Create a mapping of elements to choose from ...
	typedef std::map<std::string, sdpxml::ElementPointer> element_map_t;
	typedef element_map_t::iterator element_iterator;
	element_map_t element_map;

	// Insert <variable> tags ... look for a container called <variables> first ...
	sdpxml::ElementPointer variables = sdpxml::FindElement(Element, sdpxml::SameName("variables"));

	/** \deprecated No <variables> tag, so for backwards compatibility, try the parent container */
	if(!variables)
		variables = &Element;

	for(sdpxml::ElementCollection::iterator element = variables->Children().begin(); element != variables->Children().end(); element++)
		{
			/** \todo Allow <object>, <shader>, and <argument> tags for backwards-compatibility with documents prior to version 0.2 ... should be removed from the 0.5 branch */
			if(element->Name() == "variable" || element->Name() == "object" || element->Name() == "shader" || element->Name() == "argument")
				{
					const std::string name = sdpxml::GetAttribute<std::string>(*element, "name", "");
					if(0 == name.size())
						continue;

					if(element_map.find(name) != element_map.end())
						std::cerr << __PRETTY_FUNCTION__ << " - duplicate <variable> [" << name << "] at " << sdpxml::FileReference(Document, *element) << " will be ignored" << std::endl;
					else
						element_map[name] = &(*element);

					// Warn the user about old data ...
					if(element->Name() != "variable")
						std::cerr << __PRETTY_FUNCTION__ << " - loading data from obsolete <" << element->Name() << "> tag ... re-save document to eliminate this warning" << std::endl;
				}
		}

	// If we didn't find anything, we're done ...
	if(0 == element_map.size())
		return;

	// For each proxy ...
	for(proxy_iterator proxy = m_proxies.begin(); proxy != m_proxies.end(); ++proxy)
		{
			// See if it has a corresponding element ...
			sdpxml::ElementPointer proxy_element = element_map[(*proxy)->name()];
			if(!proxy_element)
				continue;

			// Load it up!
			(*proxy)->load(Document, *proxy_element);
			element_map.erase((*proxy)->name());
		}
}

void container::load_complete()
{
	// Defer to our proxies ...
	std::for_each(m_proxies.begin(), m_proxies.end(), std::mem_fun(&idata_proxy::load_complete));
}

void container::save(sdpxml::Element& Element, k3d::idependencies& Dependencies)
{
	// If we don't have anything to save, we're done ...
	if(!m_proxies.size())
		return;

	// Create an XML element to contain our proxies if it doesn't already exist ...
	sdpxml::Element* variables = sdpxml::FindElement(Element, sdpxml::SameName("variables"));
	if(!variables)
		variables = &Element.Append(sdpxml::Element("variables"));

	// Save 'em ...
	for(proxy_iterator proxy = m_proxies.begin(); proxy != m_proxies.end(); ++proxy)
		(*proxy)->save(*variables, Dependencies);
}

void container::enable_serialization(std::auto_ptr<idata_proxy> Proxy)
{
	// Sanity checks ...
	return_if_fail(Proxy.get());
	m_proxies.push_back(Proxy.release());
}

void container::clear()
{
	std::for_each(m_proxies.begin(), m_proxies.end(), k3d::delete_object());
	m_proxies.clear();
}

} // namespace persistence

} // namespace k3d


