/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-message-factory.c Generator of valid and invalid message data for test suite
 *
 * Copyright (C) 2005 Red Hat Inc.
 * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#ifdef __SYMBIAN32__
#include "config.h"
#else
#include <config.h>
#endif


#ifndef DOXYGEN_SHOULD_SKIP_THIS

#ifdef DBUS_BUILD_TESTS
#include "dbus-message-factory.h"
#include "dbus-message-private.h"
#include "dbus-test.h"
#include <stdio.h>

typedef enum
  {
    CHANGE_TYPE_ADJUST,
    CHANGE_TYPE_ABSOLUTE
  } ChangeType;

#define BYTE_ORDER_OFFSET  0
#define TYPE_OFFSET        1
#define BODY_LENGTH_OFFSET 4
#define FIELDS_ARRAY_LENGTH_OFFSET 12

static void
iter_recurse (DBusMessageDataIter *iter)
{
  iter->depth += 1;
  _dbus_assert (iter->depth < _DBUS_MESSAGE_DATA_MAX_NESTING);
  _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
}

static int
iter_get_sequence (DBusMessageDataIter *iter)
{
  _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
  return iter->sequence_nos[iter->depth];
}

static void
iter_set_sequence (DBusMessageDataIter *iter,
                   int                  sequence)
{
  _dbus_assert (sequence >= 0);
  iter->sequence_nos[iter->depth] = sequence;
}

static void
iter_unrecurse (DBusMessageDataIter *iter)
{
  iter->depth -= 1;
  _dbus_assert (iter->depth >= 0);
}

static void
iter_next (DBusMessageDataIter *iter)
{
  iter->sequence_nos[iter->depth] += 1;
}

static dbus_bool_t
iter_first_in_series (DBusMessageDataIter *iter)
{
  int i;

  i = iter->depth;
  while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
    {
      if (iter->sequence_nos[i] != 0)
        return FALSE;
      ++i;
    }
  return TRUE;
}

typedef dbus_bool_t (* DBusInnerGeneratorFunc)   (DBusMessageDataIter *iter,
                                                  DBusMessage        **message_p);
typedef dbus_bool_t (* DBusMessageGeneratorFunc) (DBusMessageDataIter *iter,
                                                  DBusString          *data,
                                                  DBusValidity        *expected_validity);

static void
set_reply_serial (DBusMessage *message)
{
  if (message == NULL)
    _dbus_assert_not_reached ("oom");
  if (!dbus_message_set_reply_serial (message, 100))
    _dbus_assert_not_reached ("oom");
}

static dbus_bool_t
generate_trivial_inner (DBusMessageDataIter *iter,
                        DBusMessage        **message_p)
{
  DBusMessage *message;

  switch (iter_get_sequence (iter))
    {
    case 0:
      message = dbus_message_new_method_call ("org.freedesktop.TextEditor",
                                              "/foo/bar",
                                              "org.freedesktop.DocumentFactory",
                                              "Create");
      break;
    case 1:
      message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
      set_reply_serial (message);
      break;
    case 2:
      message = dbus_message_new_signal ("/foo/bar",
                                         "org.freedesktop.DocumentFactory",
                                         "Created");
      break;
    case 3:
      message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);

      if (!dbus_message_set_error_name (message,
                                        "org.freedesktop.TestErrorName"))
        _dbus_assert_not_reached ("oom");
      
      {
        DBusMessageIter iter;
        const char *v_STRING = "This is an error";
        
        dbus_message_iter_init_append (message, &iter);
        if (!dbus_message_iter_append_basic (&iter,
                                             DBUS_TYPE_STRING,
                                             &v_STRING))
          _dbus_assert_not_reached ("oom");
      }
      
      set_reply_serial (message);
      break;
    default:
      return FALSE;
    }
  
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  *message_p = message;
  
  return TRUE;
}

