#!/usr/bin/python -t

import re, os, string, sys
from getopt import getopt, GetoptError

class UsageError(SystemExit):
    def __init__(self, details):
        SystemExit.__init__(self, 2)
        appname = os.path.basename(sys.argv[0])
        print >>sys.stderr, '%s: %s' % (appname, details)
        print >>sys.stderr, 'Try `%s --help\' for more information.' % appname

def print_usage():
    print '''\
Usage: %s [OPTIONS] NAMESPACE LOCALNAME [TypeName]
Generates the header file for TypeName.

NAMESPACE  - namespace of the type, for instance 'GTK'
LOCAL_NAME - local name of the type, for instance 'TREE_VIEW'
TypeName   - optionally the type name, for instance 'GtkTreeView

Options:
  -b, --base=TYPE  Base type of the object. Default: GObject.
  -m, --mode=MODE  Operation mode: header or body. Default: header.
''' % os.path.basename(sys.argv[0])

def parse_type(typename):
    namespace, localname = re.match(r'^([A-Z]+[a-z]*)([A-Z][a-z].*)', typename).groups()
    localname = re.sub(r'([a-z])([A-Z])', lambda m: '_'.join(m.groups()), localname)
    return namespace.upper(), localname.upper()

opts, args = None, None

try:
    opts, args = getopt(sys.argv[1:], '?hm:b:', ('help', 'mode=', 'base='))

except GetoptError, e:
    raise UsageError, e

base = 'GObject'
mode = 'header'

for key, value in opts:
    if key in ('-b', '--base'):
        base = value
        continue

    if key in ('-m', '--mode'):
        mode = value
        continue

    if key in ('-?', '-h', '--help'):
        print_usage()
        raise SystemExit

if len(args) < 2 or len(args) > 3:
    raise UsageError, 'arguments missing'

namespace, localname = map(string.upper, args[0:2])
fnprefix = '_'.join((namespace, localname)).lower()
fnspaces = ' ' * len(fnprefix)

typename = (
    len(args) > 2 and args[2] or
    re.subn('(^|_)(\w)', lambda m: m.group(2).upper(), fnprefix)[0])
filename = typename.replace('_', '-')

base_namespace, base_localname = parse_type(base)

templates = {
    'header': '''\
#ifndef __%(namespace)s_%(localname)s_H__
#define __%(namespace)s_%(localname)s_H__

#include <glib-object.h>

G_BEGIN_DECLS

#define %(namespace)s_TYPE_%(localname)s           (%(fnprefix)s_get_type())
#define %(namespace)s_%(localname)s(obj)           (G_TYPE_CHECK_INSTANCE_CAST(obj, %(namespace)s_TYPE_%(localname)s, %(typename)s))
#define %(namespace)s_%(localname)s_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST(cls, %(namespace)s_TYPE_%(localname)s, %(typename)sClass))
#define %(namespace)s_IS_%(localname)s(obj)        (G_TYPE_CHECK_INSTANCE_TYPE(obj, %(namespace)s_TYPE_%(localname)s))
#define %(namespace)s_IS_%(localname)s_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE(obj, %(namespace)s_TYPE_%(localname)s))
#define %(namespace)s_%(localname)s_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), %(namespace)s_TYPE_%(localname)s, %(typename)sClass))

typedef struct _%(typename)s        %(typename)s;
typedef struct _%(typename)sClass   %(typename)sClass;
typedef struct _%(typename)sPrivate %(typename)sPrivate;

struct _%(typename)s
{
  %(base)s parent_instance;
  %(typename)sPrivate *priv;
};

struct _%(typename)sClass
{
  %(base)sClass parent_class;
};

GType %(fnprefix)s_get_type (void) G_GNUC_CONST;

G_END_DECLS

#endif /* __%(namespace)s_%(localname)s_H__ */ ''',

    'body': '''\
#include "%(filename)s.h"

enum
{
  PROP_NONE
  /* TODO: fill in properties */
};

struct _%(typename)sPrivate
{
  /* TODO: fill in private members */
};

G_DEFINE_TYPE (%(typename)s, %(fnprefix)s, %(base_namespace)s_TYPE_%(base_localname)s);

static void
%(fnprefix)s_init (%(typename)s *self)
{
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
                                            %(namespace)s_TYPE_%(localname)s,
                                            %(typename)sPrivate);
}

#if 0
static void
%(fnprefix)s_set_property (GObject      *object,
%(fnspaces)s               guint         prop_id,
%(fnspaces)s               const GValue *value,
%(fnspaces)s               GParamSpec   *pspec)
{
  %(typename)s *self = %(namespace)s_%(localname)s (object);

  switch (prop_id)
    {
      /* TODO: handle properties */

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
%(fnprefix)s_get_property (GObject    *object,
%(fnspaces)s               guint       prop_id,
%(fnspaces)s               GValue     *value,
%(fnspaces)s               GParamSpec *pspec)
{
  %(typename)s *self = %(namespace)s_%(localname)s (object);

  switch (prop_id)
    {
      /* TODO: handle properties */

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
%(fnprefix)s_dispose (GObject *object)
{
  %(typename)s *self = %(namespace)s_%(localname)s (object);

  /* TODO: release resources */

  G_OBJECT_CLASS (%(fnprefix)s_parent_class)->dispose (object);
}
#endif

static void
%(fnprefix)s_class_init (%(typename)sClass *cls)
{
#if 0
  GObjectClass *oclass = G_OBJECT_CLASS (cls);

  oclass->set_property = %(fnprefix)s_set_property;
  oclass->get_property = %(fnprefix)s_get_property;
  oclass->dispose = %(fnprefix)s_dispose;
#endif

  g_type_class_add_private (cls, sizeof (%(typename)sPrivate));
}

%(typename)s*
%(fnprefix)s_new (void)
{
  return g_object_new (%(namespace)s_TYPE_%(localname)s, NULL);
}
'''
}

print templates[mode] % vars()
