/*
 *
 *  D-Bus++ - C++ bindings for D-Bus
 *
 *  Copyright (C) 2005-2007  Paolo Durante <shackan@gmail.com>
 *
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


#include <dbus-c++/introspection.h>
#include <dbus-c++/object.h>
#include <dbus-c++/message.h>

#include <dbus/dbus.h>

#include <sstream>

using namespace DBus;

static const char* introspectable_name = "org.freedesktop.DBus.Introspectable";

IntrospectableAdaptor::IntrospectableAdaptor()
: InterfaceAdaptor(introspectable_name)
{
	register_method(IntrospectableAdaptor, Introspect, Introspect);
}

Message IntrospectableAdaptor::Introspect( const CallMessage& call )
{
	debug_log("requested introspection data");

	std::ostringstream xml;

	xml << DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE;

	const std::string path = object()->path();

	xml << "<node name=\"" << path << "\">";

	InterfaceAdaptorTable::const_iterator iti;

	for(iti = _interfaces.begin(); iti != _interfaces.end(); ++iti)
	{
		debug_log("introspecting interface %s", iti->first.c_str());

		IntrospectedInterface* const intro = iti->second->introspect();
		if(intro)
		{
			xml << "<interface name=\"" << intro->name << "\">";

			for(const IntrospectedProperty* p = intro->properties; p->name; ++p)
			{
				std::string access;

				if(p->read)  access += "read";
				if(p->write) access += "write";

				xml << "<property name=\"" << p->name << "\""
				    << " type=\"" << p->type << "\""
				    << " access=\"" << access << "\"/>";
			}

			for(const IntrospectedMethod* m = intro->methods; m->args; ++m)
			{
				xml << "<method name=\"" << m->name << "\">";

				for(const IntrospectedArgument* a = m->args; a->type; ++a)
				{
					xml << "<arg direction=\"" << (a->in ? "in" : "out") << "\""
					    << " type=\"" << a->type << "\"";

					if(a->name) xml << " name=\"" << a->name << "\"";

					xml << "/>";
				}

				xml << "</method>";
			}

			for(const IntrospectedMethod* m = intro->signals; m->args; ++m)
			{
				xml << "<signal name=\"" << m->name << "\">";

				for(const IntrospectedArgument* a = m->args; a->type; ++a)
				{
					xml << "<arg type=\"" << a->type << "\"";

					if(a->name) xml << " name=\"" << a->name << "\"";

					xml << "/>";
				}
				xml << "</signal>";
			}

			xml << "</interface>";
		}
	}
	const ObjectAdaptorPList children = ObjectAdaptor::from_path_prefix(path + '/');

	ObjectAdaptorPList::const_iterator oci;

	debug_log("nb children: %d", children.size());
	for(oci = children.begin(); oci != children.end(); ++oci) 
	{
	 
		std::string name = (*oci)->path().substr(path.length()+1);
		
		std::string::size_type loc = name.find('/');
		if( loc != std::string::npos ) {
			name.substr(loc);
		}
		
		xml << "<node name=\"" << name << "\"/>";
	}

	xml << "</node>";

	ReturnMessage reply(call);
	MessageIter wi = reply.writer();
	wi.append_string(xml.str().c_str());
	return reply;
}

IntrospectedInterface* const IntrospectableAdaptor::introspect() const
{
	static IntrospectedArgument Introspect_args[] =
	{
		{ "data", "s", false },
		{ 0, 0, 0 }
	};
	static IntrospectedMethod Introspectable_methods[] =
	{
		{ "Introspect", Introspect_args },
		{ 0, 0 }
	};
	static IntrospectedMethod Introspectable_signals[] =
	{
		{ 0, 0 }
	};
	static IntrospectedProperty Introspectable_properties[] =
	{
		{ 0, 0, 0, 0 }
	};
	static IntrospectedInterface Introspectable_interface =
	{
		introspectable_name,
		Introspectable_methods,
		Introspectable_signals,
		Introspectable_properties
	};
	return &Introspectable_interface;
}

IntrospectableProxy::IntrospectableProxy()
: InterfaceProxy(introspectable_name)
{}

std::string IntrospectableProxy::Introspect()
{
	DBus::CallMessage call;

	call.member("Introspect");

	DBus::Message ret = invoke_method(call);

	DBus::MessageIter ri = ret.reader();
	const char* str = ri.get_string();

	return str;
}
