#
# Copyright (C) 2008 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
# Copyright (C) 2008 Alp Toker <alp@atoker.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# aint with this library; see the file COPYING.LIB.  If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

package CodeGeneratorGObject;

# Default Licence Templates
my $licenceTemplate = << "EOF";
/*
 * Copyright (C) 2008 Luke Kenneth Casson Leighton <lkcl\@lkcl.net>
 * Copyright (C) 2008 Alp Toker <alp\@atoker.com>
 * Copyright (C) 2008 Apple Inc.
 */
EOF

my %implIncludes = ();
my %hdrIncludes = ();
my %hdrPropIncludes = ();
my %implTypedefs = ();

# Default constructor
sub new
{
    my $object = shift;
    my $reference = { };

    $codeGenerator = shift;
    $outputDir = shift;

    bless($reference, $object);
    return $reference;
}

sub finish
{
    my $object = shift;
}

# Params: 'domClass' struct
sub GenerateInterface
{
    my $object = shift;
    my $dataNode = shift;
    my $defines = shift;

    my $name = $dataNode->name;

    print "$name\n";

    $object->Generate($dataNode);

    # Write changes
    my $fname = "gdom_" . $name; #decamelize($name);
    $fname =~ s/_//g;
    $object->WriteData($fname);
}

# Params: 'idlDocument' struct
sub GenerateModule
{
    my $object = shift;
    my $dataNode = shift;

    $module = $dataNode->module;
}

sub ClsToGObjType
{
    my $className = shift;
    return uc(decamelize($className))
}

sub GetParentClassName
{
    my $dataNode = shift;

    # ?? que??
    #return $dataNode->extendedAttributes->{"LegacyParent"} if $dataNode->extendedAttributes->{"LegacyParent"};
    return "GdomDOMObject" if @{$dataNode->parents} eq 0;
    return "Gdom" . $codeGenerator->StripModule($dataNode->parents(0));
}
sub GetParentGObjType
{
    my $dataNode = shift;

    # ?? que??
    #return $dataNode->extendedAttributes->{"LegacyParent"} if $dataNode->extendedAttributes->{"LegacyParent"};
    return "GDOM_TYPE_DOM_OBJECT" if @{$dataNode->parents} eq 0;
    return "GDOM_TYPE_" . ClsToGObjType($codeGenerator->StripModule($dataNode->parents(0)));
}


# Algorithm taken from String::CamelCase 0.01
sub camelize
{
    my $s = shift;
    join('', map{ ucfirst $_ } split(/(?<=[A-Za-z])_(?=[A-Za-z])|\b/, $s));
}

# Algorithm taken from String::CamelCase 0.01
sub decamelize
{
    my $s = shift;
    $s =~ s{([^a-zA-Z]?)([A-Z]*)([A-Z])([a-z]?)}{
        my $fc = pos($s)==0;
        my ($p0,$p1,$p2,$p3) = ($1,lc$2,lc$3,$4);
        my $t = $p0 || $fc ? $p0 : '_';
        $t .= $p3 ? $p1 ? "${p1}_$p2$p3" : "$p2$p3" : "$p1$p2";
        $t;
    }ge;
    $s;
}

sub GetHdrName
{
        my $name = $codeGenerator->StripModule(shift);

    return "gdom$name";
}

sub GetClassName
{
        my $name = $codeGenerator->StripModule(shift);

    return "Gdom$name";
}

sub GetGIncludeHdr
{
    my $type = shift;
        my $name = GetHdrName($type);
        #my $name = camelize(GetClassName($type));

        return "" if $type eq "int";
        return "" if $type eq "long";
        return "" if $type eq "short";
        return "" if $type eq "char";
        return "" if $type eq "float";
        return "" if $type eq "double";
        return "" if $type eq "unsigned";
        return "" if $type eq "unsigned int";
        return "" if $type eq "unsigned long";
        return "" if $type eq "unsigned long long";
        return "" if $type eq "unsigned short";
        return "" if $type eq "DOMTimeStamp";
        return "Range.h" if $type eq "CompareHow";
        return "gdomCanvasRenderingContext2D.h" if $type eq "DOMObject";
        return "" if $type eq "unsigned char";
        return "" if $type eq "RGBColor"; # XXX TODO - DOM class wrapper
        return "glib-object.h" if $type eq "DOMString";
        return "gdomEventTargetNode.h" if $type eq "EventTarget";

        return "glib-object.h" if $type eq "float";

        return "glib-object.h" if $type eq "boolean";

        return "glib-object.h" if $type eq "void";

    return "$name.h";
}

# returns 1 if we need to create a gobject wrapper around the return result
sub IsGDOMClassType
{
    my $type = shift;
        my $name = GetClassName($type);

        return 0 if $type eq "DOMTimeStamp";
        return 0 if $type eq "DOMString";
        return 0 if $type eq "CompareHow";
        return 0 if $type eq "float";
        return 0 if $type eq "double";
        return 0 if $type eq "boolean";
        return 0 if $type eq "char";
        return 0 if $type eq "long";
        return 0 if $type eq "short";
        return 0 if $type eq "uchar";
        return 0 if $type eq "unsigned";
        return 0 if $type eq "int";
        return 0 if $type eq "unsigned int";
        return 0 if $type eq "unsigned long";
        return 0 if $type eq "unsigned long long";
        return 0 if $type eq "unsigned short";
        return 0 if $type eq "RGBColor"; # XXX TODO - DOM class wrapper

        return 0 if $type eq "void";

    return 1;
}

sub GetPropGType
{
    my $type = shift;
    my $name = GetClassName($type);

    return "string" if $type eq "DOMString";
    return "float" if $type eq "float";
    return "notyetsupported" if $type eq "CompareHow";
    return "double" if $type eq "double";
    return "boolean" if $type eq "boolean";
    return "char" if $type eq "char";
    return "long" if $type eq "long";
    return "int" if $type eq "short";
    return "uchar" if $type eq "uchar";
    return "uint" if $type eq "unsigned";
    return "int" if $type eq "int";
    return "uint" if $type eq "unsigned int";
    return "uint64" if $type eq "unsigned long long";
    return "ulong" if $type eq "unsigned long";
    return "ushort" if $type eq "unsigned short";
    return "ulong" if $type eq "RGBColor"; # XXX TODO - DOM class wrapper

    return "object";
}

sub GetGType
{
    my $type = shift;
        my $name = GetClassName($type);
        #my $name = camelize(GetClassName($type));

        return "GdomEventTargetNode *" if $type eq "EventTarget";
        return "const gchar *" if $type eq "DOMString";
        return "gfloat " if $type eq "float";
        return "WebCore::Range::CompareHow " if $type eq "CompareHow";
        return "gdouble " if $type eq "double";
        return "gboolean " if $type eq "boolean";
        return "gchar " if $type eq "char";
        return "glong " if $type eq "long";
        return "gshort " if $type eq "short";
        return "guchar " if $type eq "uchar";
        return "guint " if $type eq "unsigned";
        return "gint " if $type eq "int";
        return "guint " if $type eq "unsigned int";
        return "gulong " if $type eq "unsigned long";
        return "guint64 " if $type eq "unsigned long long";
        return "gushort " if $type eq "unsigned short";
        return "gulong" if $type eq "RGBColor"; # XXX TODO - DOM class wrapper

        return "void" if $type eq "void";

    return "$name *";
}