static dbus_bool_t
generate_many_bodies_inner (DBusMessageDataIter *iter,
                            DBusMessage        **message_p)
{
  DBusMessage *message;
  DBusString signature;
  DBusString body;

  /* Keeping this small makes things go faster */
  message = dbus_message_new_method_call ("o.z.F",
                                          "/",
                                          "o.z.B",
                                          "Nah");
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  set_reply_serial (message);

  if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
    _dbus_assert_not_reached ("oom");
  
  if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter),
                                                message->byte_order,
                                                &signature, &body))
    {
      const char *v_SIGNATURE;

      v_SIGNATURE = _dbus_string_get_const_data (&signature);
      if (!_dbus_header_set_field_basic (&message->header,
                                         DBUS_HEADER_FIELD_SIGNATURE,
                                         DBUS_TYPE_SIGNATURE,
                                         &v_SIGNATURE))
        _dbus_assert_not_reached ("oom");

      if (!_dbus_string_move (&body, 0, &message->body, 0))
        _dbus_assert_not_reached ("oom");

      _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET,
                                _dbus_string_get_length (&message->body),
                                message->byte_order);
      
      *message_p = message;
    }
  else
    {
      dbus_message_unref (message);
      *message_p = NULL;
    }
  
  _dbus_string_free (&signature);
  _dbus_string_free (&body);

  return *message_p != NULL;
}

static void
generate_from_message (DBusString            *data,
                       DBusValidity          *expected_validity,
                       DBusMessage           *message)
{
  _dbus_message_set_serial (message, 1);
  _dbus_message_lock (message);

  *expected_validity = DBUS_VALID;
  
  /* move for efficiency, since we'll nuke the message anyway */
  if (!_dbus_string_move (&message->header.data, 0,
                          data, 0))
    _dbus_assert_not_reached ("oom");

  if (!_dbus_string_copy (&message->body, 0,
                          data, _dbus_string_get_length (data)))
    _dbus_assert_not_reached ("oom");
}

static dbus_bool_t
generate_outer (DBusMessageDataIter   *iter,
                DBusString            *data,
                DBusValidity          *expected_validity,
                DBusInnerGeneratorFunc func)
{
  DBusMessage *message;

  message = NULL;
  if (!(*func)(iter, &message))
    return FALSE;

  iter_next (iter);
  
  _dbus_assert (message != NULL);

  generate_from_message (data, expected_validity, message);

  dbus_message_unref (message);

  return TRUE;
}

static dbus_bool_t
generate_trivial (DBusMessageDataIter   *iter,
                  DBusString            *data,
                  DBusValidity          *expected_validity)
{
  return generate_outer (iter, data, expected_validity,
                         generate_trivial_inner);
}

static dbus_bool_t
generate_many_bodies (DBusMessageDataIter   *iter,
                      DBusString            *data,
                      DBusValidity          *expected_validity)
{
  return generate_outer (iter, data, expected_validity,
                         generate_many_bodies_inner);
}

static DBusMessage*
simple_method_call (void)
{
  DBusMessage *message;
  /* Keeping this small makes stuff go faster */
  message = dbus_message_new_method_call ("o.b.Q",
                                          "/f/b",
                                          "o.b.Z",
                                          "Fro");
  if (message == NULL)
    _dbus_assert_not_reached ("oom");
  return message;
}

static DBusMessage*
simple_signal (void)
{
  DBusMessage *message;
  message = dbus_message_new_signal ("/f/b",
                                     "o.b.Z",
                                     "Fro");
  if (message == NULL)
    _dbus_assert_not_reached ("oom");
  return message;
}

static DBusMessage*
simple_method_return (void)
{
  DBusMessage *message;
  message =  dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  set_reply_serial (message);
  
  return message;
}

static DBusMessage*
simple_error (void)
{
  DBusMessage *message;
  message =  dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  if (!dbus_message_set_error_name (message, "foo.bar"))
    _dbus_assert_not_reached ("oom");
  
  set_reply_serial (message);
  
  return message;
}

