/*
*   Copyright (C) 2007 Kevin Krammer <kevin.krammer@gmx.at>
*
*   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.
*/

// TQt includes
#include <tqdom.h>
#include <tqtextstream.h>

// local includes
#include "methodgen.h"

static bool parseDBusSignature(const TQString& signature, Argument& argument)
{
    argument.dbusSignature = signature;

    if (signature.length() == 1)
    {
        if (signature == "b")
        {
            argument.signature = "bool";
            argument.accessor = "Bool";
            argument.isPrimitive = true;
        }
        else if (signature == "y")
        {
            argument.signature = "TQ_UINT8";
            argument.accessor = "Byte";
            argument.isPrimitive = true;
        }
        else if (signature == "n")
        {
            argument.signature = "TQ_INT16";
            argument.accessor = "Int16";
            argument.isPrimitive = true;
        }
        else if (signature == "q")
        {
            argument.signature = "TQ_UINT16";
            argument.accessor = "UInt16";
            argument.isPrimitive = true;
        }
        else if (signature == "i")
        {
            argument.signature = "TQ_INT32";
            argument.accessor = "Int32";
            argument.isPrimitive = true;
        }
        else if (signature == "u")
        {
            argument.signature = "TQ_UINT32";
            argument.accessor = "UInt32";
            argument.isPrimitive = true;
        }
        else if (signature == "x")
        {
            argument.signature = "TQ_INT64";
            argument.accessor = "Int64";
            argument.isPrimitive = true;
        }
        else if (signature == "t")
        {
            argument.signature = "TQ_UINT64";
            argument.accessor = "UInt64";
            argument.isPrimitive = true;
        }
        else if (signature == "d")
        {
            argument.signature = "double";
            argument.accessor = "Double";
            argument.isPrimitive = true;
        }
        else if (signature == "s")
        {
            argument.signature = "TQString";
            argument.accessor = "String";
            argument.isPrimitive = false;

            argument.forwardDeclarations.append("class TQString");
            argument.sourceIncludes["TQt"].append("<tqstring.h>");
        }
        else if (signature == "o")
        {
            argument.signature = "TQT_DBusObjectPath";
            argument.accessor = "ObjectPath";
            argument.isPrimitive = false;

            argument.forwardDeclarations.append("class TQT_DBusObjectPath");
            argument.sourceIncludes["tqdbus"].append("<tqdbusobjectpath.h>");
        }
        else if (signature == "h")
        {
            argument.signature = "TQT_DBusUnixFd";
            argument.accessor = "UnixFd";
            argument.isPrimitive = false;

            argument.forwardDeclarations.append("class TQT_DBusUnixFd");
            argument.sourceIncludes["tqdbus"].append("<tqdbusunixfd.h>");
        }
        else if (signature == "v")
        {
            argument.signature = "TQT_DBusVariant";
            argument.accessor = "Variant";
            argument.isPrimitive = false;

            argument.forwardDeclarations.append("class TQT_DBusVariant");
            argument.sourceIncludes["tqdbus"].append("<tqdbusvariant.h>");
        }
        else
            return false;
    }
    else if (signature.startsWith("a"))
    {
        if (signature == "as")
        {
            argument.signature = "TQStringList";
            argument.accessor = "List";
            argument.subAccessor = "TQStringList";
            argument.isPrimitive = false;

            argument.forwardDeclarations.append("class TQStringList");

            argument.sourceIncludes["tqdbus"].append("<tqdbusdatalist.h>");
            argument.sourceIncludes["TQt"].append("<tqstringlist.h>");
        }
        else if (signature.startsWith("a{"))
        {
            int from = signature.find("{");
            int to = signature.findRev("}");
            if (from == -1 || to == -1 || (to - from - 1) < 2) return false;

            TQString dictSignature = signature.mid(from + 1, (to - from - 1));

            Argument key;
            if (!parseDBusSignature(dictSignature.left(1), key)) return false;

            Argument value;
            if (parseDBusSignature(dictSignature.mid(1), value))
            {
                if (!value.subAccessor.isEmpty())
                {
                    argument.isPrimitive = false;
                    argument.containerClass = "TQT_DBusDataMap< " + key.signature + " >";
                    argument.signature = "TQT_DBusDataMap< " + key.signature + " >";
                    argument.accessor = key.accessor + "KeyMap";

                    argument.forwardDeclarations.append("template <typename K> class TQT_DBusDataMap");
                    argument.forwardDeclarations += key.forwardDeclarations;

                    argument.sourceIncludes = key.sourceIncludes;
                    argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>");
                    argument.sourceIncludes["tqdbus"].append("<tqdbusdatamap.h>");
                }
                else
                {
                    argument.isPrimitive = false;
                    argument.containerClass = "TQT_DBusDataMap< " + key.signature + " >";
                    argument.signature = "TQMap< " + key.signature +
                                        ", " + value.signature + " >";
                    argument.accessor = key.accessor + "KeyMap";
                    argument.subAccessor = value.accessor + "Map";

                    argument.forwardDeclarations.append("template <typename K, typename V> class TQMap");
                    argument.forwardDeclarations += key.forwardDeclarations;
                    argument.forwardDeclarations += value.forwardDeclarations;

                    argument.sourceIncludes = key.sourceIncludes;
                    argument.sourceIncludes["TQt"].append("<tqmap.h>");
                    argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>");
                    argument.sourceIncludes["tqdbus"].append("<tqdbusdatamap.h>");

                    TQMap<TQString, TQStringList>::const_iterator it =
                        value.sourceIncludes.begin();
                    TQMap<TQString, TQStringList>::const_iterator endIt =
                        value.sourceIncludes.end();
                    for (; it != endIt; ++it)
                    {
                        argument.sourceIncludes[it.key()] += it.data();
                    }
                }
            }
            else
            {
                argument.isPrimitive = false;
                argument.containerClass = "TQT_DBusDataMap< " + key.signature + " >";
                argument.signature = "TQT_DBusDataMap< " + key.signature + " >";
                argument.accessor = key.accessor + "KeyMap";

                argument.forwardDeclarations.append("template <typename K> class TQT_DBusDataMap");
                argument.forwardDeclarations += key.forwardDeclarations;

                argument.sourceIncludes = key.sourceIncludes;
                argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>");
                argument.sourceIncludes["tqdbus"].append("<tqdbusdatamap.h>");
            }
        }
        else
        {
            TQString itemSignature = signature.mid(1);

            Argument item;
            if (parseDBusSignature(itemSignature, item) && !itemSignature.startsWith("a"))
            {
                argument.isPrimitive = false;
                argument.signature = "TQValueList< " + item.signature + " >";
                argument.accessor = "List";
                argument.subAccessor = item.accessor + "List";
                argument.containerClass = "TQT_DBusDataList";

                argument.forwardDeclarations.append("class TQT_DBusDataList");
                argument.forwardDeclarations.append("template <typename T> class TQValueList");
                argument.forwardDeclarations += item.forwardDeclarations;

                argument.sourceIncludes["TQt"].append("<tqvaluelist.h>");
                argument.sourceIncludes["tqdbus"].append("<tqdbusdatalist.h>");

                TQMap<TQString, TQStringList>::const_iterator it =
                    item.sourceIncludes.begin();
                TQMap<TQString, TQStringList>::const_iterator endIt =
                    item.sourceIncludes.end();
                for (; it != endIt; ++it)
                {
                    argument.sourceIncludes[it.key()] += it.data();
                }
            }
            else
            {
                argument.signature = "TQT_DBusDataList";
                argument.accessor = "List";
                argument.isPrimitive = false;

                argument.forwardDeclarations.append("class TQT_DBusDataList");

                argument.sourceIncludes["tqdbus"].append("<tqdbusdatalist.h>");
            }
        }
    }
    else
        return false;

    return true;
}

