/*
 * Copyright 2008-2011 Novell, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include <stdio.h>
#include <glib.h>
#include <string.h>
#include <dbind/dbind.h>

/* Wow! dbus is unpleasant to use */

#define DESKICE_PATH      "/Novell/ICEDesktop/Daemon"
#define DESKICE_NAMESPACE "Novell.ICEDesktop.Daemon"

void marshal (DBusMessage *msg, const char *type, void *ptr)
{
    DBusMessageIter iter;

    dbus_message_iter_init_append (msg, &iter);
    dbind_any_marshal (&iter, &type, &ptr);
}

void demarshal (DBusMessage *msg, const char *type, void *ptr)
{
    DBusMessageIter iter;

    if (!dbus_message_iter_init (msg, &iter))
      {
        fprintf (stderr, "no data in msg\n");
        g_assert_not_reached ();
      }
    else
        dbind_any_demarshal (&iter, &type, &ptr);
}

#if 0
dbus_bool_t  dbus_message_marshal   (DBusMessage  *msg,
                                     char        **marshalled_data_p,
                                     int          *len_p);

void dump_msg (DBusMessage *msg)
{
    char *data = NULL;
    int   len, i, j;

    dbus_message_marshal (msg, &data, &len);
    for (i = 0; i < (len+15)/16; i++) {
        fprintf (stderr, "%4.d | ", i * 16);
        for (j = 0; j < 16; j++) {
            unsigned char c = (i*16+j <= len) ? data[i*16+j] : 0;
            fprintf (stderr, "0x%.2x ", c);
        }
        fprintf (stderr, " | ");
        for (j = 0; j < 16; j++) {
            char c = (i*16+j <= len) ? data[i*16+j] : '\0';
            fprintf (stderr, "%c", g_ascii_isprint (c) ? c : '.');
        }
    }
}
#endif

void test_simple ()
{
    dbus_int32_t v1, v2;
    DBusMessage *msg;

    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
    v1 = 42;
    marshal (msg, "i", &v1);
    demarshal (msg, "i", &v2);
    g_assert (v2 == 42);
    g_assert (v1 == v2);

    dbind_any_free ("i", &v2); /* nop */
    dbus_message_unref (msg);

    printf ("simple ok\n");
}

void test_array ()
{
    GArray *a1, *a2;
    DBusMessage *msg;

    /* pod types */
    a1 = g_array_new (FALSE, FALSE, sizeof (dbus_int32_t));
    g_array_set_size (a1, 4);
    g_array_index (a1, dbus_int32_t, 0) = 42;
    g_array_index (a1, dbus_int32_t, 1) = 17;
    g_array_index (a1, dbus_int32_t, 2) = 26;
    g_array_index (a1, dbus_int32_t, 3) = 38;

    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
    marshal (msg, "ai", &a1);
    demarshal (msg, "ai", &a2);

    g_assert (a2 != NULL);
    g_assert (a2->len == 4);
    g_assert (g_array_index (a2, dbus_int32_t, 0) == 42);
    g_assert (g_array_index (a2, dbus_int32_t, 1) == 17);
    g_assert (g_array_index (a2, dbus_int32_t, 2) == 26);
    g_assert (g_array_index (a2, dbus_int32_t, 3) == 38);
    g_array_free (a1, TRUE);

    dbind_any_free ("ai", &a2);
    dbus_message_unref (msg);

    printf ("array ok\n");
}

/* this taught me that the struct type is a mis-nomer, 
   it is generated by brackets */
void test_struct_native ()
{
    DBusMessage *msg;
    DBusMessageIter iter, arr, str;

    /* manually create ar(ss) */
    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);

    dbus_message_iter_init_append (msg, &iter);

    dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ss)", &arr);
    {
        char *foo;
        dbus_message_iter_open_container (&arr, DBUS_TYPE_STRUCT, NULL, &str);

        foo = "foo";
        dbus_message_iter_append_basic (&str, DBUS_TYPE_STRING, &foo);
        foo = "baa";
        dbus_message_iter_append_basic (&str, DBUS_TYPE_STRING, &foo);
        
        dbus_message_iter_close_container (&arr, &str);
    }
    dbus_message_iter_close_container (&iter, &arr);

    printf ("native struct marshalling ok\n");
    
    dbus_message_unref (msg);
}