sub getIncHdr
{
    my $type = shift;

    my $includeHdr = GetGIncludeHdr($type);
    if ($includeHdr) {
        if ($type eq "EventTarget") {
            return "gdomEventTargetNode.h";
        }
        elsif ($type eq "DOMObject") {
            # XXX ???? wtf?? :) copied from jsgenerator.
            return "gdomCanvasRenderingContext2D.h";
        } else {
            return $includeHdr;
        }
    }
    return "";
}

sub addPropIncHdr
{
    my $type = shift;

    my $hdr = getIncHdr($type);
    if ($hdr ne "") {
        $hdrPropIncludes{$hdr} = 1;
        if (IsGDOMClassType($type)) {
            $hdrPropIncludes{"priv".$hdr} = 1;
            $hdrPropIncludes{"int".$hdr} = 1;
        }
    }

}

sub addIncHdr
{
    my $type = shift;

    my $hdr = getIncHdr($type);
    if ($hdr ne "") {
        $hdrIncludes{$hdr} = 1;
    }

}

sub addIncBody
{
    my $type = shift;

    my $hdr = getIncHdr($type);
    if ($hdr ne "") {
        $implIncludes{$hdr} = 1;
    }
}

sub GetPrivFn
{
    my $interfaceName = shift;
    my $lcase_ifacename = shift;
    my $res = shift;
    my $parm = shift;

    if ($interfaceName eq "EventTarget") {
        return "WebCore::EventTargetNode * $res = static_cast<WebCore::EventTargetNode*>(gdom_event_target_node_get_private(self));";
    }
    return "WebCore::${interfaceName} * $res = (${lcase_ifacename}_get_private($parm));";
}

sub skipPropTemporarily
{
    my $type = shift;
    my $prop_type = shift;

    # skip MediaList
    if ($prop_type eq "stylesheets::MediaList") {
        return 1;
    }

    #if ($prop_type eq "EventTarget") {
    #    return 1;
    #}

    # skip CSSVariablesDeclaration
    if ($prop_type eq "CSSVariablesDeclaration") {
        return 1;
    }

    # skip CSSMutableStyleDeclaration
    #if ($prop_type eq "CSSStyleDeclaration") {
    #    return 1;
    #}

    # skip CSSMutableStyleDeclaration
    #if ($prop_type eq "CSSMutableStyleDeclaration") {
    #    return 1;
    #}

    # skip EventListener
    if ($prop_type eq "EventListener") {
        return 1;
    }

    # skip DOMWindow
    if ($prop_type eq "DOMWindow") {
        return 1;
    }

    # skip Constructors (whatever they are) for now.
    if ($prop_type =~ /Constructor$/) {
        return 1;
    }

    # canvas2d....
    if ($prop_type eq "custom") {
        return 1;
    }

    if ($prop_type eq "Array") {
        return 1;
    }

    if ($prop_type eq "DOMTimeStamp") {
        return 1;
    }

    # TODO: SVG
    if ($codeGenerator->IsPodType($type) || $codeGenerator->IsSVGAnimatedType($type)) {
        return 1;
    }
    return 0;
}

sub UsesManualToJSImplementation
{
    my $type = shift;

    # skipped out CanvasPixelArray - see JSCanvasPixelArrayCustom.cpp for why.
    return 1 if $type eq "Node" or $type eq "Document" or $type eq "HTMLCollection" or $type eq "SVGPathSeg" or $type eq "StyleSheet" or $type eq "CSSRule" or $type eq "CSSValue" or $type eq "Event" or $type eq "Element" or $type eq "Text";
    return 0;
}