static TQMap<TQString, TQString> extractTypeAnnotations(const TQDomElement& element)
{
    const TQString annotationPrefix = "org.freedesktop.DBus.TQt3.Type.";

    TQMap<TQString, TQString> annotations;

    TQDomNode node = element.firstChild();
    for (uint count = 1; !node.isNull(); node = node.nextSibling(), ++count)
    {
        if (!node.isElement()) continue;

        TQDomElement element = node.toElement();
        if (element.tagName() != "annotation") continue;

        TQString name = element.attribute("name");
        if (name.isEmpty()) continue;

        TQString value = element.attribute("value").stripWhiteSpace();
        if (value.isEmpty()) continue;

        if (!name.startsWith(annotationPrefix)) continue;

        TQString arg = name.mid(annotationPrefix.length());

        annotations.insert(arg, value);
    }

    return annotations;
}

static bool hasAnnotation(const TQDomElement& element, const TQString& annotation, TQString* value = 0)
{
    for (TQDomNode node = element.firstChild(); !node.isNull();
         node = node.nextSibling())
    {
        if (!node.isElement()) continue;

        TQDomElement childElement = node.toElement();
        if (childElement.tagName() != "annotation") continue;
        if (childElement.attribute("name") != annotation) continue;

        if (value != 0) *value = childElement.attribute("value");
        return true;
    }

    return false;
}

static TQValueList<Argument> extractArguments(const TQDomElement& methodElement,
        Class& classData)
{
    TQMap<TQString, TQString> argAnnotations = extractTypeAnnotations(methodElement);

    TQValueList<Argument> arguments;

    bool isSignal = methodElement.tagName() == "signal";

    uint inCount  = 0;
    uint outCount = 0;
    for (TQDomNode node = methodElement.firstChild(); !node.isNull();
         node = node.nextSibling())
    {
        if (!node.isElement()) continue;

        TQDomElement element = node.toElement();
        if (element.tagName() != "arg") continue;
        if (element.attribute("type").isEmpty()) continue;

        Argument argument;
        argument.name = element.attribute("name");
        if (argument.name.isEmpty())
            argument.name = TQString("arg%1").arg(inCount + outCount);

        argument.direction = Argument::In;
        if (!isSignal && element.attribute("direction", "in") == "out")
            argument.direction = Argument::Out;

        TQString annotation;
        if (!isSignal && argument.direction == Argument::In)
        {
            annotation = argAnnotations[TQString("In%1").arg(inCount)];
            ++inCount;
        }
        else
        {
            annotation = argAnnotations[TQString("Out%1").arg(outCount)];
            ++outCount;
        }

        if (!annotation.isEmpty())
        {
            // just assume nobody uses annotations for primitives
            argument.annotatedType = annotation;
            argument.signature     = annotation;
            argument.isPrimitive   = false;
            argument.dbusSignature = element.attribute("type");

            TQString includeBase =
                TQString("\"%1type%2.h\"").arg(classData.name.lower());

            argument.headerIncludes["local"].append(includeBase.arg("declarations"));
            argument.sourceIncludes["local"].append(includeBase.arg("includes"));
            argument.sourceIncludes["tqdbus"].append("<tqdbusdataconverter.h>");
        }
        else if (!parseDBusSignature(element.attribute("type"), argument))
        {
            argument.signature = "TQT_DBusData";
            argument.isPrimitive = false;

            argument.forwardDeclarations.append("class TQT_DBusData");
            argument.sourceIncludes["tqdbus"].append("<tqdbusdata.h>");
        }

        arguments.append(argument);
    }

    return arguments;
}

static void writeVariable(const Argument& argument, uint index,
        const TQString& prefix, TQTextStream& stream)
{
    stream << prefix << argument.signature << " _" << argument.name;
    if (argument.direction == Argument::In)
    {
        if (!argument.annotatedType.isEmpty())
        {
            stream << ";" << endl;

            // TODO: error handling?
            stream << prefix << "TQT_DBusDataConverter::convertFromTQT_DBusData<"
                   << argument.annotatedType
                   << TQString(">(message[%1], _").arg(index)
                   << argument.name << ")";
        }
        else if (!argument.accessor.isEmpty())
        {
            stream << TQString::fromUtf8(" = message[%1].to").arg(index);
            stream << argument.accessor;

            if (!argument.subAccessor.isEmpty())
            {
                stream << TQString("().to%1").arg(argument.subAccessor);
            }

            stream << "()";
        }
        else
            stream << TQString::fromUtf8(" = message[%1]").arg(index);
    }

    stream << ";" << endl;
}

static void writeVariables(const TQString& prefix, const Method& method,
        TQTextStream& stream)
{
    uint count = 0;
    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    for (; it != endIt; ++it)
    {
        writeVariable(*it, count, prefix, stream);

        if ((*it).direction == Argument::In) ++count;
    }
}

static void writeSignalEmit(const Method& signal, TQTextStream& stream)
{
    stream << "        emit " << signal.name << "(";

    TQValueList<Argument>::const_iterator it    = signal.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = signal.arguments.end();
    for (; it != endIt;)
    {
        stream << "_" << (*it).name;

        ++it;
        if (it != endIt) stream << ", ";
    }

    stream << ");" << endl;
}