void test_struct_simple ()
{
    typedef struct {
        char *foo;
        char *baa;
        char *baz;
    } FooBaa;
    GArray *a1 = NULL, *a2 = NULL;
    DBusMessage *msg;

    a1 = g_array_new (FALSE, FALSE, sizeof (FooBaa));
    g_array_set_size (a1, 2);
    g_array_index (a1, FooBaa, 0).foo = "foo";
    g_array_index (a1, FooBaa, 0).baa = "baa";
    g_array_index (a1, FooBaa, 0).baz = "baz";
    g_array_index (a1, FooBaa, 1).foo = "Foo";
    g_array_index (a1, FooBaa, 1).baa = "baA";
    g_array_index (a1, FooBaa, 1).baz = "BaZ";

    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
    marshal (msg, "a(sss)", &a1);
    demarshal (msg, "a(sss)", &a2);

    g_assert (a2 != NULL);
    g_assert (a2 != a1);
    g_assert (a2->len == 2);
    g_assert (!strcmp (g_array_index (a2, FooBaa, 0).foo, "foo"));
    g_assert (!strcmp (g_array_index (a2, FooBaa, 0).baa, "baa"));
    g_assert (!strcmp (g_array_index (a2, FooBaa, 0).baz, "baz"));
    g_assert (!strcmp (g_array_index (a2, FooBaa, 1).foo, "Foo"));
    g_assert (!strcmp (g_array_index (a2, FooBaa, 1).baa, "baA"));
    g_assert (!strcmp (g_array_index (a2, FooBaa, 1).baz, "BaZ"));
    
    printf ("simple struct ok\n");

    dbind_any_free ("a(sss)", &a2);
    dbus_message_unref (msg);
}

void test_struct_complex ()
{
    typedef struct {
        dbus_int32_t x, y;
    } Point;
    typedef struct {
        unsigned char pad1;
        double        val;
        Point         tl, br;
        char          pad2;
        char         *name;
    } Complex;
#define TYPEOF_POINT \
    DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
        DBUS_TYPE_INT32_AS_STRING \
        DBUS_TYPE_INT32_AS_STRING \
    DBUS_STRUCT_END_CHAR_AS_STRING
#define TYPEOF_COMPLEX \
    DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
        DBUS_TYPE_BYTE_AS_STRING \
        DBUS_TYPE_DOUBLE_AS_STRING \
        TYPEOF_POINT \
        TYPEOF_POINT \
        DBUS_TYPE_BYTE_AS_STRING \
        DBUS_TYPE_STRING_AS_STRING \
    DBUS_STRUCT_END_CHAR_AS_STRING


    DBusMessage *msg;
    Complex c1, c2;

    memset (&c1, 0, sizeof (c1));
    memset (&c2, 0, sizeof (c2));

    c1.pad1 = 2;
    c1.val = 0.1327569;
    c1.tl.x = 1;
    c1.tl.y = 17;
    c1.br.x = 2587;
    c1.br.y = -1;
    c1.pad2 = 1;
    c1.name = "stroustrup";

    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
    marshal (msg, TYPEOF_COMPLEX, &c1);
    demarshal (msg, TYPEOF_COMPLEX, &c2);

    g_assert (c2.pad1 == 2);
    g_assert (c2.val == c1.val);
    g_assert (c2.val != 0);
    g_assert (c2.tl.x == 1);
    g_assert (c2.tl.y == 17);
    g_assert (c2.br.x == 2587);
    g_assert (c2.br.y == -1);
    g_assert (c2.pad2 == 1);
    g_assert (!strcmp (c1.name, "stroustrup"));
    
    printf ("complex struct ok\n");

    dbind_any_free (TYPEOF_COMPLEX, &c2);
    dbus_message_unref (msg);
}