static dbus_bool_t
generate_special (DBusMessageDataIter   *iter,
                  DBusString            *data,
                  DBusValidity          *expected_validity)
{
  int item_seq;
  DBusMessage *message;
  int pos;
  dbus_int32_t v_INT32;

  _dbus_assert (_dbus_string_get_length (data) == 0);
  
  message = NULL;
  pos = -1;
  v_INT32 = 42;
  item_seq = iter_get_sequence (iter);

  if (item_seq == 0)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      /* set an invalid typecode */
      _dbus_string_set_byte (data, pos + 1, '$');

      *expected_validity = DBUS_INVALID_UNKNOWN_TYPECODE;
    }
  else if (item_seq == 1)
    {
      char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH+2];
      const char *v_STRING;
      int i;
      
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");

      i = 0;
      while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1))
        {
          long_sig[i] = DBUS_TYPE_ARRAY;
          ++i;
        }
      long_sig[i] = DBUS_TYPE_INVALID;

      v_STRING = long_sig;
      if (!_dbus_header_set_field_basic (&message->header,
                                         DBUS_HEADER_FIELD_SIGNATURE,
                                         DBUS_TYPE_SIGNATURE,
                                         &v_STRING))
        _dbus_assert_not_reached ("oom");
      
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION;
    }
  else if (item_seq == 2)
    {
      char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2+4];
      const char *v_STRING;
      int i;
      
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");

      i = 0;
      while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1))
        {
          long_sig[i] = DBUS_STRUCT_BEGIN_CHAR;
          ++i;
        }

      long_sig[i] = DBUS_TYPE_INT32;
      ++i;

      while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2 + 3))
        {
          long_sig[i] = DBUS_STRUCT_END_CHAR;
          ++i;
        }
      long_sig[i] = DBUS_TYPE_INVALID;
      
      v_STRING = long_sig;
      if (!_dbus_header_set_field_basic (&message->header,
                                         DBUS_HEADER_FIELD_SIGNATURE,
                                         DBUS_TYPE_SIGNATURE,
                                         &v_STRING))
        _dbus_assert_not_reached ("oom");
      
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION;
    }
  else if (item_seq == 3)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR);
      
      *expected_validity = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED;
    }
  else if (item_seq == 4)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_END_CHAR);
      
      *expected_validity = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED;
    }
  else if (item_seq == 5)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR);
      _dbus_string_set_byte (data, pos + 2, DBUS_STRUCT_END_CHAR);
      
      *expected_validity = DBUS_INVALID_STRUCT_HAS_NO_FIELDS;
    }
  else if (item_seq == 6)
    {
      message = simple_method_call ();
      generate_from_message (data, expected_validity, message);
      
      _dbus_string_set_byte (data, TYPE_OFFSET, DBUS_MESSAGE_TYPE_INVALID);
      
      *expected_validity = DBUS_INVALID_BAD_MESSAGE_TYPE;
    }
  else if (item_seq == 7)
    {
      /* Messages of unknown type are considered valid */
      message = simple_method_call ();
      generate_from_message (data, expected_validity, message);
      
      _dbus_string_set_byte (data, TYPE_OFFSET, 100);
      
      *expected_validity = DBUS_VALID;
    }
  else if (item_seq == 8)
    {
      message = simple_method_call ();
      generate_from_message (data, expected_validity, message);
      
      _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
                                DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4,
                                message->byte_order);
      _dbus_marshal_set_uint32 (data, FIELDS_ARRAY_LENGTH_OFFSET,
                                DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4,
                                message->byte_order);
      *expected_validity = DBUS_INVALID_MESSAGE_TOO_LONG;
    }
  else if (item_seq == 9)
    {
      const char *v_STRING = "not a valid bus name";
      message = simple_method_call ();

      if (!_dbus_header_set_field_basic (&message->header,
                                         DBUS_HEADER_FIELD_SENDER,
                                         DBUS_TYPE_STRING, &v_STRING))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);

      *expected_validity = DBUS_INVALID_BAD_SENDER;
    }
  else if (item_seq == 10)
    {
      message = simple_method_call ();

      if (!dbus_message_set_interface (message, DBUS_INTERFACE_LOCAL))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);

      *expected_validity = DBUS_INVALID_USES_LOCAL_INTERFACE;
    }
  else if (item_seq == 11)
    {
      message = simple_method_call ();

      if (!dbus_message_set_path (message, DBUS_PATH_LOCAL))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);

      *expected_validity = DBUS_INVALID_USES_LOCAL_PATH;
    }
  else if (item_seq == 12)
    {
      /* Method calls don't have to have interface */
      message = simple_method_call ();

      if (!dbus_message_set_interface (message, NULL))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_VALID;
    }
  else if (item_seq == 13)
    {
      /* Signals require an interface */
      message = simple_signal ();

      if (!dbus_message_set_interface (message, NULL))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_INVALID_MISSING_INTERFACE;
    }
  else if (item_seq == 14)
    {
      message = simple_method_return ();

      if (!_dbus_header_delete_field (&message->header, DBUS_HEADER_FIELD_REPLY_SERIAL))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_INVALID_MISSING_REPLY_SERIAL;
    }
  else if (item_seq == 15)
    {
      message = simple_error ();

      if (!dbus_message_set_error_name (message, NULL))
        _dbus_assert_not_reached ("oom");
      
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_INVALID_MISSING_ERROR_NAME;
    }
  else if (item_seq == 16)
    {
      char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*4+10];
      const char *v_STRING;
      int i;
      int n_begins;
      
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");

      i = 0;
      while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*3 + 3))
        {
          long_sig[i] = DBUS_TYPE_ARRAY;
          ++i;
          long_sig[i] = DBUS_DICT_ENTRY_BEGIN_CHAR;
          ++i;
          long_sig[i] = DBUS_TYPE_INT32;
          ++i;
        }
      n_begins = i / 3;

      long_sig[i] = DBUS_TYPE_INT32;
      ++i;
      
      while (n_begins > 0)
        {
          long_sig[i] = DBUS_DICT_ENTRY_END_CHAR;
          ++i;
          n_begins -= 1;
        }
      long_sig[i] = DBUS_TYPE_INVALID;
      
      v_STRING = long_sig;
      if (!_dbus_header_set_field_basic (&message->header,
                                         DBUS_HEADER_FIELD_SIGNATURE,
                                         DBUS_TYPE_SIGNATURE,
                                         &v_STRING))
        _dbus_assert_not_reached ("oom");
      
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION;
    }
  else if (item_seq == 17)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);

      _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY);
      _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR);
      
      *expected_validity = DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED;
    }
  else if (item_seq == 18)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);
      
      _dbus_string_set_byte (data, pos + 1, DBUS_DICT_ENTRY_END_CHAR);
      
      *expected_validity = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED;
    }
  else if (item_seq == 19)
    {
      message = simple_method_call ();
      if (!dbus_message_append_args (message,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INT32, &v_INT32,
                                     DBUS_TYPE_INVALID))
        _dbus_assert_not_reached ("oom");
                                     
      _dbus_header_get_field_raw (&message->header,
                                  DBUS_HEADER_FIELD_SIGNATURE,
                                  NULL, &pos);
      generate_from_message (data, expected_validity, message);

      _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY);
      _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR);
      _dbus_string_set_byte (data, pos + 3, DBUS_DICT_ENTRY_END_CHAR);
      
      *expected_validity = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS;
    }
  else
    {
      return FALSE;
    }

  if (message)
    dbus_message_unref (message);

  iter_next (iter);
  return TRUE;
}