static void writeMethodIntrospection(const Method& method, bool& firstArgument,
    TQTextStream& stream)
{
    stream << "    methodElement.setAttribute(\"name\", \""
           << method.name << "\");" << endl;

    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    for (; it != endIt; ++it)
    {
        stream << endl;
        if (firstArgument)
        {
            firstArgument = false;

            stream << "    TQDomElement argumentElement = document.createElement("
                   << "\"arg\");" << endl;
        }
        else
        {
            stream << "    argumentElement = document.createElement("
                   << "\"arg\");" << endl;
        }

        stream << "    argumentElement.setAttribute(\"name\",      \""
               << (*it).name << "\");" << endl;

        stream << "    argumentElement.setAttribute(\"type\",      \""
               << (*it).dbusSignature << "\");" << endl;

        stream << "    argumentElement.setAttribute(\"direction\", \""
               << ((*it).direction == Argument::In ? "in" : "out") << "\");"
               << endl;

        stream << "    methodElement.appendChild(argumentElement);" << endl;
    }
    stream << endl;
}

static void writeNodeInitialization(const Class& classData,
        const TQValueList<Class>& interfaces, TQTextStream& stream)
{
    stream << "bool " << classData.name
           << "::registerObject(const TQT_DBusConnection& connection, "
           << "const TQString& path)" << endl;
    stream << "{" << endl;
    stream << "    if (path.isEmpty()) return false;" << endl;
    stream << endl;

    stream << "    if (!m_private->objectPath.isEmpty()) unregisterObject();"
           << endl;
    stream << endl;

    stream << "    m_private->connection = connection;" << endl;
    stream << "    m_private->objectPath = path;" << endl;
    stream << endl;
    stream << "    if (!m_private->connection.registerObject(path, this))" << endl;
    stream << "    {" << endl;
    stream << "        m_private->connection = TQT_DBusConnection();" << endl;
    stream << "        m_private->objectPath = TQString();" << endl;
    stream << endl;
    stream << "        return false;" << endl;
    stream << "    }" << endl;
    stream << endl;

    stream << "    if (m_private->interfaces.isEmpty())" << endl;
    stream << "    {" << endl;
    stream << "        TQString name = \"org.freedesktop.DBus.Introspectable\";"
           << endl;
    stream << "        TQT_DBusObjectBase* interface = m_private;" << endl;
    stream << "        m_private->interfaces.insert(name, interface);" << endl;

    TQValueList<Class>::const_iterator it    = interfaces.begin();
    TQValueList<Class>::const_iterator endIt = interfaces.end();
    for (; it != endIt; ++it)
    {
        stream << endl;
        stream << "        name = \"" << (*it).dbusName << "\";" << endl;
        stream << "        interface = createInterface(name);" << endl;
        stream << "        Q_ASSERT(interface != 0);" << endl;
        stream << "        m_private->interfaces.insert(name, interface);" << endl;
    }

    stream << "    }" << endl;
    stream << endl;
    stream << "    return true;" << endl;
    stream << "}" << endl;
    stream << endl;
}
static void writeNodeIntrospection(const Class& classData,
        const TQValueList<Class>& interfaces, TQTextStream& stream)
{
    stream << "void " << classData.name << "::Private"
           << "::cacheIntrospectionData()" << endl;
    stream << "{" << endl;

    stream << "    TQDomDocument doc;" << endl;
    stream << "    TQDomElement nodeElement = doc.createElement(\"node\");" << endl;
    stream << "    TQDomElement interfaceElement = doc.createElement(\"interface\");"
           << endl;
    stream << "    org::freedesktop::DBus::Introspectable"
           << "::buildIntrospectionData(interfaceElement);" << endl;
    stream << "    nodeElement.appendChild(interfaceElement);" << endl;

    TQValueList<Class>::const_iterator it    = interfaces.begin();
    TQValueList<Class>::const_iterator endIt = interfaces.end();
    for (; it != endIt; ++it)
    {
        if ((*it).dbusName == "org.freedesktop.DBus.Introspectable") continue;

        stream << endl;
        stream << "    interfaceElement = doc.createElement(\"interface\");"
               << endl;
        stream << "    " << (*it).namespaces.join("::") + "::" + (*it).name
               << "::buildIntrospectionData(interfaceElement);" << endl;
        stream << "    nodeElement.appendChild(interfaceElement);" << endl;
    }

    stream << endl;
    stream << "    doc.appendChild(nodeElement);" << endl;
    stream << endl;

    stream << "    introspectionData = \"<!DOCTYPE node PUBLIC \\\""
           << "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\\\"\\n"
           << "\\\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
           << "\\\">\\n\";" << endl;
    stream << "    introspectionData += doc.toString();" << endl;
    stream << "}" << endl;
    stream << endl;
}

bool MethodGenerator::extractMethods(const TQDomElement& interfaceElement,
        Class& classData)
{
    TQMap<TQString, TQString> propertyAnnotations =
        extractTypeAnnotations(interfaceElement);

    uint propertyCount = 0;
    for (TQDomNode node = interfaceElement.firstChild(); !node.isNull();
         node = node.nextSibling())
    {
        if (!node.isElement()) continue;

        TQDomElement element = node.toElement();
        if (element.attribute("name").isEmpty()) continue;

        if (element.tagName() == "method" || element.tagName() == "signal")
        {
            Method method;
            method.name = element.attribute("name");
            method.arguments = extractArguments(element, classData);
            method.noReply = false;
            method.async = false;

            if (element.tagName() == "method")
            {
                method.async = hasAnnotation(element, "org.freedesktop.DBus.GLib.Async");
                classData.methods.append(method);
                if (method.async) {
                    method.async = false;
                    classData.methods.append(method);
                }
            }
            else
                classData.msignals.append(method);
        }
        else if (element.tagName() == "property")
        {
            Property property;
            property.name  = element.attribute("name");
            property.read  = element.attribute("access").find("read")  != -1;
            property.write = element.attribute("access").find("write") != -1;

            TQString annotation =
                propertyAnnotations[TQString("Property%1").arg(propertyCount)];

            if (!annotation.isEmpty())
            {
                property.annotatedType = annotation;
                property.signature     = annotation;
                property.dbusSignature = element.attribute("type");
                property.isPrimitive   = false;

                TQString includeBase =
                    TQString("\"%1type%2.h\"").arg(classData.name.lower());

                property.headerIncludes["local"].append(includeBase.arg("declarations"));
                property.sourceIncludes["local"].append(includeBase.arg("includes"));
                property.sourceIncludes["tqdbus"].append("<tqdbusdataconverter.h>");
            }
            else if (!parseDBusSignature(element.attribute("type"), property))
            {
                property.signature = "TQT_DBusData";
                property.isPrimitive = false;

                property.forwardDeclarations.append("class TQT_DBusData");
                property.sourceIncludes["tqdbus"].append("<tqdbusdata.h>");
            }

            classData.properties.append(property);
            ++propertyCount;
        }
    }

    return !classData.methods.isEmpty() || !classData.msignals.isEmpty() ||
           !classData.properties.isEmpty();
}