void test_struct_with_array ()
{
    typedef struct {
        GArray *vals;
        unsigned char pad1;
    } ArrayStruct;
#define TYPEOF_ARRAYSTRUCT \
        DBUS_TYPE_ARRAY_AS_STRING \
    DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
        DBUS_TYPE_ARRAY_AS_STRING \
        DBUS_TYPE_UINT32_AS_STRING \
        DBUS_TYPE_BYTE_AS_STRING \
    DBUS_STRUCT_END_CHAR_AS_STRING


    DBusMessage *msg;
    GArray *a1, *a2;
    ArrayStruct *p, *q;


    a1 = g_array_new (FALSE, FALSE, sizeof (ArrayStruct));
    g_array_set_size (a1, 2);
    p = &g_array_index (a1, ArrayStruct, 0);
    p[0].vals = g_array_new (FALSE, FALSE, sizeof (dbus_uint32_t));
    g_array_set_size (p[0].vals, 2);
    g_array_index (p[0].vals, dbus_uint32_t, 0) = 1;
    g_array_index (p[0].vals, dbus_uint32_t, 1) = 1000;
    p[0].pad1 = 2;
    p[1].vals = g_array_new (FALSE, FALSE, sizeof (dbus_uint32_t));
    g_array_set_size (p[1].vals, 2);
    g_array_index (p[1].vals, dbus_uint32_t, 0) = 1000000;
    g_array_index (p[1].vals, dbus_uint32_t, 1) = 1000000000;
    p[1].pad1 = 8;

    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
    marshal (msg, TYPEOF_ARRAYSTRUCT, &a1);
    demarshal (msg, TYPEOF_ARRAYSTRUCT, &a2);

    q = &g_array_index (a2, ArrayStruct, 0);
    g_assert (q[0].pad1 == 2);
    g_assert (g_array_index (q[1].vals, dbus_uint32_t, 1) == 1000000000);
    
    printf ("struct with array ok\n");

    dbind_any_free (TYPEOF_ARRAYSTRUCT, &a2);
    dbus_message_unref (msg);
    g_array_free (p[0].vals, TRUE);
    g_array_free (p[1].vals, TRUE);
}

void test_twovals ()
{
    typedef struct {
        dbus_int32_t v1;
        dbus_int32_t v2;
    } TwoVal;
#define TYPEOF_TWOVAL \
        DBUS_TYPE_INT32_AS_STRING \
        DBUS_TYPE_INT32_AS_STRING \

    DBusMessage *msg;
    DBusMessageIter iter;
    TwoVal i, o;
    const char *type_twoval = TYPEOF_TWOVAL;
    const char *type;
    void *ptr;

    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
    i.v1 = 42;
  i.v2 = 1764;
    dbus_message_iter_init_append (msg, &iter);
    type = type_twoval;
    ptr = &i;
    dbind_any_marshal (&iter, &type, &ptr);
    dbind_any_marshal (&iter, &type, &ptr);
    dbus_message_iter_init (msg, &iter);
    type = type_twoval;
    ptr = &o;
    dbind_any_demarshal (&iter, &type, &ptr);
    dbind_any_demarshal (&iter, &type, &ptr);
    g_assert (o.v1 == 42);
    g_assert (o.v2 == 1764);
    g_assert (i.v1 == o.v1);
    g_assert (i.v2 == o.v2);

    dbind_any_free ("ii", &o); /* nop */
    dbus_message_unref (msg);

    printf ("two-val ok\n");
}

void test_marshalling ()
{
    test_simple ();
    test_array ();
    test_struct_native ();
    test_struct_simple ();
    test_struct_complex ();
    test_struct_with_array ();
    test_twovals ();

    printf ("Marshalling ok\n");
}

void test_teamspaces (DBusConnection *bus)
{
    GArray *spaces;
    DBusError error;
    int i;
    typedef struct {
        char *name;
        char *id;
        char *url;
    } TeamSpace;

    dbus_error_init (&error);
    if (!dbind_method_call_reentrant (bus,
                                      NULL,
                                      DESKICE_PATH,
                                      DESKICE_NAMESPACE,
                                      "GetTeamList",
                                      &error,
                                      "=>a(sss)",
                                      &spaces)) {
        fprintf (stderr, "Error getting team spaces %s: %s\n",
                 error.name, error.message);
        dbus_error_free (&error);
        return;
    }

    if (!spaces) {
        fprintf (stderr, "no teamspaces\n");
        return;
    }
    fprintf (stderr, "%d teamspace(s)\n", spaces->len);
    for (i = 0; i < spaces->len; i++) {
        TeamSpace *space = &g_array_index (spaces, TeamSpace, i);
        fprintf (stderr, "\t%d: %s, %s, %s\n", i, space->name, space->id, space->url);
    }

    dbind_any_free_ptr ("a(sss)", spaces);
}

void test_helpers ()
{
    dbind_find_c_alignment ("(sss)");
    dbind_find_c_alignment ("a(sss)");
    dbind_find_c_alignment ("(s(s)yd(d)s)");
    dbind_find_c_alignment ("a{ss}");
    printf ("helpers passed\n");
}

int main (int argc, char **argv)
{
    DBusConnection *bus;

    bus = dbus_bus_get (DBUS_BUS_SESSION, NULL);

    test_helpers ();
    test_marshalling ();
    test_teamspaces (bus);

    return 0;
}
