#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>
#include <gmodule.h>

#include <clutter/clutter.h>
#include <X11/Xlib.h>

Display *dpy;

typedef struct {
    GSource source;
    Display *dpy;
    GPollFD event_poll_fd;
} X11Source;

static gboolean
handle_xevent(GSource * source)
{
    XEvent ev;
    do {
        XNextEvent(dpy, &ev);
        switch (ev.type) {
        case Expose:
	    printf("Expose: %d,%d,%d,%d\n", ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height);
            break;
        case ConfigureNotify:
	    printf("ConfigureNotify: %d,%d\n", ev.xconfigure.width, ev.xconfigure.height);
            break;
        case KeyPress:
	    printf("KeyPress\n");
            break;
        case KeyRelease:
	    printf("KeyRelease\n");
            break;
        case ButtonPress:
            printf("ButtonPress: %d,%d\n", ev.xbutton.x, ev.xbutton.y);
            break;
        case ButtonRelease:
            printf("ButtonRelease: %d,%d\n", ev.xbutton.x, ev.xbutton.y);
            break;
        case MotionNotify:
            printf("MotionNotify: %d,%d\n", ev.xmotion.x, ev.xmotion.y);
            break;
        default:
	    printf("Unknown x event: %d\n", ev.type);
            break;
        }
    } while (XPending(dpy));
    return TRUE;
}

static gboolean
x11_prepare(GSource *source, gint *timeout)
{
    X11Source *x11_source = (X11Source *)source;
    *timeout = -1;
    return XPending(x11_source->dpy);
}

static gboolean
x11_check(GSource *source)
{
    X11Source *x11_source = (X11Source *)source;
    if (x11_source->event_poll_fd.revents & G_IO_IN) {
        return XPending(x11_source->dpy);
    }
    return FALSE;
}

static gboolean
x11_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    handle_xevent(source);
    return True;
}

static GSourceFuncs x11_funcs = {
    x11_prepare,
    x11_check,
    x11_dispatch
};


int
main (int argc, char *argv[])
{
    int x = 0;
    int y = 0;
    int w = 500;
    int h = 500;
    ClutterTimeline  *timeline;
    ClutterAlpha     *alpha;
    ClutterBehaviour *r_behave;
    ClutterActor     *stage;
    ClutterActor     *hand, *hand2, *label;
    ClutterColor      stage_color = { 0xcc, 0xcc, 0xcc, 0xff };

    dpy = XOpenDisplay(NULL);
    if (!dpy) {
	fprintf(stderr, "XOpenDisplay failed\n");
	return 1;
    }

    Window root = DefaultRootWindow(dpy);
    XSetWindowAttributes attr;
    attr.backing_store = NotUseful;
    attr.colormap = DefaultColormap(dpy, DefaultScreen(dpy));
    attr.border_pixel = 0;
    attr.background_pixmap = None;
    attr.background_pixel = 0;
    attr.event_mask = 0;
    attr.bit_gravity = ForgetGravity;
    int default_depth = DefaultDepth(dpy, DefaultScreen(dpy));
    Visual *vi = DefaultVisual(dpy, DefaultScreen(dpy));
    int win_mask = 0;
    Window win = XCreateWindow(dpy, root, x, y, w, h, 0, default_depth, InputOutput, vi, 0, &attr);
    XMapWindow(dpy,root);

    GSource *source = g_source_new(&x11_funcs, sizeof(X11Source));
    X11Source *x11_source = (X11Source *)source;
    x11_source->dpy = dpy;
    g_source_set_priority(source, G_PRIORITY_DEFAULT);
    x11_source->event_poll_fd.fd = ConnectionNumber(dpy);
    x11_source->event_poll_fd.events = G_IO_IN;
    g_source_add_poll(source, &x11_source->event_poll_fd);
    g_source_set_can_recurse(source, TRUE);
    g_source_attach(source, NULL);

    // clutter
    clutter_x11_set_display(dpy);
    clutter_x11_disable_event_retrieval ();

    clutter_init (&argc, &argv);

    /* Make a hand */
    GError *err = NULL;
    hand = clutter_texture_new_from_file ("redhand.png", &err);
    if (!hand) {
	g_print("%s\n", err->message);
	g_error("pixbuf load failed");
    }

    stage = clutter_stage_new();
    clutter_x11_get_stage_visual(stage);
    clutter_x11_set_stage_foreign(stage, win);

    clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);

    hand = clutter_texture_new_from_file ("redhand2.png", &err);
    if (!hand) {
	g_print("%s\n", err->message);
	g_error("pixbuf load failed. hand2");
    }


    clutter_actor_set_position (hand, 240, 140);
    clutter_actor_show (hand);
    clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand);

    /* Make a timeline */
    timeline = clutter_timeline_new (7692); /* num frames, fps */
    clutter_timeline_set_loop (timeline, TRUE);

    /* Set an alpha func to power behaviour */
    alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR);

    /* Create a behaviour for that alpha */
    r_behave = clutter_behaviour_rotate_new (alpha,
	    CLUTTER_Z_AXIS,
	    CLUTTER_ROTATE_CW,
	    0.0, 360.0); 

    clutter_behaviour_rotate_set_center (CLUTTER_BEHAVIOUR_ROTATE (r_behave),
	    86, 125, 0);
  
    clutter_behaviour_apply (r_behave, hand);

    /* start the timeline and thus the animations */
    clutter_timeline_start (timeline);

    clutter_actor_show_all (stage);

    GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_ref(loop);
    g_main_loop_run(loop);
    g_main_loop_unref(loop);

    g_object_unref (r_behave);

    return 0;
}