static dbus_bool_t
generate_wrong_length (DBusMessageDataIter *iter,
                       DBusString          *data,
                       DBusValidity        *expected_validity)
{
  int lengths[] = { -42, -17, -16, -15, -9, -8, -7, -6, -5, -4, -3, -2, -1,
                    1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 30 };
  int adjust;
  int len_seq;

 restart:
  len_seq = iter_get_sequence (iter);
  if (len_seq == _DBUS_N_ELEMENTS (lengths))
    return FALSE;

  _dbus_assert (len_seq < _DBUS_N_ELEMENTS (lengths));
  
  iter_recurse (iter);
  if (!generate_many_bodies (iter, data, expected_validity))
    {
      iter_set_sequence (iter, 0); /* reset to first body */
      iter_unrecurse (iter);
      iter_next (iter);            /* next length adjustment */
      goto restart;
    }
  iter_unrecurse (iter);

  adjust = lengths[len_seq];

  if (adjust < 0)
    {
      if ((_dbus_string_get_length (data) + adjust) < DBUS_MINIMUM_HEADER_SIZE)
        _dbus_string_set_length (data, DBUS_MINIMUM_HEADER_SIZE);
      else
        _dbus_string_shorten (data, - adjust);
      *expected_validity = DBUS_INVALID_FOR_UNKNOWN_REASON;
    }
  else
    {      
      if (!_dbus_string_lengthen (data, adjust))
        _dbus_assert_not_reached ("oom");
      *expected_validity = DBUS_INVALID_TOO_MUCH_DATA;
    }

  /* Fixup lengths */
  {
    int old_body_len;
    int new_body_len;
    int byte_order;
    
    _dbus_assert (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE);
    
    byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
    old_body_len = _dbus_marshal_read_uint32 (data,
                                              BODY_LENGTH_OFFSET,
                                              byte_order,
                                              NULL);
    _dbus_assert (old_body_len < _dbus_string_get_length (data));
    new_body_len = old_body_len + adjust;
    if (new_body_len < 0)
      {
        new_body_len = 0;
        /* we just munged the header, and aren't sure how */
        *expected_validity = DBUS_VALIDITY_UNKNOWN;
      }

    _dbus_verbose ("changing body len from %u to %u by adjust %d\n",
                   old_body_len, new_body_len, adjust);
    
    _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET,
                              new_body_len,
                              byte_order);
  }

  return TRUE;
}