# Generate header and impl
sub Generate
{
    my $object = shift;
    my $dataNode = shift;

    my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
    my $hasRealParent = @{$dataNode->parents} > 0;
    my $hasParent = $hasLegacyParent || $hasRealParent;
    my $parentClassName = GetParentClassName($dataNode);
    my $parentGObjType = GetParentGObjType($dataNode);
    my $interfaceName = $dataNode->name;
    my $className = GetClassName($interfaceName);
    my $hdrName = GetHdrName($interfaceName);

    # Add the default impl header template
    @cPrefix = split("\r", $licenceTemplate);
    push(@cPrefix, "\n");

    $implIncludes{"GDOMBinding.h"} = 1;
    $implIncludes{"$hdrName.h"} = 1;
    $implIncludes{"priv${hdrName}.h"} = 1;

    my $parentInterfaceName = "DOMObject";
    my $parentGIfaceName = "GDOMObject";
    my $parentHdrName = "gdomDOMObject";
    if ($parentClassName ne "GObject")
    {
        $parentInterfaceName = substr($parentClassName, 4);
        if ($parentInterfaceName ne "DOMObject") {
            $parentGIfaceName = "GDOM".substr($parentClassName, 4);
            $parentHdrName = GetHdrName($parentInterfaceName);
        }
        else
        {
            $parentGIfaceName = "GDOMObject";
        }
        $implIncludes{"${parentHdrName}.h"} = 1;
        $implIncludes{"priv${parentHdrName}.h"} = 1;
        $hdrIncludes{"${parentHdrName}.h"} = 1;
        #$hdrIncludes{"priv${parentHdrName}.h"} = 0;
    }

    # TODO - make these only include where needed, likewise the
    # function declarations toGDOM, too
    $implIncludes{"Node.h"} = 1;
    $implIncludes{"Element.h"} = 1;
    $implIncludes{"CSSValue.h"} = 1;
    $implIncludes{"CSSRule.h"} = 1;
    $implIncludes{"HTMLCollection.h"} = 1;
    #$implIncludes{"SVGPathSeg.h"} = 1;
    $implIncludes{"StyleSheet.h"} = 1;
    $implIncludes{"Event.h"} = 1;
    $implIncludes{"CanvasPixelArray.h"} = 1;
    $implIncludes{"Text.h"} = 1;

    # XXX TODO: make this optional or in a header file.  or find a better way.
    my $hackDecls = << "EOF";
namespace WebCore {
    gpointer toGDOM(Node* node);
    gpointer toGDOM(CSSValue* node);
    gpointer toGDOM(CSSRule* node);
    gpointer toGDOM(Element* node);
    gpointer toGDOM(Document* node);
    gpointer toGDOM(HTMLCollection* node); 
    //gpointer toGDOM(SVGPathSeg* node); 
    gpointer toGDOM(StyleSheet* node); 
    gpointer toGDOM(EventTargetNode* node); 
    gpointer toGDOM(EventTarget* node); 
    gpointer toGDOM(Event* node); 
    gpointer toGDOM(CanvasPixelArray* node); 
    gpointer toGDOM(Text* node); 
} // namespace WebCore

EOF

    push(@cBody, $hackDecls);

    # XXX TODO: make this optional or in a header file.  or find a better way.
    my $stringConvert = << "EOF";
class GStringConvert {
    public:
    const gchar *m_str;
    GStringConvert(WebCore::String const& s) { m_str = g_strdup(s.utf8().data()); }
    GStringConvert(WebCore::KURL const& s) { m_str = g_strdup(s.string().utf8().data()); }
    GStringConvert(const KJS::UString & s) { m_str = g_strdup(s.UTF8String().c_str()); }
    GStringConvert(WebCore::AtomicString const&s) { m_str = g_strdup(s.string().utf8().data()); }
};

EOF

    push(@cBody, $stringConvert);

    push(@cBody, "namespace WebCore {\n");

    # Get correct pass/store types respecting PODType flag
    my $podType = $dataNode->extendedAttributes->{"PODType"};
    my $passType = $podType ? "JSSVGPODTypeWrapper<$podType>*" : "$interfaceName*";

    if ((!$hasParent or $dataNode->extendedAttributes->{"GenerateToJS"}) and !UsesManualToJSImplementation($interfaceName)) {
        #if ($podType) {
        #    push(@cBody, "KJS::JSValue* toJS(KJS::ExecState* exec, JSSVGPODTypeWrapper<$podType>* obj, SVGElement* context)\n");
        #} elsif (IsSVGTypeNeedingContextParameter($implClassName)) {
        #     push(@cBody, "KJS::JSValue* toJS(KJS::ExecState* exec, $passType obj, SVGElement* context)\n");
        #} else {
            push(@cBody, "gpointer toGDOM(WebCore::$passType obj)\n");
        #}

        push(@cBody, "{\n");
        #if ($podType) {
        #    push(@cBody, "    return cacheSVGDOMObject<JSSVGPODTypeWrapper<$podType>, $className, ${className}Prototype>(exec, obj, context);\n");
        #} elsif (IsSVGTypeNeedingContextParameter($implClassName)) {
        #    push(@cBody, "    return cacheSVGDOMObject<$implClassName, $className, ${className}Prototype>(exec, obj, context);\n");
        #} else {
            push(@cBody, "    return WebCore::cacheDOMObject<WebCore::$interfaceName, GDOM$interfaceName>(obj);\n");
        #}
        push(@cBody, "}\n");
    }

    push(@cBody, "} /* namespace WebCore */ \n");
    my $implContent = "";

    # XXX TODO push(@cBody, "using namespace WebCore;\n\n");
    push(@cBody, "extern \"C\" {\n\n");
    push(@cBody, "#define _G_GET_UTF8(str) (GStringConvert(str).m_str)\n");

    # Add the default header template
    @hPrefix = split("\r", $licenceTemplate);
        #push(@hPrefix, "\n");

    # Header guard
        my $guard = "__" . $className . "_H__";

    my $clsCaps = substr(ClsToGObjType($className), 5);
    my $lcase_ifacename = "gdom_" . decamelize($interfaceName);
    #my $parentClsCaps = "GDOM_" . ClsToGObjType($parentInterfaceName);

    @hPrefixGuard = << "EOF";
#ifndef $guard
#define $guard
EOF

    $implContent = << "EOF";
G_BEGIN_DECLS

EOF

    push(@hBodyPre, $implContent);

    $implContent = << "EOF";

#define GDOM_TYPE_${clsCaps}            (${lcase_ifacename}_get_type())
#define GDOM_${clsCaps}(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GDOM_TYPE_${clsCaps}, ${className}))
#define GDOM_${clsCaps}_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  GDOM_TYPE_${clsCaps}, $className)Class)
#define GDOM_IS_${clsCaps}(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GDOM_TYPE_${clsCaps}))
#define GDOM_IS_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  GDOM_TYPE_${clsCaps}))
#define GDOM_${clsCaps}_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  GDOM_TYPE_${clsCaps}, ${className}Class))

typedef struct _${className}Private ${className}Private;

struct _${className} {
    ${parentClassName} parent_instance;

    ${className}Private *priv;
};

struct _${className}Class {
    GObjectClass parent_class;
};

WEBKIT_GDOM_API GType
${lcase_ifacename}_get_type (void);

EOF

    push(@hBody, $implContent);

    $implContent = << "EOF";
#ifndef ${guard}_PROP_
#define ${guard}_PROP_
EOF

    push(@hBodyPropPre, $implContent);

    push(@hBodyProp, "G_BEGIN_DECLS\n");

    $hdrPropInclude{"glib-object.h"} = 1;
    $hdrPropInclude{"int${parentHdrName}.h"} = 1;
    $hdrPropInclude{"GDOMObject.h"} = 1;
    $hdrPropInclude{"${interfaceName}.h"} = 1;

    $implContent = << "EOF";
#ifndef ${guard}_INT_
#define ${guard}_INT_
#include "glib-object.h"
#include "int${parentHdrName}.h"
#include "GDOMObject.h"
#include "${interfaceName}.h"
EOF

    push(@hBodyInternal, $implContent);

     if ((!$hasParent or $dataNode->extendedAttributes->{"GenerateToJS"}) and !UsesManualToJSImplementation($interfaceName)) {
        $implContent = << "EOF";
namespace WebCore {
    gpointer toGDOM(${interfaceName}* node);
} /* namespace WebCore */ 
EOF
    push(@hBodyInternal, $implContent);
    }
    push(@hBodyInternal, "G_BEGIN_DECLS\n");

    #Class declaration
    if ($interfaceName eq "DOMWindow") {
        push(@hBodyInternal, "class GDOM$interfaceName {\n");
    } else {
        push(@hBodyInternal, "class GDOM$interfaceName : public $parentGIfaceName {\n");
        push(@hBodyInternal, "    typedef $parentGIfaceName Base;\n");
    }
    push(@hBodyInternal, "public:\n");

    push(@hBodyInternal, "    int get_gobj_type() { return GDOM_TYPE_${clsCaps}; }\n");

    my $needsSVGContext = 0;
    # Constructor
    push(@hBodyInternal, "    GDOM$interfaceName(PassRefPtr<WebCore::$interfaceName> impl" . ($needsSVGContext ? ", SVGElement* context" : "") . ");\n");

    # Destructor
    push(@hBodyInternal, "    virtual ~GDOM$interfaceName();\n") if (!$hasParent or $interfaceName eq "Document");

    if (!$hasParent || ($interfaceName eq "DOMWindow")) {
        #if ($podType) {
        #    push(@hBodyInternal, "    JSSVGPODTypeWrapper<$podType>* impl() const { return m_impl.get(); }\n");
        #    push(@hBodyInternal, "    SVGElement* context() const { return m_context.get(); }\n\n");
        #    push(@hBodyInternal, "private:\n");
        #    push(@hBodyInternal, "    RefPtr<SVGElement> m_context;\n");
        #    push(@hBodyInternal, "    RefPtr<JSSVGPODTypeWrapper<$podType> > m_impl;\n");
        #} elsif (IsSVGTypeNeedingContextParameter($implClassName)) {
        #    push(@hBodyInternal, "    $implClassName* impl() const { return m_impl.get(); }\n");
        #    push(@hBodyInternal, "    SVGElement* context() const { return m_context.get(); }\n\n");
        #    push(@hBodyInternal, "private:\n");
        #    push(@hBodyInternal, "    RefPtr<SVGElement> m_context;\n");
        #    push(@hBodyInternal, "    RefPtr<$implClassName > m_impl;\n");
        #} else {
            push(@hBodyInternal, "    WebCore::$interfaceName* impl() const { return m_impl.get(); }\n\n");
            push(@hBodyInternal, "private:\n");
            push(@hBodyInternal, "    RefPtr<WebCore::$interfaceName> m_impl;\n");
        #}
    } elsif ($dataNode->extendedAttributes->{"GenerateNativeConverter"}) {
        push(@hBodyInternal, "    WebCore::$interfaceName* impl() const\n");
        push(@hBodyInternal, "    {\n");
        push(@hBodyInternal, "        return static_cast<WebCore::$interfaceName*>(Base::impl());\n");
        push(@hBodyInternal, "    }\n");
    }
    push(@hBodyInternal, "};\n");

    # Constructor
    push(@cBody, "GDOM${interfaceName}::GDOM$interfaceName(PassRefPtr<WebCore::$interfaceName> impl" . ($needsSVGContext ? ", SVGElement* context" : "") . ")\n");
    if ($interfaceName eq "DOMWindow") {
        push(@cBody, "    :m_impl(impl)\n");
    } elsif ($hasParent) {
        push(@cBody, "    : $parentGIfaceName(impl" . ($parentNeedsSVGContext ? ", context" : "") . ")\n") if ($interfaceName ne "DOMWindow");
    } else {
        push(@cBody, "    : $parentGIfaceName()\n") ;
        push(@cBody, "    , m_context(context)\n") if $needsSVGContext;
        push(@cBody, "    , m_impl(impl)\n");
    }

    push(@cBody, "{\n");
    push(@cBody, "}\n\n");

    # Destructor
    if (!$hasParent) {
        push(@cBody, "GDOM${interfaceName}::~GDOM$interfaceName()\n");
        push(@cBody, "{\n");

        if ($interfaceName eq "Node") {
            push(@cBody, "    WebCore::GDOMScriptInterpreter::forgetDOMNodeForDocument(m_impl->document(), m_impl.get());\n");
        } else {
            #if ($podType) {
            #    my $animatedType = $implClassName;
            #    $animatedType =~ s/SVG/SVGAnimated/;

            #    # Special case for JSSVGNumber
            #    if ($codeGenerator->IsSVGAnimatedType($animatedType) and $podType ne "float") {
            #        push(@cBody, "    JSSVGDynamicPODTypeWrapperCache<$podType, $animatedType>::forgetWrapper(m_impl.get());\n");
            #    }
            #}
            push(@cBody, "    WebCore::GDOMScriptInterpreter::forgetDOMObject(m_impl.get());\n");
        }

        push(@cBody, "\n}\n\n");
    }

    # Document needs a special destructor because it's a special case for caching. It needs
    # its own special handling rather than relying on the caching that Node normally does.
    if ($interfaceName eq "Document") {
        push(@cBody, "GDOM${interfaceName}::~GDOM$interfaceName()\n");
        push(@cBody, "{\n    WebCore::GDOMScriptInterpreter::forgetDOMObject(static_cast<WebCore::${interfaceName}*>(impl()));\n}\n\n");
    }

    $implIncludes{"${parentHdrName}.h"} = 1;
    $implIncludes{"${interfaceName}.h"} = 1;
    $implIncludes{"GDOMObject.h"} = 1;

    $implTypedefs{"$className"} = 1;
    $implTypedefs{"${className}Class"} = 1;

    $implContent = << "EOF";
#include "config.h"
#include "glib-object.h"
#include <webkit/gdomdefines.h>
#include "DOMWindow.h" /* yukk: TODO - make this optional */
#include "Node.h" /* yukk: TODO - make this optional */
#include "NamedNodeMap.h" /* yukk: TODO - make this optional */
#include "CanvasPattern.h" /* yukk: TODO - make this optional */
#include "CanvasGradient.h" /* yukk: TODO - make this optional */
#include "CanvasStyle.h" /* yukk: TODO - make this optional */
#include "CanvasRenderingContext2D.h" /* yukk: TODO - make this optional */
#include "privgdomDOMObject.h" /* yukk: TODO - make this optional */
#include "gdomEventTargetNode.h" /* yukk: TODO - make this optional */
#include "privgdomEventTargetNode.h" /* yukk: TODO - make this optional on EventTarget*/
#include "${interfaceName}.h"
#include "${hdrName}.h"
#include "priv${hdrName}.h"
#include "int${hdrName}.h"
#include "${hdrName}props.h"
#include "CString.h"
#include "PlatformString.h"
#include "AtomicString.h"
#include "ustring.h"
#include "KURL.h"

#include "CanvasPixelArray.h"
#include "Event.h"
#include "Text.h"
#include "CSSValue.h"
#include "CSSRule.h"
#include "HTMLCollection.h"
#include "StyleSheet.h"
#include "Document.h"

EOF

    push(@cBodyPriv, $implContent);

    $implContent = << "EOF";

${hackDecls}

G_BEGIN_DECLS

${stringConvert}

G_DEFINE_TYPE(${className}, ${lcase_ifacename}, ${parentGObjType});

#define _G_GET_UTF8(str) (GStringConvert(str).m_str)

/*
struct _${className}Private {
    class WebCore::${interfaceName} *item;
};  

#define GDOM_${clsCaps}_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GDOM_TYPE_${clsCaps}, ${className}Private))
*/
G_END_DECLS
WebCore::${interfaceName} *
${lcase_ifacename}_get_private(${className} *request)
{
EOF

    push(@cBodyPriv, $implContent);

    if (($interfaceName eq "DOMWindow")) {
        $implContent = << "EOF";
        return NULL; /* XXX TODO */
EOF
    } else {
        $implContent = << "EOF";
    GDOM${interfaceName} *res;
    res = static_cast<GDOM${interfaceName}*>(gdom_dom_object_get_private(GDOM_DOM_OBJECT(request)));
    return static_cast<WebCore::${interfaceName}*>(res->impl());
EOF
    }
    push(@cBodyPriv, $implContent);

    # Properties
    my $functionNamePrefix = $lcase_ifacename; 
    SKIPPROP: foreach my $attribute (@{$dataNode->attributes}) {
        my $propName = decamelize($attribute->signature->name);
        my $prop_type = $attribute->signature->type;
        my $type = GetGType($prop_type);
        my $functionSig = "$className *thiz";
        my $custom = $attribute->signature->extendedAttributes->{"Custom"};

        if ($attribute->signature->extendedAttributes->{"CustomGetter"}) {
            next SKIPPROP;
        }
            
        $implIncludes{"CSSMutableStyleDeclaration.h"} = 1 if $type eq "CSSStyleDeclaration";

        if (skipPropTemporarily($type, $prop_type)) {
            next SKIPPROP;
        }
            
        addPropIncHdr($prop_type);

        #push(@hBodyProp, "WEBKIT_GDOM_API $type\n$functionNamePrefix" . "_prop_get_" . "$propName ($functionSig);\n\n");

        #push(@hBodyProp, "WEBKIT_GDOM_API void\n$functionNamePrefix" . "_prop_set_" . "$propName ($functionSig, $type" . "value);\n\n");
    }

    # Properties
    $implContent = << "EOF";
}

G_BEGIN_DECLS

enum {
    PROP_0,
EOF
    push(@cBodyPriv, $implContent);

    my @txt_install_props = ();
    my @txt_set_props = ();
    my @txt_get_props = ();

    my $priv_fn = GetPrivFn($interfaceName, ${lcase_ifacename}, "gself", "self");

    my $txt_get_prop = << "EOF";
static void ${lcase_ifacename}__get_property_fn(GObject * object, guint prop_id,
					 GValue * value,
					 GParamSpec * pspec)
{
	${className} *self = GDOM_${clsCaps}(object);
    $priv_fn
	switch (prop_id) {
EOF
    push(@txt_get_props, $txt_get_prop);

    my $txt_set_prop = << "EOF";
static void ${lcase_ifacename}__set_property_fn(GObject * object, guint prop_id,
					 const GValue * value,
					 GParamSpec * pspec)
{
	${className} *self = GDOM_${clsCaps}(object);
    $priv_fn
	switch (prop_id) {
EOF
    push(@txt_set_props, $txt_set_prop);

    SKIPENUM: foreach my $attribute (@{$dataNode->attributes}) {
        my $prop_name = $attribute->signature->name;
        my $setprop_name_fn = $codeGenerator->WK_ucfirst($prop_name);
        my $getprop_name_fn = $codeGenerator->WK_lcfirst($prop_name);

        my $propName = decamelize($prop_name);
        my $propCaps = uc($propName);
        my $prop_type = $attribute->signature->type;
        my $type = GetGType($prop_type);
        my ${propEnum} = "PROP_${propCaps}";
        my ${prop_gtype} = decamelize($prop_type);
        if ($prop_gtype eq "event_target") {
            $prop_gtype = "event_target_node";
        }
        my ${propGType} = uc($prop_gtype);
        my $lcase_gtypename = "gdom_" . $prop_gtype;

        my $custom = $attribute->signature->extendedAttributes->{"Custom"};

        if ($attribute->signature->extendedAttributes->{"CustomGetter"}) {
            next SKIPENUM;
        }
            
        $implIncludes{"CSSMutableStyleDeclaration.h"} = 1 if $type eq "CSSStyleDeclaration";
        if (skipPropTemporarily($type, $prop_type)) {
            next SKIPENUM;
        }
            
        push(@cBodyPriv, "    ${propEnum},\n");

        my $gtype = GetPropGType($prop_type);
        my $gparamflag = "G_PARAM_READABLE";
        my $writeable = $attribute->type !~ /^readonly/;
        my $const = "read-only ";
        if ($writeable && $custom) {
            $const = "read-only (due to custom functions needed in gdomwebkit)";
            next SKIPENUM;
        }
        if ($writeable && !$custom) {
            $gparamflag = "G_PARAM_READWRITE";
            $const = "read-write ";
        }

        my $_gtype = $gtype;
        if ($gtype eq "ushort") {
            $_gtype = "uint"; # yuk.
        }
        my $txt_install_prop = << "EOF";
	g_object_class_install_property (g_klass, ${propEnum}, 
			g_param_spec_${_gtype} ("${propName}", /* name */
				"${interfaceName}.${propName}", /* short description */
				"${const} ${type} ${interfaceName}.${propName}", /* longer */
EOF
        push(@txt_install_props, $txt_install_prop);
        $txt_install_prop = "/* TODO! $gtype */\n";

        if ($writeable) {

            my $ok = 0;
            my $convert_fn = "";
            if ($gtype eq "string") {
                $convert_fn = "WebCore::String";
                $ok = 1;
            }
            elsif ($gtype eq "boolean") {
                if (${interfaceName} ne "DOMWindow") { #XXX TODO: skip "putDirect" stuff - no idea what it's about yet
                    $ok = 1;
                }
            } elsif ($gtype eq "float") {
                $ok = 1;
            } elsif ($gtype eq "double") {
                if (${interfaceName} ne "DOMWindow") { #XXX TODO: skip "putDirect" stuff - no idea what it's about yet
                    $ok = 1;
                }
            } elsif ($gtype eq "uint64") {
                $ok = 1;
            } elsif ($gtype eq "ulong") {
                if (${interfaceName} ne "DOMWindow") { #XXX TODO: skip "putDirect" stuff - no idea what it's about yet
                    $ok = 1;
                }
            } elsif ($gtype eq "long") {
                if (${interfaceName} ne "DOMWindow") { #XXX TODO: skip "putDirect" stuff - no idea what it's about yet
                    $ok = 1;
                }
            } elsif ($gtype eq "uint") {
                $ok = 1;
            } elsif ($gtype eq "ushort") {
                $ok = 1;
            } elsif ($gtype eq "uchar") {
                $ok = 1;
            } elsif ($gtype eq "char") {
                $ok = 1;
            }
            # XXX TODO - skip Location
            if ($interfaceName eq "Location") {
                $ok = 0;
            }

            if ($ok) {
                push(@txt_set_props, "    case ${propEnum}:\n    {\n");
                push(@txt_set_props, "        WebCore::ExceptionCode ec = 0;\n") if @{$attribute->setterExceptions};
                push(@txt_set_props, "        gself->set${setprop_name_fn}(${convert_fn}(g_value_get_$gtype (value))");

                push(@txt_set_props, ", ec") if @{$attribute->setterExceptions};
                push(@txt_set_props, ");\n");

                push(@txt_set_props, "        break;\n    }\n");
            }
        }

        push(@txt_get_props, "    case ${propEnum}:\n    {\n");

        my $exception = "";
        if (@{$attribute->getterExceptions}) {
            $exception = "ec";
            push(@txt_get_props, "        WebCore::ExceptionCode ec = 0;\n");
        }

        my $convert_fn = "";
        my $post_convert_fn = "";
        my $done = 0;
        if ($gtype eq "string") {
            $convert_fn = "_G_GET_UTF8(";
            $post_convert_fn = ")";
        }
        elsif ($gtype eq "object") {
            #$convert_fn = "WTF::getPtr(${lcase_gtypename}_get_private(";
            #$convert_fn = GetPrivFn($interfaceName, ${lcase_gtypename}, "gself", "self");
            #$convert_fn = "${lcase_gtypename}_get_private(PassRefPtr<WebCore::${prop_type}>(";
            #$post_convert_fn = ")";
            my $domobjType = "GDOM".$prop_type;
            $txt_get_prop = << "EOF";
        PassRefPtr<WebCore::${prop_type}> ptr = gself->${getprop_name_fn}(${exception});
        g_value_set_object (value, WebCore::toGDOM(ptr.get()));
EOF
            push(@txt_get_props, $txt_get_prop);
            $done = 1;
        }

        if (!$done) {
            push(@txt_get_props, "        g_value_set_$_gtype (value, ${convert_fn}gself->${getprop_name_fn}(${exception})${post_convert_fn});\n");
        }

        push(@txt_get_props, "        break;\n    }\n");

        # is this _entirely_ necessary???
        # can it be done with generic g_param_spec_gtype
        # and getting a few things, ignoring the min-max for example?
        if ($gtype eq "int") {
            $txt_install_prop = << "EOF";
				G_MININT,        /* min */
				G_MAXINT, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "boolean") {
            $txt_install_prop = << "EOF";
                FALSE, /* default */
EOF
        } elsif ($gtype eq "float") {
            $txt_install_prop = << "EOF";
				G_MINFLOAT,        /* min */
				G_MAXFLOAT, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "double") {
            $txt_install_prop = << "EOF";
				G_MINDOUBLE,        /* min */
				G_MAXDOUBLE, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "uint64") {
            $txt_install_prop = << "EOF";
				0,        /* min */
				G_MAXUINT64, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "ulong") {
            $txt_install_prop = << "EOF";
				0,        /* min */
				G_MAXULONG, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "long") {
            $txt_install_prop = << "EOF";
				G_MINLONG,        /* min */
				G_MAXLONG, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "uint") {
            $txt_install_prop = << "EOF";
				0,        /* min */
				G_MAXUINT, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "ushort") {
            $txt_install_prop = << "EOF";
				0,        /* min */
				G_MAXUINT16, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "uchar") {
            $txt_install_prop = << "EOF";
				G_MININT8,        /* min */
				G_MAXINT8, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "char") {
            $txt_install_prop = << "EOF";
				0,        /* min */
				G_MAXUINT8, /* max */
				0,        /* default */
EOF
        } elsif ($gtype eq "string") {
            $txt_install_prop = << "EOF";
                "", /* default */
EOF
        } elsif ($gtype eq "object") {
            $txt_install_prop = << "EOF";
				GDOM_TYPE_${propGType}, /* gobject type */
EOF
        }
        push(@txt_install_props, $txt_install_prop);
        $txt_install_prop = << "EOF";
				(GParamFlags)${gparamflag}));                   

EOF
        push(@txt_install_props, $txt_install_prop);


    }

    push(@cBodyPriv, "};\n\n");


    $txt_get_prop = << "EOF";
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}
EOF
    push(@txt_get_props, $txt_get_prop);

    $txt_set_prop = << "EOF";
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}
EOF
    push(@txt_set_props, $txt_set_prop);

    my $txt_set_get_props = << "EOF";
	case PROP_AGE:
		self->age = g_value_get_int (value);
		break;

	case PROP_AGE:
		g_value_set_int(value, self->age);
		break;

        +   g_value_init(&params[0], G_TYPE_FROM_INSTANCE(object));
        +   g_value_set_instance(&params[0], (gpointer)object);

EOF


    $implContent = << "EOF";

static void ${lcase_ifacename}_finalize(GObject* object)
{
    /*${className}* request = GDOM_${clsCaps}(object);*/
    /*${className}Private* priv = request->priv;*/
    
    /*GObjectClass *parent_class = g_type_class_peek (g_type_peek_parent (${parentGObjType}));*/

    /* Free any memory or close any files */ 

    /*parent_class-> finalize (object);*/

    G_OBJECT_CLASS(${lcase_ifacename}_parent_class)->finalize(object);
}

@txt_set_props

@txt_get_props

static void ${lcase_ifacename}_class_init(${className}Class* requestClass)
{
	GObjectClass *g_klass = G_OBJECT_CLASS (requestClass);
    g_klass->finalize = ${lcase_ifacename}_finalize;
	g_klass->set_property = ${lcase_ifacename}__set_property_fn;
	g_klass->get_property = ${lcase_ifacename}__get_property_fn;
    
    @txt_install_props

    /*g_type_class_add_private(requestClass, sizeof(${className}Private));*/
}

static void ${lcase_ifacename}_init(${className}* request)
{
    /*${className}Private* priv = GDOM_${clsCaps}_GET_PRIVATE(request);
    request->priv = priv;*/
}   

EOF

    push(@cBodyPriv, $implContent);

    $implIncludes{"intgdom${interfaceName}.h"} = 1;
    $implContent = << "EOF";

#ifndef ${guard}_PRIV_
#define ${guard}_PRIV_
#include "glib-object.h"
#include "${parentHdrName}.h"
#include "GDOMObject.h"
/*#include "priv${parentHdrName}.h"*/
/*G_BEGIN_DECLS*/
/*class WebCore::${interfaceName};*/
#include "${interfaceName}.h"
EOF

    push(@hBodyPriv, $implContent);

    $implContent = << "EOF";
//namespace WebCore {

class GDOM${interfaceName} : public ${parentGIfaceName} {

public:
    GDOM${interfaceName}(WebCore::${interfaceName}* impl) : $parentGIfaceName}(impl)
    { impl->ref(); }
    ~GDOM${interfaceName}() { m_impl->deref(); }

    WebCore::${interfaceName}* impl() const { return m_impl; }


private:
    WebCore::${interfaceName} *m_impl;
};

EOF

        #push(@hBodyPriv, $implContent);

    $implContent = << "EOF";
//} // namespace WebCore

typedef struct _${className} ${className};

WebCore::${interfaceName} *
${lcase_ifacename}_get_private(${className} *request);

/*G_END_DECLS*/

#endif /* ${guard}_PRIV_ */

EOF

    #return reinterpret_cast<WebCore::${interfaceName}*>((WebCore::Node*)(res));
        push(@hBodyPriv, $implContent);

        #if ($paramIDLType eq "DOMObject") {
            # XXX ???? wtf?? :) copied from jsgenerator.
        #    $implIncludes{"JSCanvasRenderingContext2D.h"} = 1;
        #} else {
        #    $implIncludes{$includeHdr} = 1;
        #}
        $implIncludes{"${interfaceName}.h"} = 1;
        $implIncludes{"privgdomDOMObject.h"} = 1;

        LOOP: foreach my $function (@{$dataNode->functions}) {
            #my $functionName = decamelize($className) . "_" . decamelize($function->signature->name);
                my $fn_sig_name = $function->signature->name;
                my $fn_sig_type = $function->signature->type;
                my $functionName = "gdom_" . decamelize($interfaceName) . "_" . decamelize($fn_sig_name);
                my $returnType = GetGType($fn_sig_type);
                my $ret_is_cls_type = IsGDOMClassType($fn_sig_type);

                my $functionSig = "$className *thiz";

                my $call_impl_params = "";
                my $gdom_fn_prefix = "gdom_" . decamelize($fn_sig_type);

                if ($functionName eq "gdom_css_style_declaration_set_property") {
                    # reserved name clash in glib!
                    $functionName = "gdom_css_style_declaration_set_css_property";
                }
                # XXX skip CompareHow functions for now
                if ($functionName eq "gdom_range_compare_boundary_points") {
                    push(@hBody, "\n/* XXX TODO: skipping CompareHow function ${functionName} */\n\n");
                    push(@cBody, "\n/* XXX TODO: skipping CompareHow function ${functionName} */\n\n");
                    next LOOP;
                }

                # XXX skip SVGDocument functions for now
                if ($returnType eq "GdomSVGDocument *") {
                    push(@hBody, "\n/* XXX TODO: skipping SVGDocument function ${functionName} */\n\n");
                    push(@cBody, "\n/* XXX TODO: skipping SVGDocument function ${functionName} */\n\n");
                    next LOOP;
                }

                # XXX skip some custom functions for now
                my $is_custom_fn = $function->signature->extendedAttributes->{"Custom"};
                if ($functionName eq "gdom_html_collection_item" ||
                    $functionName eq "gdom_html_collection_named_item") {
                    $is_custom_fn = 0;
                }
                if ($is_custom_fn && 
                    $functionName ne "gdom_node_iterator_next_node" && 
                    $functionName ne "gdom_node_append_child" && 
                    $functionName ne "gdom_node_remove_child" && 
                    $functionName ne "gdom_node_insert_before" &&
                    $functionName ne "gdom_node_replace_child" &&
                    $functionName ne "gdom_tree_walker_parent_node" && 
                    $functionName ne "gdom_tree_walker_first_child" && 
                    $functionName ne "gdom_tree_walker_last_child" && 
                    $functionName ne "gdom_tree_walker_previous_sibling" && 
                    $functionName ne "gdom_tree_walker_next_sibling" && 
                    $functionName ne "gdom_tree_walker_previous_node" && 
                    $functionName ne "gdom_tree_walker_next_node" && 
                    $functionName ne "gdom_node_iterator_previous_node") {
                    push(@hBody, "\n/* XXX TODO: custom function ${functionName} */\n\n");
                    push(@cBody, "\n/* XXX TODO: custom function ${functionName} */\n\n");
                    next LOOP;
                }

                foreach my $param (@{$function->parameters}) {
                    my $paramIDLType = $param->type;
                    addIncBody($paramIDLType);
                    addIncHdr($paramIDLType);
                    my $paramType = GetGType($paramIDLType);
                    my $paramName = decamelize($param->name);

                    $functionSig .= ", $paramType$paramName";

                    my $param_is_cls_type = IsGDOMClassType($paramIDLType);
                    if ($param_is_cls_type) {
                        if ($paramIDLType ne "DOMObject" &&
                            $paramIDLType ne "EventTarget") {
                            $implIncludes{"intgdom${paramIDLType}.h"} = 1;
                            $implIncludes{"privgdom${paramIDLType}.h"} = 1;
                            $implIncludes{"${paramIDLType}.h"} = 1; # this isn't strictly necessary but one of the auto-generated files needed it so....
                        }
                    }
                    if ($param_is_cls_type || ($paramIDLType eq "DOMString")) {
                        $paramName = "_g_" . $paramName;
                    }
                    if ($call_impl_params) {
                        $call_impl_params .= ", $paramName";
                    }
                    else {
                        $call_impl_params = "$paramName";
                    }
                }

                addIncHdr($fn_sig_type);
                if ($ret_is_cls_type) {
                #    $implTypedefs{"Gdom${fn_sig_type}"} = 1;
                }

                if ($returnType ne "void") {

                    if ($ret_is_cls_type) {
                        if ($fn_sig_type eq "DOMObject") {
                            # XXX ???? wtf?? :) copied from jsgenerator.
                            $implIncludes{"gdomCanvasRenderingContext2D.h"} = 1;
                        } else {
                            if ($fn_sig_type ne "EventTarget") {
                                $implIncludes{"intgdom${fn_sig_type}.h"} = 1;
                                $implIncludes{"privgdom${fn_sig_type}.h"} = 1;
                                $implIncludes{"gdom${fn_sig_type}.h"} = 1;
                            }
                            $implIncludes{"${fn_sig_type}.h"} = 1;
                        }
                    }
                }

                # XXX skip custom functions for now
                # but skip from here to allow some headers to be created
                # for a successful compile.
                if ($is_custom_fn &&
                    $functionName ne "gdom_node_remove_child" && 
                    $functionName ne "gdom_node_insert_before" &&
                    $functionName ne "gdom_node_replace_child" &&
                    $functionName ne "gdom_node_append_child") {
                    push(@hBody, "\n/* XXX TODO: custom function ${functionName} */\n\n");
                    push(@cBody, "\n/* XXX TODO: custom function ${functionName} */\n\n");
                    next LOOP;
                }

                my $_returnType = $returnType;
                my $domobjType = "GDOM".$fn_sig_type;
                if ($fn_sig_type eq 'DOMObject' &&
                    $functionName eq 'gdom_html_canvas_element_get_context') {
                    # XXX HACK!
                    $_returnType = 'GdomCanvasRenderingContext *';
                    $domobjType = 'GDOMCanvasRenderingContext';
                    $gdom_fn_prefix = 'gdom_canvas_rendering_context';
                    next LOOP; # XXX - too involved for now.  skip it.
                    # must convert to a DOMObject (TODO: browse JS code to find out how)
                    # better: work out how to create a GDOMDomObject
                }
                     
                if ($fn_sig_type eq 'DOMObject' &&
                    $functionName eq 'gdom_document_get_css_canvas_context') {
                    # XXX HACK!
                    $_returnType = 'GdomCanvasRenderingContext2D *';
                    $domobjType = 'GDOMCanvasRenderingContext2D';
                    $gdom_fn_prefix = 'gdom_canvas_rendering_context2d';
                    next LOOP; # XXX - too involved for now.  skip it.
                    # must convert to a DOMObject (TODO: browse JS code to find out how)
                    # better: work out how to create a GDOMDomObject
                }
                     

                push(@hBody, "WEBKIT_GDOM_API $_returnType\n$functionName ($functionSig);\n\n");

                push(@cBody, "$_returnType\n$functionName ($functionSig)\n{\n");

                if ($returnType ne "void") {
                    # XXX TODO: return proper default result
                    push(@cBody, "    g_return_val_if_fail (thiz, 0);\n");
                } else {
                    push(@cBody, "    g_return_if_fail (thiz);\n");
                }

                push(@cBody, "    WebCore::${interfaceName} * item = (${lcase_ifacename}_get_private(thiz));\n");
                if ($returnType ne "void") {
                    # XXX TODO: return proper default result
                    push(@cBody, "    g_return_val_if_fail (item, 0);\n");
                } else {
                    push(@cBody, "    g_return_if_fail (item);\n");
                }

            foreach my $param (@{$function->parameters}) {
                    my $paramName = decamelize($param->name);
                    my $paramIDLType = $param->type;
                    my $paramTypeIsPrimitive = $codeGenerator->IsPrimitiveType($paramIDLType);
                    my $param_is_cls_type = IsGDOMClassType($paramIDLType);
                    addIncHdr($paramIDLType);
                    if ($param_is_cls_type) {
                    #    $implTypedefs{"Gdom${paramIDLType}"} = 1;
                    }
                    if (!$paramTypeIsPrimitive) {
                        if ($returnType ne "void") {
                            # XXX TODO: return proper default result
                            push(@cBody, "    g_return_val_if_fail ($paramName, 0);\n");
                        } else {
                            push(@cBody, "    g_return_if_fail ($paramName);\n");
                        }
                    }
                }

                $returnParamName = "";
                foreach my $param (@{$function->parameters}) {
                    my $paramIDLType = $param->type;
                    my $paramType = GetGType($paramIDLType);
                    my $paramName = decamelize($param->name);

                    my $param_is_cls_type = IsGDOMClassType($paramIDLType);
                    if ($paramIDLType eq "DOMString") {
                        push(@cBody, "    WebCore::String _g_${paramName} = WebCore::String($paramName);\n");
                    }
                    if ($param_is_cls_type) {
                        my $param_gdom_fn = "gdom_" . decamelize($paramIDLType);
                        #push(@cBody, "    WTF::PassRefPtr<WebCore::${paramIDLType}> * _g_${paramName} = WTF::RefPtr<${param_gdom_fn}_get_private($paramName)>;\n");
                        if ($paramIDLType eq "EventTarget") {
                            $implIncludes{"privgdomEventTargetNode.h"} = 1;
                            push(@cBody, "    WebCore::EventTargetNode * _g_${paramName} = static_cast<WebCore::EventTargetNode*>(gdom_event_target_node_get_private($paramName));\n");
                        } else {
                            push(@cBody, "    WebCore::${paramIDLType} * _g_${paramName} = (${param_gdom_fn}_get_private($paramName));\n");
                        }
                        if ($returnType ne "void") {
                            # XXX TODO: return proper default result
                            push(@cBody, "    g_return_val_if_fail (_g_${paramName}, 0);\n");
                        } else {
                            push(@cBody, "    g_return_if_fail (_g_${paramName});\n");
                        }

                    }
                    $returnParamName = "_g_".$paramName if $param->extendedAttributes->{"Return"};
                }

                my $assign = "";
                my $assign_pre = "";
                my $assign_post = "";
                $g_res = 0;
                if ($returnType ne "void" && !$is_custom_fn) {

                    if ($ret_is_cls_type) {
                        $assign = "PassRefPtr<WebCore::${fn_sig_type}> g_res = ";
                        $assign_pre = "WTF::getPtr(";
                        $assign_post = ")";
                    } else {
                        $assign = "${_returnType} res = ";
                    }
                }
                my $exceptions = "";
                if (@{$function->raisesExceptions}) {
                    push(@cBody, "    WebCore::ExceptionCode ec = 0;\n");
                    if (${call_impl_params} ne "") {
                        $exceptions = ", ec";
                    } else {
                        $exceptions = "ec";
                    }
                }

                # need this for including ... _something_ - for the class args
                #if ($ret_is_cls_type) {
                #    $implIncludes{"gdom${fn_sig_type}.h"} = 1;
                #}
                if ($functionName eq "gdom_node_append_child" ||
                    $functionName eq "gdom_node_insert_before" ||
                    $functionName eq "gdom_node_replace_child" ||
                    $functionName eq "gdom_node_remove_child") {
                    my $custom_gdom_node_append_child = << "EOF";
    bool ok = item->${fn_sig_name}(${call_impl_params}${exceptions});
    if (ok)
    {
        ${_returnType} res = static_cast<${_returnType}>(WebCore::toGDOM($returnParamName)); 
        return res;
    }
    return NULL;
EOF
                    push(@cBody, $custom_gdom_node_append_child);
                } elsif ($fn_sig_type eq "DOMObject" &&
                    $functionName ne 'gdom_document_get_css_canvas_context') {
                    push(@cBody, "    /* XXX TODO - get canvas object (item->${fn_sig_name}(${call_impl_params}${exceptions}));*/\n" );
                    
                } elsif ($fn_sig_type eq "DOMString") {
                    push(@cBody, "    ${assign}_G_GET_UTF8(item->${fn_sig_name}(${call_impl_params}${exceptions}));\n" );
                } else {
                    if ($g_res) {
                        $g_res = "${assign}${assign_pre}item->${fn_sig_name}(${call_impl_params}${exceptions}${assign_post})" ;
                    } else {
                        push(@cBody, "    ${assign}${assign_pre}item->${fn_sig_name}(${call_impl_params}${exceptions}${assign_post});\n" );
                    }
                }
                if ($returnType ne "void" && !$is_custom_fn) {

                    if ($fn_sig_type ne "DOMObject") {
                        if ($ret_is_cls_type) {
                            push(@cBody, "    ${_returnType} res = static_cast<${_returnType}>(WebCore::toGDOM(g_res.get())); \n");
                        }
                    }
                    if ($fn_sig_type eq "DOMObject") {
                        push(@cBody, "    return NULL; /* XXX TODO: return canvas object */\n");
                    } else {
                        push(@cBody, "    return res;\n");
                    }
                }
                push(@cBody, "\n");

                push(@cBody, "}\n\n");
        }

    push(@cBody, "} /* extern \"C\" */\n\n");
    push(@cBodyPriv, "} /* extern \"C\" */\n\n");

    push(@hBodyInternal, "G_END_DECLS\n\n");
    push(@hBodyInternal, "#endif /* ${guard}_INT_*/\n");

    push(@hBody, "G_END_DECLS\n\n");
    push(@hBody, "#endif /* $guard */\n");

    push(@hBodyProp, "G_END_DECLS\n\n");
    push(@hBodyProp, "#endif /* ${guard}_PROP_*/\n");
}

# Internal helper
sub WriteData
{
    my ($object, $name) = @_;


    # Write Internal header
    my $hdrFNameInternal = "$outputDir/int" . $name . ".h";
    open(INTHEADER, ">$hdrFNameInternal") or die "Couldn't open file $hdrFNameInternal";

    print INTHEADER @hPrefix;
    print INTHEADER "\n";
    print INTHEADER @hBodyInternal;
    close(INTHEADER);

    # Write Private header
    my $hdrFNamePriv = "$outputDir/priv" . $name . ".h";
    open(PRIVHEADER, ">$hdrFNamePriv") or die "Couldn't open file $hdrFNamePriv";

    print PRIVHEADER @hPrefix;
    print PRIVHEADER "\n";
    print PRIVHEADER @hBodyPriv;
    close(PRIVHEADER);

    # Write Private Implementation
    my $implFileNamePriv = "$outputDir/priv" . $name . ".cpp";
    open(PRIVIMPL, ">$implFileNamePriv") or die "Couldn't open file $implFileNamePriv";

    print PRIVIMPL @cPrefix;
    print PRIVIMPL "\n";
    print PRIVIMPL @cBodyPriv;

    close(PRIVIMPL);

    # Write properties header (avoids recursive nightmare)
    my $hdrPropFName = "$outputDir/" . $name . "props.h";
    open(PROPHEADER, ">$hdrPropFName") or die "Couldn't open file $hdrPropFName";

    $hdrPropIncludes{"glib-object.h"} = 1;

    print PROPHEADER @hPrefix;
    print PROPHEADER "/* recursive includes / defines issue forces separate header for properties.\nperhaps - better - all gdom gtk functions should be in here!*/\n";
    print PROPHEADER @hBodyPropPre;
    print PROPHEADER "#include <webkit/gdomdefines.h>\n";
    print PROPHEADER map { "#include \"$_\"\n" } sort keys(%hdrPropIncludes);
    print PROPHEADER "\n" if keys(%hdrPropIncludes);
    print PROPHEADER "\n";
    print PROPHEADER @hBodyProp;

    close(HEADER);

    # Write public header.
    my $hdrFName = "$outputDir/" . $name . ".h";
    open(HEADER, ">$hdrFName") or die "Couldn't open file $hdrFName";

    $hdrIncludes{"glib-object.h"} = 1;

    print HEADER @hPrefix;
    print HEADER @hPrefixGuard;
    print HEADER "#include <webkit/gdomdefines.h>\n";
    print HEADER map { "#include \"$_\"\n" } sort keys(%hdrIncludes);
    print HEADER "\n" if keys(%hdrIncludes);
    print HEADER "\n";
    print HEADER @hBodyPre;
    print HEADER map { "typedef struct _$_ $_;\n" } sort keys(%implTypedefs);
    print HEADER "\n" if keys(%implTypedefs);
    print HEADER @hBody;

    close(HEADER);

    # Write the implementation sources
    my $implFileName = "$outputDir/" . $name . ".cpp";
    open(IMPL, ">$implFileName") or die "Couldn't open file $implFileName";

    print IMPL @cPrefix;
    print IMPL "#include \"config.h\"\n\n";
    print IMPL "#include <wtf/GetPtr.h>\n";
    print IMPL "#include \"CString.h\"\n";
    print IMPL "#include \"PlatformString.h\"\n";
    print IMPL "#include \"AtomicString.h\"\n";
    print IMPL "#include \"ustring.h\"\n";
    print IMPL "#include \"KURL.h\"\n";
    print IMPL map { "#include \"$_\"\n" } sort keys(%implIncludes);
    print IMPL "\n" if keys(%implIncludes);
    print IMPL @cBody;

    close(IMPL);

    %implIncludes = ();
    %implTypedefs = ();
    %hdrIncludes = ();
    @hPrefix = ();
    @hBody = ();
    @hBodyPriv = ();
    @hBodyInternal = ();
    @hBodyProp = ();
    @hBodyPropPre = ();

    @cPrefix = ();
    @cBody = ();
    @cBodyPriv = ();
}

1;
