#include <stdio.h>
#include <fcntl.h>
#include <Python.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBrules.h>
#include <gdk/gdkx.h>
#include <assert.h>

#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

#define MAX_COMPONENTS 400
#define XKB_XFREE86_RULES "/usr/X11R6/lib/X11/xkb/rules/xfree86"
#define XKB_SUN_RULES "/usr/X11R6/lib/X11/xkb/rules/sun"

static XkbRF_RulesPtr rules;

PyObject *list_rules ();
PyObject *set_rule (PyObject *, PyObject *);
PyObject * py_get_rulesbase ();

char * get_rulesbase ();

static PyMethodDef _xkbMethods[] = {
   { "list_rules", list_rules, 1 },
   { "set_rule", set_rule, 1 },
   { "get_rulesbase", py_get_rulesbase, 1 },
   { NULL, NULL }
};

char *
get_rulesbase ()
{
 char *rulesbase = XKB_XFREE86_RULES;
#ifdef __sparc__
 int fd;

 fd = open("/dev/kbd", O_RDONLY);
 if (fd >= 0) {
   rulesbase = XKB_SUN_RULES;
 }
#endif

 return rulesbase;
}

PyObject *
py_get_rulesbase ()
{
 return Py_BuildValue ("s", get_rulesbase ());
}

void
init_xkb ()
{
 char *lang;
 PyObject *m;
 m = Py_InitModule ("_xkb", _xkbMethods);

 lang = getenv ("LC_ALL");
 rules = XkbRF_Load (get_rulesbase (), (lang) ? lang : "C", True, True);
 if (!rules)
   Py_FatalError ("unable to load XKB rules database");

 if (PyErr_Occurred ())
   Py_FatalError ("can't initialize module _xkb");
}

PyObject *
set_rule (PyObject *self, PyObject *args)
{
 XkbRF_VarDefsRec defs;
 XkbComponentNamesRec rnames;

 if (!PyArg_ParseTuple (args, "ssss", &defs.model, &defs.layout, &defs.variant, &defs.options))
   return NULL;

 if (!strcmp (defs.model, ""))
   defs.model = NULL;
 if (!strcmp (defs.layout, ""))
   defs.layout = NULL;
 if (!strcmp (defs.variant, ""))
   defs.variant = NULL;
 if (!strcmp (defs.options, ""))
   defs.options = NULL;

 XkbRF_GetComponents (rules, &defs, &rnames);

 XkbGetKeyboardByName (GDK_DISPLAY (), XkbUseCoreKbd, &rnames,
                       XkbGBN_AllComponentsMask,
                       XkbGBN_AllComponentsMask, True);

 XkbRF_SetNamesProp (GDK_DISPLAY (), get_rulesbase (), &defs);

 return Py_BuildValue ("i", 1);
}

PyObject *
list_rules ()
{
 PyObject *models, *layouts, *variants, *options, *py_rules;
 int num_comp;
 int i;

 models = PyDict_New ();
 num_comp = min (rules->models.num_desc, MAX_COMPONENTS);
 for (i = 0; i < num_comp; i++)
   {
     PyObject *desc, *name;

     name = PyString_FromString (rules->models.desc[i].name);
     desc = PyString_FromString (rules->models.desc[i].desc);
     PyDict_SetItem (models, name, desc);
   }

 layouts = PyDict_New ();
 num_comp = min (rules->layouts.num_desc, MAX_COMPONENTS);
 for (i = 0; i < num_comp; i++)
   {
     PyObject *desc, *name;

     name = PyString_FromString (rules->layouts.desc[i].name);
     desc = PyString_FromString (rules->layouts.desc[i].desc);
     PyDict_SetItem (layouts, name, desc);
   }

 variants = PyDict_New ();
 num_comp = min (rules->variants.num_desc, MAX_COMPONENTS);
 for (i = 0; i < num_comp; i++)
   {
     PyObject *desc, *name;

     name = PyString_FromString (rules->variants.desc[i].name);
     desc = PyString_FromString (rules->variants.desc[i].desc);
     PyDict_SetItem (variants, name, desc);
   }

 options = PyDict_New ();
 num_comp = min (rules->options.num_desc, MAX_COMPONENTS);
 for (i = 0; i < num_comp; i++)
   {
     PyObject *desc, *name;

     name = PyString_FromString (rules->options.desc[i].name);
     desc = PyString_FromString (rules->options.desc[i].desc);
     PyDict_SetItem (options, name, desc);
   }

 py_rules = PyTuple_New (4);

 PyTuple_SET_ITEM (py_rules, 0, models);
 PyTuple_SET_ITEM (py_rules, 1, layouts);
 PyTuple_SET_ITEM (py_rules, 2, variants);
 PyTuple_SET_ITEM (py_rules, 3, options);

 return py_rules;
}

int main (int argc, char **argv)
{
 int major, minor, event, error, reason;
 int max;
 int i;
 Display *dpy;
 XkbComponentNamesRec ptrns = { NULL, NULL, NULL, NULL, NULL, "*" };
 XkbComponentListPtr comps;
 XkbDescPtr xkb;


 major = XkbMajorVersion;
 minor = XkbMinorVersion;
/*    if (!XkbQueryExtension(dpy, &op, &event, &error, &major, &minor))  */
/*      { */
/*        fprintf (stderr, "no xkb\n"); */
/*      } */

 dpy = XkbOpenDisplay (NULL, &event, &error, &major, &minor, &reason);
 assert (dpy);

 max = MAX_COMPONENTS;
 comps = XkbListComponents (dpy, XkbUseCoreKbd, &ptrns, &max);
 assert (comps);
/*    for (i = 0; i < comps->num_geometry; i++) */
/*    { */
/*      printf ("%s\n", comps->geometry[i].name); */
/*    } */


 list_rules ();

/*    xkb = XkbGetKeyboard (dpy, XkbAllComponentsMask, XkbUseCoreKbd); */
/*    xkb->names = NULL; */
/*    XkbGetNames (dpy, XkbGeometryNameMask, xkb); */
/*    printf ("%s\n", XkbAtomText (dpy, xkb->names->geometry, 3)); */

 return 0;
}