static dbus_bool_t
generate_byte_changed (DBusMessageDataIter *iter,
                       DBusString          *data,
                       DBusValidity        *expected_validity)
{
  int byte_seq;
  int v_BYTE;

  /* This is a little convoluted to make the bodies the
   * outer loop and each byte of each body the inner
   * loop
   */

 restart:
  if (!generate_many_bodies (iter, data, expected_validity))
    return FALSE;

  iter_recurse (iter);
  byte_seq = iter_get_sequence (iter);
  iter_next (iter);
  iter_unrecurse (iter);
  
  if (byte_seq == _dbus_string_get_length (data))
    {
      _dbus_string_set_length (data, 0);
      /* reset byte count */
      iter_recurse (iter);
      iter_set_sequence (iter, 0);
      iter_unrecurse (iter);
      goto restart;
    }
  else
    {
      /* Undo the "next" in generate_many_bodies */
      iter_set_sequence (iter, iter_get_sequence (iter) - 1);
    }

  _dbus_assert (byte_seq < _dbus_string_get_length (data));
  v_BYTE = _dbus_string_get_byte (data, byte_seq);
  v_BYTE += byte_seq; /* arbitrary but deterministic change to the byte */
  _dbus_string_set_byte (data, byte_seq, v_BYTE);
  *expected_validity = DBUS_VALIDITY_UNKNOWN;

  return TRUE;
}

static dbus_bool_t
find_next_typecode (DBusMessageDataIter *iter,
                    DBusString          *data,
                    DBusValidity        *expected_validity)
{
  int body_seq;
  int byte_seq;
  int base_depth;

  base_depth = iter->depth;

 restart:
  _dbus_assert (iter->depth == (base_depth + 0));
  _dbus_string_set_length (data, 0);

  body_seq = iter_get_sequence (iter);
  
  if (!generate_many_bodies (iter, data, expected_validity))
    return FALSE;
  /* Undo the "next" in generate_many_bodies */
  iter_set_sequence (iter, body_seq);
  
  iter_recurse (iter);
  while (TRUE)
    {
      _dbus_assert (iter->depth == (base_depth + 1));
      
      byte_seq = iter_get_sequence (iter);

      _dbus_assert (byte_seq <= _dbus_string_get_length (data));
      
      if (byte_seq == _dbus_string_get_length (data))
        {
          /* reset byte count */
          iter_set_sequence (iter, 0);
          iter_unrecurse (iter);
          _dbus_assert (iter->depth == (base_depth + 0));
          iter_next (iter); /* go to the next body */
          goto restart;
        }

      _dbus_assert (byte_seq < _dbus_string_get_length (data));

      if (_dbus_type_is_valid (_dbus_string_get_byte (data, byte_seq)))
        break;
      else
        iter_next (iter);
    }

  _dbus_assert (byte_seq == iter_get_sequence (iter));
  _dbus_assert (byte_seq < _dbus_string_get_length (data));

  iter_unrecurse (iter);

  _dbus_assert (iter->depth == (base_depth + 0));
  
  return TRUE;
}

