#!/usr/bin/perl

use strict;
use vars qw($group $name $out @includes $init $destroy %widgets);

sub usage
{
	print STDERR "$_[0]\n" if @_;
	print STDERR <<EOT;
Usage: $0 [options] [<widget spec> ...]
Generates a Qt designer-plugin for the widget set defined in widget spec
or STDIN if no filename is given

Options:
	-g <group>       default widget group name to display in designer
	                 (default: KDE)
	-n <plugin name> name of the plugin class to generate
	                 (default: <group>WidgetsPlugin)
	-o <filename>    write output to filename (default: STDOUT)
EOT
	exit 1;
}

sub warning($) { print STDERR "Warning: $_[0]\n" }

my ($class, %defs);
sub addwidget
{
	return if $class =~ /^(Includes|Init|Destroy)$/;
	$defs{IncludeFile} = lc "$class.h" unless exists $defs{IncludeFile};
	$defs{ImplClass} = $class unless exists $defs{ImplClass};
	$defs{Group} = $group unless exists $defs{Group};
	$defs{ToolTip} = $class unless exists $defs{ToolTip};
	$defs{WhatsThis} = $class unless exists $defs{Long};
	$defs{IconSet} = lc "$class.png" unless exists $defs{IconSet};
	$defs{ConstructorArgs} = '(parent, name)' unless exists $defs{ConstructorArgs};
	$defs{IsContainer} = $defs{IsContainer} ? 'true' : 'false';
	$widgets{$class} = { %defs };
}

$group = 'KDE';

while ($ARGV[0] =~ /^-/)
{
	my $opt = shift @ARGV;
	usage "missing parameter for $opt" unless @ARGV;
	if ($opt eq '-g') { $group = shift @ARGV }
	elsif ($opt eq '-n') { $name = shift @ARGV }
	elsif ($opt eq '-o') { $out = shift @ARGV }
	else { usage "Unknown option $opt" }
}

$name = "${group}WidgetsPlugin"	unless $name;
warning "classname changed to \"$name\""
	if $name =~ s/(^[^A-Za-z_]+|[^A-Za-z0-9_])/_/g;

while (<>)
{
	chomp;
	next if /^(#|\s*$)/;
	
	if (/^\s*\[([A-Za-z_][A-Za-z0-9_]*)\]\s*$/)
	{
		addwidget if $class;
		%defs = {};
		$class = $1;
		next;
	}
	elsif (/^\s*\[(.*)\]\s*$/) { die "Invalid class name \"$1\"" }
	die "Not in a widget definition" unless $class;
	if ($class eq 'Includes') { push @includes, $_ }
	elsif ($class eq 'Init') { $init .= "\n\t$_" }
	elsif ($class eq 'Destroy') { $destroy .= "\n\t$_" }
	elsif (/^\s*(IncludeFile|ImplClass|Group|ToolTip|WhatsThis|IconSet|ConstructorArgs|IsContainer)\s*=\s*(.*)\s*/)
	{
		$defs{$1} = $2;
	}
	else { die "Syntax error on line $." }
}
addwidget if $class;

warning "Nothing to do", exit 0 unless %widgets;

my @keys = sort keys %widgets;

if ($out) { open OUT, ">$out" or die "Can't open $out for writing" }
else { open OUT, ">&STDOUT" }

(my $scriptname = $0) =~ s|^.*/||;
print OUT <<EOT;
/*
 * This file was autogenerated by $scriptname. Any changes will be lost!
 */

#include <qwidgetplugin.h>
// for pixmap search
#include <kstandarddirs.h>

EOT

print OUT map { "#include \"$_\"\n" } @includes, map { $widgets{$_}->{IncludeFile} } @keys;

print OUT <<EOT;

class $name : public QWidgetPlugin
{
public:
	$name();
	virtual ~$name();

	virtual QStringList keys() const
	{
		QStringList result;
		for (WidgetInfos::ConstIterator it = m_widgets.begin(); it != m_widgets.end(); ++it)
			result << it.key();
		return result;
	}
	virtual QWidget *create(const QString &key, QWidget *parent = 0, const char *name = 0);
	virtual QIconSet iconSet(const QString &key) const
	{
		QString path = locate("data", "kdewidgets/pics/" + m_widgets[key].iconSet);
		return QIconSet(path);
	}
	virtual bool isContainer(const QString &key) const
	{
		return m_widgets[key].isContainer;
	}
EOT

print OUT map { <<EOT } qw(group includeFile toolTip whatsThis);
	virtual QString $_(const QString &key) const
	{
		return m_widgets[key].$_;
	}
EOT

print OUT <<EOT;

private:
	struct WidgetInfo
	{
		QString group;
		QString iconSet;
		QString includeFile;
		QString toolTip;
		QString whatsThis;
		bool isContainer;
	};
	typedef QMap<QString, WidgetInfo> WidgetInfos;
	WidgetInfos m_widgets;
};

${name}::$name()
{
	WidgetInfo widget;
EOT

print OUT map { my $w = $_; "\n", (map { my $attr = ucfirst $_; <<EOT } qw(group iconSet includeFile toolTip whatsThis)), <<EOT } @keys;
	widget.$_ = "$widgets{$w}->{$attr}";
EOT
	widget.isContainer = $widgets{$w}->{IsContainer};
	m_widgets.insert("$_", widget);
EOT

print OUT <<EOT;
$init
}

${name}::~$name()
{$destroy
}

QWidget *${name}::create(const QString &key, QWidget *parent, const char *name)
{
EOT

print OUT map { <<EOT } @keys;
	if (key == "$_")
		return new $widgets{$_}->{ImplClass}$widgets{$_}->{ConstructorArgs};
EOT

print OUT <<EOT;
	return 0;
}

Q_EXPORT_PLUGIN($name)
EOT