void MethodGenerator::writeMethodDeclaration(const Method& method, bool pureVirtual,
        bool withError, TQTextStream& stream)
{
    stream << method.name << "(";

    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    for (; it != endIt;)
    {
        if (!(*it).isPrimitive && (*it).direction == Argument::In)
            stream << "const ";

        stream << (*it).signature;

        if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&";

        stream << " " << (*it).name;

        ++it;
        if (it != endIt || withError) stream << ", ";
    }

    if (withError)
        stream << "TQT_DBusError& error)";
    else
        stream << ")";

    if (pureVirtual)
        stream << " = 0;" << endl;
    else
        stream << ";" << endl;

    stream << endl;
}

void MethodGenerator::writePropertyDeclaration(const Property& property,
        bool pureVirtual, TQTextStream& stream)
{
    if (property.write)
    {
        stream << "    virtual void set" << property.name << "(";

        if (!property.isPrimitive) stream << "const ";

        stream << property.signature;

        if (!property.isPrimitive) stream << "&";

        stream << " value, TQT_DBusError& error)";

        if (pureVirtual)
            stream << " = 0;" << endl;
        else
            stream << ";" << endl;
    }

    if (property.read)
    {
        stream << "    virtual " << property.signature << " get"
               << property.name << "(TQT_DBusError& error) const";

        if (pureVirtual)
            stream << " = 0;" << endl;
        else
            stream << ";" << endl;
    }

    if (property.read || property.write) stream << endl;
}

void MethodGenerator::writeMethodCallDeclaration(const Method& method,
        TQTextStream& stream)
{
    if (method.async)
        stream << "void call" << method.name << "Async";
    else
        stream << "TQT_DBusMessage call" << method.name;

    stream << "(const TQT_DBusMessage& message);" << endl;
    stream << endl;
}