static const int typecodes[] = {
  DBUS_TYPE_INVALID,
  DBUS_TYPE_BYTE,
  DBUS_TYPE_BOOLEAN,
  DBUS_TYPE_INT16,
  DBUS_TYPE_UINT16,
  DBUS_TYPE_INT32,
  DBUS_TYPE_UINT32,
  DBUS_TYPE_INT64,
  DBUS_TYPE_UINT64,
  DBUS_TYPE_DOUBLE,
  DBUS_TYPE_STRING,
  DBUS_TYPE_OBJECT_PATH,
  DBUS_TYPE_SIGNATURE,
  DBUS_TYPE_ARRAY,
  DBUS_TYPE_VARIANT,
  DBUS_STRUCT_BEGIN_CHAR,
  DBUS_STRUCT_END_CHAR,
  DBUS_DICT_ENTRY_BEGIN_CHAR,
  DBUS_DICT_ENTRY_END_CHAR,
  255 /* random invalid typecode */
};
  
static dbus_bool_t
generate_typecode_changed (DBusMessageDataIter *iter,
                           DBusString          *data,
                           DBusValidity        *expected_validity)
{
  int byte_seq;
  int typecode_seq;
  int base_depth;

  base_depth = iter->depth;

 restart:
  _dbus_assert (iter->depth == (base_depth + 0));
  _dbus_string_set_length (data, 0);
  
  if (!find_next_typecode (iter, data, expected_validity))
    return FALSE;

  iter_recurse (iter);
  byte_seq = iter_get_sequence (iter);

  _dbus_assert (byte_seq < _dbus_string_get_length (data));
  
  iter_recurse (iter);
  typecode_seq = iter_get_sequence (iter);
  iter_next (iter);

  _dbus_assert (typecode_seq <= _DBUS_N_ELEMENTS (typecodes));
  
  if (typecode_seq == _DBUS_N_ELEMENTS (typecodes))
    {
      _dbus_assert (iter->depth == (base_depth + 2));
      iter_set_sequence (iter, 0); /* reset typecode sequence */
      iter_unrecurse (iter);
      _dbus_assert (iter->depth == (base_depth + 1));
      iter_next (iter); /* go to the next byte_seq */
      iter_unrecurse (iter);
      _dbus_assert (iter->depth == (base_depth + 0));
      goto restart;
    }

  _dbus_assert (iter->depth == (base_depth + 2));
  iter_unrecurse (iter);
  _dbus_assert (iter->depth == (base_depth + 1));
  iter_unrecurse (iter);
  _dbus_assert (iter->depth == (base_depth + 0));

#if 0
  printf ("Changing byte %d in message %d to %c\n",
          byte_seq, iter_get_sequence (iter), typecodes[typecode_seq]);
#endif
  
  _dbus_string_set_byte (data, byte_seq, typecodes[typecode_seq]);
  *expected_validity = DBUS_VALIDITY_UNKNOWN;
  return TRUE;
}

typedef struct
{
  ChangeType type;
  dbus_uint32_t value; /* cast to signed for adjusts */
} UIntChange;

static const UIntChange uint32_changes[] = {
  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -1 },
  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -2 },
  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -3 },
  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 1 },
  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 2 },
  { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 3 },
  { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX },
  { CHANGE_TYPE_ABSOLUTE, 0 },
  { CHANGE_TYPE_ABSOLUTE, 1 },
  { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 1 },
  { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 5 }
};

