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

#include <X11/Xlib.h>
#include <X11/extensions/sync.h>

int have_xsync_extension(Display *dpy)
{
    int event_base = 0;
    int error_base;
    int major = 3, minor = 1;

    if (!XSyncInitialize(dpy, &major, &minor))
        return 0;

    printf("XSync %d.%d\n", major, minor);

    if (!XSyncQueryExtension(dpy, &event_base, &error_base))
        return 0;

    return event_base;
}

void print_system_counters(Display *dpy)
{
    XSyncSystemCounter *counters;
    int ncounters;
    int i;
    uint64_t resolution;

    printf("System counters available:\n");
    counters = XSyncListSystemCounters(dpy, &ncounters);

    for (i = 0; i < ncounters; i++) {
        resolution = (uint64_t)counters[i].resolution.hi << 32 | counters[i].resolution.lo;
        printf("	%s (%ld): resolution %ld\n",
                counters[i].name,
                counters[i].counter,
                resolution);
    }

    XSyncFreeSystemCounterList(counters);
}

XSyncCounter get_idle_counter(Display *dpy)
{
    XSyncSystemCounter *counters;
    static XSyncCounter idle = 0;
    int ncounters;
    int i;

    if (idle != 0)
        return idle;

    counters = XSyncListSystemCounters(dpy, &ncounters);

    for (i = 0; i < ncounters; i++) {
        if (strcmp(counters[i].name, "IDLETIME") == 0)
        {
            idle = counters[i].counter;
            break;
        }
    }

    XSyncFreeSystemCounterList(counters);

    return idle;
}

XSyncCounter get_idle_counter_for_device(Display *dpy, int deviceid)
{
    XSyncSystemCounter *counters;
    XSyncCounter idle = 0;
    int ncounters;
    int i;
    char name[25];

    counters = XSyncListSystemCounters(dpy, &ncounters);

    sprintf(name, "DEVICEIDLETIME %d", deviceid);
    for (i = 0; i < ncounters; i++) {
        if (strcmp(counters[i].name, name) == 0)
        {
            idle = counters[i].counter;
            break;
        }
    }

    XSyncFreeSystemCounterList(counters);

    return idle;
}

void _watch_counter(Display *dpy, XSyncCounter counter,
                   int timeout, int delta, XSyncValueType mode,
                   XSyncTestType test)
{
    XSyncAlarmAttributes attr;
    unsigned long mask;
    XSyncValue _delta, _timeout;
    XSyncAlarm alarm;

    memset(&attr, 0, sizeof(attr));

    mask = XSyncCACounter | XSyncCAValueType | XSyncCAValue |
           XSyncCATestType | XSyncCADelta | XSyncCAEvents;

    XSyncIntToValue(&_delta, delta); /* delta value */
    XSyncIntToValue(&_timeout, timeout); /* ms */

    attr.events = True;
    attr.trigger.counter = counter;
    attr.trigger.value_type = mode;
    attr.trigger.test_type = test;
    attr.trigger.wait_value = _timeout;
    attr.delta = _delta;

    alarm = XSyncCreateAlarm(dpy, mask, &attr);
    printf("alarm is %ld\n", alarm);

}

void watch_counter_positive(Display *dpy, XSyncCounter counter,
                   int timeout, int delta)
{
    _watch_counter(dpy, counter, timeout, delta, XSyncAbsolute,
                   XSyncPositiveTransition);
}

void watch_counter_negative(Display *dpy, XSyncCounter counter,
                   int timeout, int delta)
{
    _watch_counter(dpy, counter, timeout, delta, XSyncAbsolute,
                   XSyncNegativeTransition);
}

void print_alarm(Display *dpy, XSyncAlarmNotifyEvent *event)
{
    uint64_t counter, alarm;

    counter = (uint64_t)XSyncValueHigh32(event->counter_value) << 32 | XSyncValueLow32(event->counter_value);
    alarm = (uint64_t)XSyncValueHigh32(event->alarm_value) << 32 | XSyncValueLow32(event->alarm_value);

    printf("AlarmNotify on %ld: counter %ld alarm %ld state %d\n", event->alarm,
            counter, alarm, event->state);
}

int main (int argc, char **argv)
{
    Display *dpy;
    int event_base = 0;
    XSyncCounter idle;
    int deviceid = 0;

    dpy = XOpenDisplay(NULL);
    assert(dpy);

    event_base = have_xsync_extension(dpy);
    if (!event_base) {
        printf ("Could not find XSYNC extension\n");
        return 1;
    }

    if (argc > 1)
        deviceid = atoi(argv[1]);

    print_system_counters(dpy);

    if (deviceid)
        idle = get_idle_counter_for_device(dpy, deviceid);
    else
        idle = get_idle_counter(dpy);

    /* 0.5 seconds idle */
    watch_counter_positive(dpy, idle, 500, 0);
    /* when we break idle */
    watch_counter_negative(dpy, idle, 500, 0);
    XFlush(dpy);

    while(1)
    {
        XEvent ev;
        XNextEvent(dpy, &ev);

        if (ev.type == event_base + XSyncAlarmNotify)
            print_alarm(dpy, (XSyncAlarmNotifyEvent*)&ev);
    }

    XCloseDisplay(dpy);

    return 0;
}
