/*
 * 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 <dbind/dbind.h>
#include <glib.h>
#include <stdio.h>
#include <string.h>

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_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 = dbus_bus_get (DBUS_BUS_SESSION, NULL);
  g_assert (bus != NULL);

  test_helpers ();
  test_marshalling ();

  return 0;
}