static dbus_bool_t
generate_uint32_changed (DBusMessageDataIter *iter,
                         DBusString          *data,
                         DBusValidity        *expected_validity)
{
  int body_seq;
  int byte_seq;
  int change_seq;
  dbus_uint32_t v_UINT32;
  int byte_order;
  const UIntChange *change;
  int base_depth;

  /* Outer loop is each body, next loop is each change,
   * inner loop is each change location
   */

  base_depth = iter->depth;
  
 next_body:
  _dbus_assert (iter->depth == (base_depth + 0));
  _dbus_string_set_length (data, 0);
  body_seq = iter_get_sequence (iter);
  
  if (!generate_many_bodies (iter, data, expected_validity))
    return FALSE;

  _dbus_assert (iter->depth == (base_depth + 0));

  iter_set_sequence (iter, body_seq); /* undo the "next" from generate_many_bodies */
  iter_recurse (iter);
 next_change:
  _dbus_assert (iter->depth == (base_depth + 1));
  change_seq = iter_get_sequence (iter);
  
  if (change_seq == _DBUS_N_ELEMENTS (uint32_changes))
    {
      /* Reset change count */
      iter_set_sequence (iter, 0);
      iter_unrecurse (iter);
      iter_next (iter);
      goto next_body;
    }

  _dbus_assert (iter->depth == (base_depth + 1));
  
  iter_recurse (iter);
  _dbus_assert (iter->depth == (base_depth + 2));
  byte_seq = iter_get_sequence (iter);
  /* skip 4 bytes at a time */
  iter_next (iter);
  iter_next (iter);
  iter_next (iter);
  iter_next (iter);
  iter_unrecurse (iter);

  _dbus_assert (_DBUS_ALIGN_VALUE (byte_seq, 4) == (unsigned) byte_seq);
  if (byte_seq >= (_dbus_string_get_length (data) - 4))
    {
      /* reset byte count */
      _dbus_assert (iter->depth == (base_depth + 1));
      iter_recurse (iter);
      _dbus_assert (iter->depth == (base_depth + 2));
      iter_set_sequence (iter, 0);
      iter_unrecurse (iter);
      _dbus_assert (iter->depth == (base_depth + 1));
      iter_next (iter);
      goto next_change;
    }
  
  _dbus_assert (byte_seq <= (_dbus_string_get_length (data) - 4));

  byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET);
  
  v_UINT32 = _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL);

  change = &uint32_changes[change_seq];

  if (change->type == CHANGE_TYPE_ADJUST)
    {
      v_UINT32 += (int) change->value;
    }
  else
    {
      v_UINT32 = change->value;
    }

#if 0
  printf ("body %d change %d pos %d ",
          body_seq, change_seq, byte_seq);

  if (change->type == CHANGE_TYPE_ADJUST)
    printf ("adjust by %d", (int) change->value);
  else
    printf ("set to %u", change->value);
  
  printf (" \t%u -> %u\n",
          _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL),
          v_UINT32);
#endif
  
  _dbus_marshal_set_uint32 (data, byte_seq, v_UINT32, byte_order);
  *expected_validity = DBUS_VALIDITY_UNKNOWN;

  _dbus_assert (iter->depth == (base_depth + 1));
  iter_unrecurse (iter);
  _dbus_assert (iter->depth == (base_depth + 0));
          
  return TRUE;
}

typedef struct
{
  const char *name;
  DBusMessageGeneratorFunc func;  
} DBusMessageGenerator;

static const DBusMessageGenerator generators[] = {
  { "trivial example of each message type", generate_trivial },
  { "assorted arguments", generate_many_bodies },
  { "assorted special cases", generate_special },
  { "each uint32 modified", generate_uint32_changed },
  { "wrong body lengths", generate_wrong_length },
  { "each byte modified", generate_byte_changed },
#if 0
  /* This is really expensive and doesn't add too much coverage */
  { "change each typecode", generate_typecode_changed }
#endif
};

void
_dbus_message_data_free (DBusMessageData *data)
{
  _dbus_string_free (&data->data);
}

void
_dbus_message_data_iter_init (DBusMessageDataIter *iter)
{
  int i;
  
  iter->depth = 0;
  i = 0;
  while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
    {
      iter->sequence_nos[i] = 0;
      ++i;
    }
  iter->count = 0;
}

dbus_bool_t
_dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter,
                                      DBusMessageData     *data)
{
  DBusMessageGeneratorFunc func;
  int generator;

 restart:
  generator = iter_get_sequence (iter);
  
  if (generator == _DBUS_N_ELEMENTS (generators))
    return FALSE;

  iter_recurse (iter);
  
  if (iter_first_in_series (iter))
    {
      printf (" testing message loading: %s ", generators[generator].name);
      fflush (stdout);
    }
  
  func = generators[generator].func;

  if (!_dbus_string_init (&data->data))
    _dbus_assert_not_reached ("oom");
  
  if ((*func)(iter, &data->data, &data->expected_validity))
    ;
  else
    {
      iter_set_sequence (iter, 0);
      iter_unrecurse (iter);
      iter_next (iter); /* next generator */
      _dbus_string_free (&data->data);
      printf ("%d test loads cumulative\n", iter->count);
      goto restart;
    }
  iter_unrecurse (iter);

  iter->count += 1;
  return TRUE;
}

#endif /* !DOXYGEN_SHOULD_SKIP_THIS */

#endif /* DBUS_BUILD_TESTS */
