#ifndef K3DSDK_PERSISTENCE_H
#define K3DSDK_PERSISTENCE_H

// 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 Declares the k3d::persistence::container and related classes used to automate serialization of data objects
		\author Tim Shead (tshead@k-3d.com)
*/

#include "application.h"
#include "icommand_node.h"
#include "icommand_tree.h"
#include "iobject.h"
#include "ipersistent.h"
#include "result.h"

#include <sdpxml/sdpxml.h>

#include <memory>

namespace k3d
{

// Forward declarations
class idocument;

namespace persistence
{

/////////////////////////////////////////////////////////////////////////////
// idata_proxy

/// Abstract interface for a proxy object that can serialize / deserialize its underlying data object
class idata_proxy
{
public:
	virtual ~idata_proxy() {}

	virtual const std::string name() = 0;
	virtual void load(sdpxml::Document& Document, sdpxml::Element& Element) = 0;
	virtual void load_complete() = 0;
	virtual void save(sdpxml::Element& Element, k3d::idependencies& Dependencies) = 0;

protected:
	idata_proxy() {}
	idata_proxy(const idata_proxy& RHS) {}
	idata_proxy& operator = (const idata_proxy& RHS) { return *this; }
};

} // namespace persistence

} // namespace k3d

#include "persistence_private.h"

namespace k3d
{

namespace persistence
{

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

/**
Mixin class that automates serialization of data objects - derive your parent object
from this one, and call the enable_serialization() method for each child you want
automatically serialized.
*/

class container
{
protected:
	container() {}
	virtual ~container();

	void load(sdpxml::Document& Document, sdpxml::Element& Element);
	void load_complete();
	void save(sdpxml::Element& Element, k3d::idependencies& Dependencies);

	/// Inserts a data object proxy into the collection, so it can be automatically serialized / deserialized
	void enable_serialization(std::auto_ptr<idata_proxy> Data);
	/// Clears the collection of data object proxies
	void clear();

private:
	container(const container& RHS);
	container& operator = (const container& RHS);

	/// Contains a collection of data objects to be automatically serialized
	typedef std::list<idata_proxy*> proxy_list;
	typedef proxy_list::iterator proxy_iterator;
	proxy_list m_proxies;
};

/////////////////////////////////////////////////////////////////////////////
// proxy

/// Convenience factory function for creating persistence::idata_proxy objects
template<typename data_t>
std::auto_ptr<idata_proxy> proxy(data_t& Data)
{
	return std::auto_ptr<idata_proxy>(new k3d::implementation_private::generic_data_proxy<data_t>(Data));
}

/// Convenience factory function for creating persistence::idata_proxy objects
template<typename data_t>
std::auto_ptr<idata_proxy> object_proxy(data_t& Data)
{
	return std::auto_ptr<idata_proxy>(new k3d::implementation_private::object_data_proxy<data_t>(Data));
}

} // namespace persistence

/// Provides a boilerplate implementation of an object that can be serialized
template<typename base_t>
class persistent :
	public base_t,
	public ipersistent,
	public persistence::container
{
public:
	persistent(idocument& Document) :
		base_t(Document)
	{
	}

	void load(sdpxml::Document& Document, sdpxml::Element& Element)
	{
		// Load object name ...
		std::string name;
		sdpxml::ParseAttribute(Element, "name", name);
		base_t::set_name(name);

		// Load Variables
		persistence::container::load(Document, Element);

		// Add ourselves to the command tree ...
		icommand_node* const parent = dynamic_cast<icommand_node*>(&base_t::document());
		return_if_fail(parent);
		application().command_tree().add_node(*this, *parent);
	}

	void load_complete()
	{
		persistence::container::load_complete();
	}

	void save(sdpxml::Element& Element, k3d::idependencies& Dependencies)
	{
		persistence::container::save(Element, Dependencies);
	}
};

} // namespace k3d

#endif // !K3DSDK_PERSISTENCE_H


