/*
 * xsmp.c
 *
 *  Created on: Oct 30, 2008
 *      Author: jos
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <glib.h>

#include "xsmp.h"

static void xsmp_save_yourself(SmcConn smc_conn, SmPointer client_data, int save_type,
        Bool shutdown, int interact_style, Bool fast) {
    fprintf(stderr, "save yourself!\n");
    SmcSaveYourselfDone(smc_conn, True);
}

static void xsmp_save_complete(SmcConn smc_conn, SmPointer client_data) {
    fprintf(stderr, "save complete!\n");
}

static void xsmp_die(SmcConn smc_conn, SmPointer client_data) {
    fprintf(stderr, "die!\n");
    SmcCloseConnection(smc_conn, 0, NULL);
    exit(0);
}

static void xsmp_shutdown_cancelled(SmcConn smc_conn, SmPointer client_data) {
    fprintf(stderr, "shutdown cancelled!\n");
}

static void smc_error_handler(SmcConn smc_conn, Bool swap, int offending_minor_opcode,
        unsigned long offending_sequence, int error_class, int severity, SmPointer values) {
    fprintf(stderr, "smc error!\n");
}

static void sm_client_xsmp_disconnect(XSMPConnection *xsmp) {
    SmcConn connection;

    if (!xsmp->connection)
        return;

    fprintf(stderr, "Disconnecting\n");

    connection = xsmp->connection;
    xsmp->connection = NULL;
    SmcCloseConnection(connection, 0, NULL);
    xsmp->state = XSMP_STATE_CONNECTION_CLOSED;

}

static void ice_error_handler(IceConn ice_conn, Bool swap, int offending_minor_opcode,
        unsigned long offending_sequence, int error_class, int severity, IcePointer values);
static void ice_io_error_handler(IceConn ice_conn);
static void ice_connection_watch(IceConn ice_conn, IcePointer client_data, Bool opening,
        IcePointer *watch_data);

static void ice_error_handler(IceConn ice_conn, Bool swap, int offending_minor_opcode,
        unsigned long offending_sequence, int error_class, int severity, IcePointer values) {
    fprintf(stderr, "ICE error\n");
}

static void ice_io_error_handler(IceConn ice_conn) {
    fprintf(stderr, "ICE io error\n");
}

static int process_ice_messages(IceConn ice_conn) {
    IceProcessMessagesStatus status;

    fprintf(stderr, "process ICE messages\n");
    status = IceProcessMessages(ice_conn, NULL, NULL);

    switch (status)
    {
        case IceProcessMessagesSuccess:
            fprintf(stderr, "ICE message: success!\n");
        return 1;

        case IceProcessMessagesIOError:
            fprintf(stderr, "ICE message: IO error!\n");
        sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
        return 0;

        case IceProcessMessagesConnectionClosed:
            fprintf(stderr, "ICE message: connection closed!\n");
        return 0;

    }
    fprintf(stderr, "ICE message: ????\n");
    return 0;
}

static gboolean ice_iochannel_watch(GIOChannel *channel, GIOCondition condition,
        gpointer client_data) {
    fprintf(stderr, "ice_iochannel_watch\n");
    return process_ice_messages(client_data);
}

static void ice_connection_watch(IceConn ice_conn, IcePointer client_data, Bool opening,
        IcePointer *watch_data) {
    guint watch_id;

    fprintf(stderr, "IceConnectionWatch\n");
    if (opening) {
        GIOChannel *channel;
        int fd = IceConnectionNumber(ice_conn);

        fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
        channel = g_io_channel_unix_new(fd);
        watch_id = g_io_add_watch(channel, G_IO_IN | G_IO_ERR, ice_iochannel_watch, ice_conn);
        g_io_channel_unref(channel);

        *watch_data = GUINT_TO_POINTER(watch_id);
    } else {
        watch_id = GPOINTER_TO_UINT(*watch_data);
        g_source_remove(watch_id);
    }
}

static void ice_init(void) {
    IceSetIOErrorHandler(ice_io_error_handler);
    IceSetErrorHandler(ice_error_handler);
    IceAddConnectionWatch(ice_connection_watch, NULL);
}

static FILE* logfile = NULL;

int main(int argc, char **argv) {
    XSMPConnection *xsmp;
    SmcConn connection;
    SmcCallbacks callbacks;
    char *ret_client_id;
    char error_string_ret[256];
    const char client_id[] = "xsmp-test";
    error_string_ret[0] = '\0';

    ice_init();
    xsmp = malloc(sizeof(XSMPConnection));
    xsmp->client_id = strdup(client_id);
    SmcSetErrorHandler(smc_error_handler);
    callbacks.save_yourself.callback = xsmp_save_yourself;
    callbacks.die.callback = xsmp_die;
    callbacks.save_complete.callback = xsmp_save_complete;
    callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;

    callbacks.save_yourself.client_data = xsmp;
    callbacks.die.client_data = xsmp;
    callbacks.save_complete.client_data = xsmp;
    callbacks.shutdown_cancelled.client_data = xsmp;

    connection = SmcOpenConnection(NULL, xsmp, SmProtoMajor, SmProtoMinor, SmcSaveYourselfProcMask
            | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, &callbacks,
            NULL, &ret_client_id, sizeof(error_string_ret), error_string_ret);
    if (!connection) {
        fprintf(stderr, "error opening SM connection\n");
        exit(-1);
    }
    fprintf(stderr, "Got client ID \"%s\"\n", ret_client_id);
    xsmp->state = XSMP_STATE_IDLE;
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    g_main_loop_run(loop);
    SmcCloseConnection(connection, 0, NULL);
    exit(0);
}