void MethodGenerator::writeMethodCall(const Class& classData,
        const Method& method, TQTextStream& stream)
{
    if (method.async)
        stream << "void " << classData.name << "::call" << method.name << "Async";
    else
        stream << "TQT_DBusMessage " << classData.name << "::call" << method.name;

    stream << "(const TQT_DBusMessage& message)" << endl;

    stream << "{" << endl;

    if (method.async)
    {
        // FIXME: using writeVariables by removing asyncCallId argument
        Method reducedMethod = method;
        reducedMethod.arguments.pop_front();

        writeVariables("    ", reducedMethod, stream);
    }
    else
    {
        stream << "    TQT_DBusError   error;" << endl;
        stream << "    TQT_DBusMessage reply;" << endl;
        stream << endl;

        writeVariables("    ", method, stream);
    }

    stream << endl;

    if (method.async)
    {
        stream << "    int _asyncCallId = 0;" << endl;
        stream << "    while (m_asyncCalls.find(_asyncCallId) != m_asyncCalls.end())"
               << endl;
        stream << "    {" << endl;
        stream << "        ++_asyncCallId;" << endl;
        stream << "    }" << endl;
        stream << "    m_asyncCalls.insert(_asyncCallId, message);" << endl;
        stream << endl;

        stream << "    " << method.name << "Async(";
    }
    else
        stream << "    if (" << method.name << "(";

    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    while (it != endIt)
    {
        stream << "_" << (*it).name;

        ++it;
        if (it != endIt) stream << ", ";
    }

    if (method.async)
    {
        stream << ");" << endl;
        stream << endl;

        stream << "    return;" << endl;
        stream << "}" << endl;
        stream << endl;
        return;
    }

	if (method.arguments.count() > 0) stream << ", ";
    stream << "error))" << endl;

    stream << "    {" << endl;
    stream << "        reply = TQT_DBusMessage::methodReply(message);" << endl;

    it = method.arguments.begin();
    for (; it != endIt; ++it)
    {
        if ((*it).direction == Argument::Out)
        {
            if (!(*it).annotatedType.isEmpty())
            {
                stream << "        TQT_DBusData " << (*it).name << "Data;" << endl;
                stream << "        TQT_DBusDataConverter::convertToTQT_DBusData<"
                       << (*it).annotatedType << ">(_"
                       << (*it).name << ", " << (*it).name << "Data);"
                       << endl;
                stream << "        reply << " << (*it).name << "Data";
            }
            else if (!(*it).accessor.isEmpty())
            {
                stream << "        reply << TQT_DBusData::from" << (*it).accessor;
                if (!(*it).subAccessor.isEmpty())
                {
                    stream << "(" << (*it).containerClass;
                }

                stream << "(_" << (*it).name << ")";

                if (!(*it).subAccessor.isEmpty())
                {
                    stream << ")";
                }
            }
            else
                stream << "        reply << _" << (*it).name;

            stream << ";" << endl;
        }
    }
    stream << "    }" << endl;
    stream << "    else" << endl;
    stream << "    {" << endl;

    stream << "        if (!error.isValid())" << endl;
    stream << "        {" << endl;
    stream << "            tqWarning(\"Call to implementation of ";

    TQStringList::const_iterator nsIt    = classData.namespaces.begin();
    TQStringList::const_iterator nsEndIt = classData.namespaces.end();
    for (; nsIt != nsEndIt; ++nsIt)
    {
        stream << *nsIt << "::";
    }

    stream  << classData.name << "::" << method.name;
    stream << " returned 'false' but error object is not valid!\");" << endl;
    stream << endl;
    stream << "            error = TQT_DBusError::stdFailed(\"";

    nsIt    = classData.namespaces.begin();
    for (; nsIt != nsEndIt; ++nsIt)
    {
        stream << *nsIt << ".";
    }

    stream << classData.name << "." << method.name << " execution failed\");"
           << endl;
    stream << "        }" << endl;
    stream << endl;

    stream << "        reply = TQT_DBusMessage::methodError(message, error);" << endl;

    stream << "    }" << endl;
    stream << endl;
    stream << "    return reply;" << endl;
    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeSignalEmitter(const Class& classData,
        const Method& method, TQTextStream& stream)
{
    stream << "bool " << classData.name << "::emit" << method.name << "(";

    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    for (; it != endIt;)
    {
        if (!(*it).isPrimitive && (*it).direction == Argument::In)
            stream << "const ";

        stream << (*it).signature;

        if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&";

        stream << " " << (*it).name;

        ++it;
        if (it != endIt) stream << ", ";
    }

    stream << ")" << endl;

    stream << "{" << endl;

    // TODO: create error or use enum for return
    stream << "    TQString path = objectPath();" << endl;
    stream << "    Q_ASSERT(!path.isEmpty());" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage message = TQT_DBusMessage::signal(path, \"";
    stream << classData.dbusName << "\", \"" << method.name << "\");" << endl;
    stream << endl;

    it = method.arguments.begin();
    for (; it != endIt; ++it)
    {
        if ((*it).direction == Argument::In)
        {
            if (!(*it).annotatedType.isEmpty())
            {
                // TODO: error handling
                stream << "    TQT_DBusData " << (*it).name << "Data;" << endl;
                stream << "    if (TQT_DBusDataConverter:convertToTQT_DBusData<"
                       << (*it).annotatedType << ">("
                       << (*it).name << ", " << (*it).name << "Data"
                       << ") != TQT_DBusDataConverter::Success) return false;"
                       << endl;
                stream << "    message << " << (*it).name << "Data";
            }
            else if (!(*it).accessor.isEmpty())
            {
                stream << "    message << TQT_DBusData::from" << (*it).accessor;
                if (!(*it).subAccessor.isEmpty())
                {
                    stream << "(" << (*it).containerClass;
                }

                stream << "(" << (*it).name << ")";

                if (!(*it).subAccessor.isEmpty())
                {
                    stream << ")";
                }
            }
            else
                stream << "    message << " << (*it).name;

            stream << ";" << endl;
        }
    }
    stream << endl;

    stream << "    return handleSignalSend(message);" << endl;

    stream << "}" << endl;
    stream << endl;
}


void MethodGenerator::writeInterfaceAsyncReplyHandler(const Class& classData,
    const Method& method, TQTextStream& stream)
{
    stream << "void " << classData.name << "::" << method.name
           << "AsyncReply(";

    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    while (it != endIt)
    {
        if (!(*it).isPrimitive && (*it).direction == Argument::In)
            stream << "const ";

        stream << (*it).signature;

        if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&";

        stream << " " << (*it).name;

        ++it;
        if (it != endIt) stream << ", ";
    }
    stream << ")" << endl;
    stream << endl;
    stream << "{" << endl;

    stream << "    TQMap<int, TQT_DBusMessage>::iterator findIt = m_asyncCalls.find(asyncCallId);" << endl;
    stream << "    if (findIt == m_asyncCalls.end()) return;" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage call = findIt.data();" << endl;
    stream << "    m_asyncCalls.erase(findIt);" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage reply = TQT_DBusMessage::methodReply(call);"
           << endl;

    it = method.arguments.begin();
    for (++it; it != endIt; ++it) // skip asyncCallId at beginning
    {
        if (!(*it).annotatedType.isEmpty())
        {
            stream << "    TQT_DBusData " << (*it).name << "Data;" << endl;

            // TODO error handling
            stream << "    if (TQT_DBusDataConverter::convertToTQT_DBusData<"
                   << (*it).annotatedType << ">(" << (*it).name << ", "
                   << (*it).name << "Data"
                   << ") != TQT_DBusDataConverter::Success) return false;"
                   << endl;
            stream << "    reply << " << (*it).name << "Data;" << endl;
        }
        else if (!(*it).accessor.isEmpty())
        {
            stream << "    reply << TQT_DBusData::from" << (*it).accessor << "(";

            if ((*it).subAccessor.isEmpty())
                stream << (*it).name;
            else
                stream << (*it).containerClass << "(" << (*it).name << ")";

            stream << ");" << endl;
        }
        else
            stream << "    reply << " << (*it).name << ";" << endl;
    }
    stream << endl;

    stream << "    handleMethodReply(reply);" << endl;

    stream << "}" << endl;
    stream << endl;

    stream << "void " << classData.name << "::" << method.name
           << "AsyncError(int asyncCallId, const TQT_DBusError& error)";
    stream << endl;

    stream << "{" << endl;

    stream << "    TQMap<int, TQT_DBusMessage>::iterator findIt = m_asyncCalls.find(asyncCallId);" << endl;
    stream << "    if (findIt == m_asyncCalls.end()) return;" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage call = findIt.data();" << endl;
    stream << "    m_asyncCalls.erase(findIt);" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage reply = TQT_DBusMessage::methodError(call, error);"
           << endl;
    stream << "    handleMethodReply(reply);" << endl;

    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeInterfaceMainMethod(const Class& classData,
        TQTextStream& stream)
{
    if (classData.methods.isEmpty()) return;

    stream << "bool " << classData.name
           << "::handleMethodCall(const TQT_DBusMessage& message)" << endl;
    stream << "{" << endl;

    stream << "    if (message.interface() != \"" << classData.dbusName
           << "\") return false;" << endl;
    stream << endl;

    TQValueList<Method>::const_iterator it    = classData.methods.begin();
    TQValueList<Method>::const_iterator endIt = classData.methods.end();
    for (; it != endIt; ++it)
    {
        stream << "    if (message.member() == \"" << (*it).name << "\")" << endl;
        stream << "    {" << endl;

        if ((*it).async)
        {
            stream << "        call" << (*it).name << "Async(message);" << endl;
            stream << endl;
        }
        else
        {
            stream << "        TQT_DBusMessage reply = call" << (*it).name
                   << "(message);" << endl;
            stream << "        handleMethodReply(reply);" << endl;
            stream << endl;
        }
        stream << "        return true;" << endl;
        stream << "    }" << endl;
        stream << endl;
    }

    stream << "    return false; " << endl;
    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeSignalHandler(const Class& classData,
        TQTextStream& stream)
{
    stream << "void " << classData.name
           << "::slotHandleDBusSignal(const TQT_DBusMessage& message)" << endl;
    stream << "{" << endl;

    TQValueList<Method>::const_iterator it    = classData.msignals.begin();
    TQValueList<Method>::const_iterator endIt = classData.msignals.end();
    bool first = true;
    for (; it != endIt; ++it)
    {
        stream << "    ";

        if (!first)
            stream << "else ";
        else
            first = false;

        stream << "if (message.member() == \"" << (*it).name << "\")" << endl;
        stream << "    {" << endl;

        writeVariables("        ", *it, stream);
        stream << endl;

        writeSignalEmit(*it, stream);

        stream << "    }" << endl;
    }

    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeProxyBegin(const Class& classData, TQTextStream& stream)
{
    stream << classData.name << "::" << classData.name
           << "(const TQString& service, const TQString& path, TQObject* parent, const char* name)" << endl;
    stream << "    : TQObject(parent, name)," << endl;
    stream << "      m_baseProxy(new TQT_DBusProxy())" << endl;
    stream << "{" << endl;
    stream << "    m_baseProxy->setInterface(\""
           << classData.dbusName << "\");" << endl;
    stream << "    m_baseProxy->setPath(path);" << endl;
    stream << "    m_baseProxy->setService(service);" << endl;
    stream << endl;

    if (!classData.msignals.isEmpty())
    {
        stream << "    TQObject::connect(m_baseProxy, "
               << "TQT_SIGNAL(dbusSignal(const TQT_DBusMessage&))," << endl;
        stream << "                     this, "
               << "       TQT_SLOT(slotHandleDBusSignal(const TQT_DBusMessage&)));"
               << endl;
    }

    if (!classData.asyncReplySignals.isEmpty())
    {
        stream << "    TQObject::connect(m_baseProxy, "
               << "TQT_SIGNAL(asyncReply(int, const TQT_DBusMessage&))," << endl;
        stream << "                     this, "
               << "       TQT_SLOT(slotHandleAsyncReply(int, const TQT_DBusMessage&)));"
               << endl;
    }

    stream << "}" << endl;

    stream << endl;

    stream << classData.name << "::~" << classData.name << "()" << endl;
    stream << "{" << endl;
    stream << "    delete m_baseProxy;" << endl;
    stream << "}" << endl;
    stream << endl;

    stream << "void " << classData.name
           << "::setConnection(const TQT_DBusConnection& connection)" << endl;
    stream << "{" << endl;
    stream << "    m_baseProxy->setConnection(connection);" << endl;
    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeProxyMethod(const TQString& className,
        const Method& method, TQTextStream& stream)
{
    stream << "bool " << className << "::" << method.name
           << (method.async ? "Async(" : "(");

    TQValueList<Argument>::const_iterator it    = method.arguments.begin();
    TQValueList<Argument>::const_iterator endIt = method.arguments.end();
    for (; it != endIt; ++it)
    {
        if (!(*it).isPrimitive && (*it).direction == Argument::In)
            stream << "const ";

        stream << (*it).signature;

        if (!(*it).isPrimitive || (*it).direction == Argument::Out) stream << "&";

        stream << " " << (*it).name << ", ";
    }

    stream << "TQT_DBusError& error)" << endl;

    stream << "{" << endl;
    stream << "    TQValueList<TQT_DBusData> parameters;" << endl;
    stream << endl;

    uint outCount = 0;

    it = method.arguments.begin();
    for (; it != endIt; ++it)
    {
        if ((*it).direction == Argument::Out)
        {
            ++outCount;
            continue;
        }

        if (!(*it).annotatedType.isEmpty())
        {
            stream << "    TQT_DBusData " << (*it).name << "Data;" << endl;

            // TODO error handling
            stream << "    if (TQT_DBusDataConverter::convertToTQT_DBusData<"
                   << (*it).annotatedType << ">(" << (*it).name << ", "
                   << (*it).name << "Data"
                   << ") != TQT_DBusDataConverter::Success) return false;"
                   << endl;
            stream << "    parameters << " << (*it).name << "Data;" << endl;
        }
        else if (!(*it).accessor.isEmpty())
        {
            stream << "    parameters << TQT_DBusData::from" << (*it).accessor << "(";

            if ((*it).subAccessor.isEmpty())
                stream << (*it).name;
            else
                stream << (*it).containerClass << "(" << (*it).name << ")";

            stream << ");" << endl;
        }
        else
            stream << "    parameters << " << (*it).name << ";" << endl;
    }

    stream << endl;

    if (outCount == 0 && method.noReply)
    {
        stream << "    if (!m_baseProxy->send(\"" << method.name
               << "\", parameters))" << endl;
        stream << "    {" << endl;
        stream << "        error = m_baseProxy->lastError();" << endl;
        stream << "        return false;" << endl;
        stream << "    }" << endl;
        stream << "    return true;" << endl;
        stream << "}" << endl;
        stream << endl;
        return;
    }

    if (method.async)
    {
        stream << "    asyncCallId = m_baseProxy->sendWithAsyncReply(\"";
        stream << method.name << "\", parameters);" << endl;
        stream << endl;

        stream << "    if (asyncCallId != 0) m_asyncCalls[asyncCallId] = \""
               << method.name << "\";" << endl;
        stream << endl;

        stream << "    error = TQT_DBusError();";
        stream << endl;

        stream << "    return (asyncCallId != 0);" << endl;
        stream << "}" << endl;
        stream << endl;
        return;
    }

    stream << "    TQT_DBusMessage reply = m_baseProxy->sendWithReply(\"";
    stream << method.name << "\", parameters, &error);" << endl;
    stream << endl;

    stream << "    if (reply.type() != TQT_DBusMessage::ReplyMessage) return false;"
           << endl;

    if (outCount == 0)
    {
        stream << "    return true;" << endl;
        stream << "}" << endl;
        stream << endl;
        return;
    }

    stream << endl;

    // TODO: create error or use enum for return
    stream << "    if (reply.count() != " << outCount << ") return false;" << endl;
    stream << endl;

    bool firstAccessor    = true;
    bool firstSubAccessor = true;

    it = method.arguments.begin();
    for (; it != endIt; ++it)
    {
        if ((*it).direction == Argument::In) continue;

        --outCount;

        if (!(*it).annotatedType.isEmpty())
        {
            // TODO error handling
            stream << "    if (TQT_DBusDataConverter::convertFromTQT_DBusData<"
                   << (*it).annotatedType << ">(reply.front(), "
                   << (*it).name
                   << ") != TQT_DBusDataConverter::Success) return false;"
                   << endl;
        }
        else if (!(*it).accessor.isEmpty())
        {
            if (firstAccessor)
            {
                stream << "    bool ok = false;" << endl;
                stream << endl;
                firstAccessor = false;
            }

            if ((*it).subAccessor.isEmpty())
            {
                stream << "    " << (*it).name << " = reply.front().to"
                    << (*it).accessor << "(&ok);" << endl;
            }
            else
            {
                if (firstSubAccessor)
                {
                    stream << "    bool subOK = false;" << endl;
                    stream << endl;
                    firstSubAccessor = false;
                }

                stream << "    " << (*it).name << " = reply.front().to"
                    << (*it).accessor << "(&ok).to" << (*it).subAccessor
                    << "(&subOK);" << endl;

                // TODO: create error or use enum for return
                stream << "    if (!subOK) return false;" << endl;
            }

            // TODO: create error or use enum for return
            stream << "    if (!ok) return false;" << endl;
        }
        else
            stream << "    " << (*it).name << " = reply.front();" << endl;
        stream << endl;

        if (outCount > 0)
        {
            stream << "    reply.pop_front();" << endl;
            stream << endl;
        }
    }

    stream << "    return true;" << endl;

    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeProxyGenericProperty(const Class& classData,
        TQTextStream& stream)
{
    stream << "void " << classData.name
           << "::setDBusProperty(const TQString& name, "
           << "const TQT_DBusVariant& value, TQT_DBusError& error)"
           << endl;
    stream << "{" << endl;

    stream << "    TQT_DBusConnection connection = m_baseProxy->connection();" << endl;
    stream << endl;
    stream << "    TQT_DBusMessage message = TQT_DBusMessage::methodCall("
           << "m_baseProxy->service(), m_baseProxy->path(), "
           << "\"org.freedesktop.DBus.Properties\", \"Set\");" << endl;
    stream << endl;

    stream << "    message << TQT_DBusData::fromString(m_baseProxy->interface());"
           << endl;
    stream << "    message << TQT_DBusData::fromString(name);" << endl;
    stream << "    message << TQT_DBusData::fromVariant(value);" << endl;
    stream << endl;

    stream << "    connection.sendWithReply(message, &error);" << endl;

    stream << "}" << endl;

    stream << endl;

    stream << "TQT_DBusVariant " << classData.name
           << "::getDBusProperty(const TQString& name, TQT_DBusError& error) const"
           << endl;
    stream << "{" << endl;

    stream << "    TQT_DBusConnection connection = m_baseProxy->connection();" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage message = TQT_DBusMessage::methodCall("
           << "m_baseProxy->service(), m_baseProxy->path(), "
           << "\"org.freedesktop.DBus.Properties\", \"Get\");" << endl;
    stream << endl;

    stream << "    message << TQT_DBusData::fromString(m_baseProxy->interface());"
           << endl;
    stream << "    message << TQT_DBusData::fromString(name);" << endl;
    stream << endl;

    stream << "    TQT_DBusMessage reply = connection.sendWithReply(message, &error);"
           << endl;
    stream << endl;

    stream << "    if (reply.type() != TQT_DBusMessage::ReplyMessage)"
           << " return TQT_DBusVariant();" << endl;
    stream << "    if (reply.count() != 1) return TQT_DBusVariant();" << endl;

    stream << endl;

    stream << "    bool ok = false;" << endl;
    stream << "    TQT_DBusVariant value = reply.front().toVariant(&ok);" << endl;

    // TODO generate error
    stream << "    if (!ok) return TQT_DBusVariant();" << endl;
    stream << endl;

    stream << "    return value;" << endl;

    stream << "}" << endl;

    stream << endl;
}

void MethodGenerator::writeProxyProperty(const Class& classData,
        const Property& property, TQTextStream& stream)
{
    if (property.write)
    {
        stream << "void " << classData.name << "::set" << property.name << "(";

        if (!property.isPrimitive) stream << "const ";

        stream << property.signature;

        if (!property.isPrimitive) stream << "&";

        stream << " value, TQT_DBusError& error)" << endl;
        stream << "{" << endl;
        stream << "    TQT_DBusVariant variant;" << endl;

        if (!property.annotatedType.isEmpty())
        {
            // TODO: error handling
            stream << "    TQT_DBusDataConverter::convertToTQT_DBusData<"
                   << property.annotatedType << ">(value, variant.value);"
                   << endl;
        }
        else if (!property.accessor.isEmpty())
        {
            stream << "    variant.value = TQT_DBusData::from"
                   << property.accessor << "(";

            if (property.subAccessor.isEmpty())
                stream << "value";
            else
                stream << property.containerClass << "(value)";

            stream << ");" << endl;
        }
        else
            stream << "    variant.value = TQT_DBusData(value);" << endl;

        stream << "    variant.signature = \"" << property.dbusSignature << "\";"
               << endl;

        stream << endl;
        stream << "    setDBusProperty(\"" << property.name
               << "\", variant, error);" << endl;

        stream << "}" << endl;
        stream << endl;
    }

    if (property.read)
    {
        stream << property.signature << " " << classData.name
               << "::get" << property.name << "(TQT_DBusError& error) const" << endl;
        stream << "{" << endl;

        stream << "    TQT_DBusVariant variant = getDBusProperty(\""
               << property.name << "\", error);" << endl;
        stream << endl;
        stream << "    if (error.isValid()) return "
               << property.signature << "();" << endl;
        stream << endl;

        if (!property.annotatedType.isEmpty())
        {
            stream << "    " << property.signature << " result;" << endl;

            // TODO error handling
            stream << "    TQT_DBusDataConverter::convertFromTQT_DBusData<"
                   << property.annotatedType << ">(variant.value, result);"
                   << endl;
        }
        else if (!property.accessor.isEmpty())
        {
            stream << "    bool ok = false;" << endl;
            stream << endl;

            if (property.subAccessor.isEmpty())
            {
                stream << "    " << property.signature << " result = ";
                stream << " variant.value.to" << property.accessor
                       << "(&ok);" << endl;
            }
            else
            {
                stream << "    bool subOK = false;" << endl;
                stream << endl;

                stream << "    " << property.signature << " result = ";
                stream << " variant.value.to"
                       << property.accessor << "(&ok).to" << property.subAccessor
                       << "(&subOK);" << endl;

                // TODO: create error
                stream << "    if (!subOK) {}" << endl;
            }

            // TODO: create error
            stream << "    if (!ok) {}" << endl;
        }
        else
            stream << "    " << property.signature << " result = variant.value;";
        stream << endl;

        stream << "    return result;" << endl;
        stream << "}" << endl;
        stream << endl;
    }
}

void MethodGenerator::writeProxyAsyncReplyHandler(const Class& classData,
        TQTextStream& stream)
{
    stream << "void " << classData.name
           << "::slotHandleAsyncReply(int asyncCallId, const TQT_DBusMessage& message)" << endl;
    stream << "{" << endl;

    stream << "    TQMap<int, TQString>::iterator findIt = "
           << "m_asyncCalls.find(asyncCallId);" << endl;
    stream << "    if (findIt == m_asyncCalls.end()) return;" << endl;
    stream << endl;
    stream << "    const TQString signalName = findIt.data();" << endl;
    stream << "    m_asyncCalls.erase(findIt);" << endl;
    stream << endl;

    TQValueList<Method>::const_iterator it    = classData.asyncReplySignals.begin();
    TQValueList<Method>::const_iterator endIt = classData.asyncReplySignals.end();
    bool first = true;
    for (; it != endIt; ++it)
    {
        stream << "    ";

        if (!first)
            stream << "else ";
        else
            first = false;

        stream << "if (signalName == \"" << (*it).name << "\")" << endl;
        stream << "    {" << endl;

        // FIXME tricking writeVariables and writeSignalEmit into writing
        // the reply emit code by manipulating arguments and name
        stream << "        int _asyncCallId = asyncCallId;" << endl << endl;

        stream << "        if (message.type() == TQT_DBusMessage::ErrorMessage) {" << endl;
        stream << "            emit AsyncErrorResponseDetected(_asyncCallId, message.error());" << endl;
        stream << "        }" << endl << endl;

        Method signal = *it;
        signal.arguments.pop_front();

        writeVariables("        ", signal, stream);
        stream << endl;

        signal = *it;
        signal.name += "AsyncReply";

        writeSignalEmit(signal, stream);

        stream << "    }" << endl;
    }

    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeIntrospectionDataMethod(const Class& classData,
    TQTextStream& stream)
{
    stream << "void " << classData.name
           << "::buildIntrospectionData(TQDomElement& interfaceElement)" << endl;
    stream << "{" << endl;

    stream << "    interfaceElement.setAttribute(\"name\", \""
           << classData.dbusName << "\");" << endl;
    stream << endl;

    bool firstMethod   = true;
    bool firstArgument = true;

    TQValueList<Method>::const_iterator it    = classData.methods.begin();
    TQValueList<Method>::const_iterator endIt = classData.methods.end();
    for (; it != endIt; ++it)
    {
        if (firstMethod)
        {
            firstMethod = false;
            stream << "    TQDomDocument document = interfaceElement.ownerDocument();" << endl;
            stream << "    TQDomElement methodElement = document.createElement("
                   << "\"method\");" << endl;
        }
        else
        {
            stream << endl;
            stream << "    methodElement = document.createElement("
                   << "\"method\");" << endl;
        }

        writeMethodIntrospection(*it, firstArgument, stream);

        stream << "    interfaceElement.appendChild(methodElement);" << endl;
    }

    it    = classData.msignals.begin();
    endIt = classData.msignals.end();
    for (; it != endIt; ++it)
    {
        if (firstMethod)
        {
            firstMethod = false;
            stream << "    TQDomDocument document = interfaceElement.ownerDocument();" << endl;
            stream << endl;
            stream << "    TQDomElement methodElement = document.createElement("
                   << "\"signal\");" << endl;
        }
        else
        {
            stream << endl;
            stream << "    methodElement = document.createElement("
                   << "\"signal\");" << endl;
        }

        writeMethodIntrospection(*it, firstArgument, stream);

        stream << "    interfaceElement.appendChild(methodElement);" << endl;
    }

    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeNodePrivate(const Class& classData, TQTextStream& stream)
{
    stream << "class " << classData.name
           << "::Private : public org::freedesktop::DBus::Introspectable" << endl;
    stream << "{" << endl;
    stream << "public:" << endl;
    stream << "    virtual ~Private();" << endl;
    stream << endl;

    stream << "public:" << endl;
    stream << "    TQMap<TQString, TQT_DBusObjectBase*> interfaces;" << endl;
    stream << "    TQString introspectionData;" << endl;
    stream << endl;
    stream << "    TQT_DBusConnection connection;" << endl;
    stream << "    TQString objectPath;" << endl;
    stream << endl;

    stream << "protected:" << endl;
    stream << "    virtual bool Introspect(TQString& data, TQT_DBusError& error);"
           << endl;
    stream << endl;
    stream << "    virtual void handleMethodReply(const TQT_DBusMessage& reply);"
           << endl;

    stream << "private:" << endl;
    stream << "    void cacheIntrospectionData();" << endl;

    stream << "};" << endl;
    stream << endl;
}

void MethodGenerator::writeNodeBegin(const Class& classData, TQTextStream& stream)
{
    stream << classData.name << "::" << classData.name
           << "()  : TQT_DBusObjectBase()," << endl;
    stream << "    m_private(new Private())" << endl;
    stream << "{" << endl;
    stream << "}" << endl;
    stream << endl;

    stream << classData.name << "::~" << classData.name << "()" << endl;
    stream << "{" << endl;
    stream << "    unregisterObject();" << endl;
    stream << endl;
    stream << "    delete m_private;" << endl;
    stream << "}" << endl;
    stream << endl;
}

void MethodGenerator::writeNodeMethods(const Class& classData,
            const TQValueList<Class>& interfaces, TQTextStream& stream)
{
    writeNodeInitialization(classData, interfaces, stream);

    stream << "void " << classData.name << "::unregisterObject()" << endl;
    stream << "{" << endl;
    stream << "    if (m_private->objectPath.isEmpty()) return;" << endl;
    stream << endl;
    stream << "    m_private->connection.unregisterObject(m_private->objectPath);" << endl;
    stream << endl;
    stream << "    m_private->connection = TQT_DBusConnection();" << endl;
    stream << "    m_private->objectPath = TQString();" << endl;
    stream << "}" << endl;
    stream << endl;

    stream << "bool " << classData.name
           << "::handleMethodCall(const TQT_DBusMessage& message)" << endl;
    stream << "{" << endl;
    stream << "    TQMap<TQString, TQT_DBusObjectBase*>::iterator findIt = "
           << "m_private->interfaces.find(message.interface());" << endl;
    stream << "    if (findIt == m_private->interfaces.end()) return false;"
           << endl;
    stream << endl;
    stream << "    return delegateMethodCall(message, findIt.data());" << endl;
    stream << "}" << endl;
    stream << endl;

    stream << classData.name << "::Private::~Private()" << endl;
    stream << "{" << endl;
    stream << "    TQMap<TQString, TQT_DBusObjectBase*>::const_iterator it    = "
           << "interfaces.begin();" << endl;
    stream << "    TQMap<TQString, TQT_DBusObjectBase*>::const_iterator endIt = "
           << "interfaces.end();" << endl;
    stream << "    for (; it != endIt; ++it)" << endl;
    stream << "    {" << endl;
    stream << "        TQT_DBusObjectBase* interface = it.data();" << endl;
    stream << "        if (interface != this)" << endl;
    stream << "            delete interface;" << endl;
    stream << "    }" << endl;
    stream << "    interfaces.clear();" << endl;
    stream << "}" << endl;
    stream << endl;

    stream << "bool " << classData.name << "::Private"
           << "::Introspect(TQString& data, TQT_DBusError& error)" << endl;
    stream << "{" << endl;
    stream << "    Q_UNUSED(error);" << endl;
    stream << "    if (introspectionData.isEmpty()) cacheIntrospectionData();"
           << endl;
    stream << endl;
    stream << "    data = introspectionData;" << endl;
    stream << endl;
    stream << "    return true;" << endl;
    stream << "}" << endl;
    stream << endl;

    stream << "void " << classData.name << "::Private"
           << "::handleMethodReply(const TQT_DBusMessage& reply)" << endl;
    stream << "{" << endl;
    stream << "    connection.send(reply);" << endl;
    stream << "}" << endl;
    stream << endl;

    writeNodeIntrospection(classData, interfaces, stream);
}

// End of File
