#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "rfb.h"
#include "d3des.h"
#include <gst/gst.h>

#include <stdlib.h>
#include <string.h>

#define RFB_GET_UINT32(ptr) GST_READ_UINT32_BE(ptr)
#define RFB_GET_UINT16(ptr) GST_READ_UINT16_BE(ptr)
#define RFB_GET_UINT8(ptr) GST_READ_UINT8(ptr)

#define RFB_SET_UINT32(ptr, val) GST_WRITE_UINT32_BE((ptr),(val))
#define RFB_SET_UINT16(ptr, val) GST_WRITE_UINT16_BE((ptr),(val))
#define RFB_SET_UINT8(ptr, val) GST_WRITE_UINT8((ptr),(val))

GST_DEBUG_CATEGORY_EXTERN (rfbdecoder_debug);
#define GST_CAT_DEFAULT rfbdecoder_debug


static gboolean rfb_decoder_state_wait_for_protocol_version (RfbDecoder *
    decoder);
static gboolean rfb_decoder_state_wait_for_security (RfbDecoder * decoder);
static gboolean rfb_decoder_state_send_client_initialisation (RfbDecoder *
    decoder);
static gboolean rfb_decoder_state_wait_for_server_initialisation (RfbDecoder *
    decoder);
static gboolean rfb_decoder_state_security_result (RfbDecoder * decoder);
static gboolean rfb_decoder_state_normal (RfbDecoder * decoder);
static gboolean rfb_decoder_state_framebuffer_update (RfbDecoder * decoder);
static gboolean rfb_decoder_state_framebuffer_update_rectangle (RfbDecoder *
    decoder);
static gboolean rfb_decoder_state_set_colour_map_entries (RfbDecoder * decoder);
static gboolean rfb_decoder_state_server_cut_text (RfbDecoder * decoder);
static void rfb_decoder_raw_encoding (RfbDecoder * decoder, gint start_x,
    gint start_y, gint rect_w, gint rect_h);
static void rfb_decoder_copyrect_encoding (RfbDecoder * decoder, gint start_x,
    gint start_y, gint rect_w, gint rect_h);
static void rfb_decoder_rre_encoding (RfbDecoder * decoder, gint start_x,
    gint start_y, gint rect_w, gint rect_h);
static void rfb_decoder_corre_encoding (RfbDecoder * decoder, gint start_x,
    gint start_y, gint rect_w, gint rect_h);
static void rfb_decoder_hextile_encoding (RfbDecoder * decoder, gint start_x,
    gint start_y, gint rect_w, gint rect_h);

RfbDecoder *
rfb_decoder_new (void)
{
  RfbDecoder *decoder = g_new0 (RfbDecoder, 1);

  decoder->socket = NULL;
  decoder->cancellable = g_cancellable_new ();

  decoder->password = NULL;

  decoder->use_copyrect = FALSE;

  decoder->offset_x = 0;
  decoder->offset_y = 0;
  decoder->rect_width = 0;
  decoder->rect_height = 0;
  decoder->shared_flag = TRUE;
  decoder->disconnected = FALSE;
  decoder->data = NULL;
  decoder->data_len = 0;
  decoder->error = NULL;

  return decoder;
}

void
rfb_decoder_free (RfbDecoder * decoder)
{
  g_return_if_fail (decoder != NULL);

  if (decoder->cancellable) {
    g_cancellable_cancel (decoder->cancellable);
    g_object_unref (decoder->cancellable);
    decoder->cancellable = NULL;
  }

  if (decoder->socket) {
    g_object_unref (decoder->socket);
    decoder->socket = NULL;
  }

  g_clear_error (&decoder->error);

  g_free (decoder->data);

  g_free (decoder);
}

gboolean
rfb_decoder_connect_tcp (RfbDecoder * decoder, gchar * host, guint port)
{
  GError *err = NULL;
  GInetAddress *addr;
  GSocketAddress *saddr;
  GResolver *resolver;

  GST_DEBUG ("connecting to the rfb server");

  g_return_val_if_fail (decoder != NULL, FALSE);
  g_return_val_if_fail (decoder->socket == NULL, FALSE);
  g_return_val_if_fail (host != NULL, FALSE);

  /* look up name if we need to */
  addr = g_inet_address_new_from_string (host);
  if (!addr) {
    GList *results;

    resolver = g_resolver_get_default ();

    results =
        g_resolver_lookup_by_name (resolver, host, decoder->cancellable, &err);
    if (!results)
      goto name_resolve;
    addr = G_INET_ADDRESS (g_object_ref (results->data));

    g_resolver_free_addresses (results);
    g_object_unref (resolver);
  }

  saddr = g_inet_socket_address_new (addr, port);

  decoder->socket =
      g_socket_new (g_socket_address_get_family (saddr), G_SOCKET_TYPE_STREAM,
      G_SOCKET_PROTOCOL_TCP, &err);

  if (!decoder->socket)
    goto no_socket;

  GST_DEBUG ("opened receiving client socket");

  if (!g_socket_connect (decoder->socket, saddr, decoder->cancellable, &err))
    goto connect_failed;

  g_object_unref (saddr);

  decoder->disconnected = FALSE;

  return TRUE;

no_socket:
  {
    GST_WARNING ("Failed to create socket: %s", err->message);
    if (decoder->error == NULL)
      decoder->error = err;
    else
      g_clear_error (&err);
    g_object_unref (saddr);
    return FALSE;
  }
name_resolve:
  {
    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      GST_DEBUG ("Cancelled name resolval");
    } else {
      GST_WARNING ("Failed to resolve host '%s': %s", host, err->message);
      if (decoder->error == NULL) {
        decoder->error = err;
        err = NULL;
      }
    }
    g_clear_error (&err);
    g_object_unref (resolver);
    return FALSE;
  }
connect_failed:
  {
    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      GST_DEBUG ("Cancelled connecting");
    } else {
      GST_WARNING ("Failed to connect to host '%s:%d': %s", host, port,
          err->message);
      if (decoder->error == NULL) {
        decoder->error = err;
        err = NULL;
      }
    }
    g_clear_error (&err);
    g_object_unref (saddr);
    return FALSE;
  }
}

/**
 * rfb_decoder_iterate:
 * @decoder: The rfb context
 *
 * Initializes the connection with the rfb server
 *
 * Returns: TRUE if initialization was succesfull, FALSE on fail.
 */
gboolean
rfb_decoder_iterate (RfbDecoder * decoder)
{
  gboolean ret;

  g_return_val_if_fail (decoder != NULL, FALSE);
  g_return_val_if_fail (decoder->socket != NULL, FALSE);

  if (decoder->state == NULL) {
    GST_DEBUG ("First iteration: set state to -> wait for protocol version");
    decoder->state = rfb_decoder_state_wait_for_protocol_version;
  }

  GST_DEBUG ("Executing next state in initialization");
  ret = decoder->state (decoder);

  if (ret == FALSE) {
    if (decoder->error == NULL)
      GST_WARNING ("Failure, but no error stored");
    else
      GST_WARNING ("Failure: %s", decoder->error->message);
  }

  return ret;
}

static guint8 *
rfb_decoder_read (RfbDecoder * decoder, guint32 len)
{
  guint32 total = 0;
  gssize now = 0;
  GError *err = NULL;

  g_return_val_if_fail (decoder->socket != NULL, NULL);
  g_return_val_if_fail (len > 0, NULL);

  if (G_UNLIKELY (len > decoder->data_len)) {
    g_free (decoder->data);
    decoder->data = g_malloc (len);
    decoder->data_len = len;
  }

  while (total < len) {
    now = g_socket_receive (decoder->socket, (gchar *) decoder->data + total,
        len - total, decoder->cancellable, &err);

    if (now < 0)
      goto recv_error;

    total += now;
  }
  return decoder->data;

recv_error:
  {
    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      GST_DEBUG ("Read on socket cancelled");
    } else {
      GST_ERROR ("Read error on socket: %s", err->message);
      if (decoder->error == NULL) {
        decoder->error = err;
        err = NULL;
      }
    }
    g_clear_error (&err);
    decoder->disconnected = TRUE;
    return NULL;
  }
}

static gint
rfb_decoder_send (RfbDecoder * decoder, guint8 * buffer, guint len)
{
  gssize now = 0;
  GError *err = NULL;

  g_return_val_if_fail (decoder->socket != NULL, 0);
  g_return_val_if_fail (buffer != NULL, 0);
  g_return_val_if_fail (len > 0, 0);

  now = g_socket_send (decoder->socket, (gchar *) buffer, len,
      decoder->cancellable, &err);

  if (now < 0)
    goto send_error;

done:
  return now;

send_error:
  {
    if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
      GST_DEBUG ("Send on socket cancelled");
    } else {
      GST_ERROR ("Send error on socket: %s", err->message);
      if (decoder->error == NULL) {
        decoder->error = err;
        err = NULL;
      }
    }
    g_clear_error (&err);
    goto done;
  }
}

void
rfb_decoder_send_update_request (RfbDecoder * decoder,
    gboolean incremental, gint x, gint y, gint width, gint height)
{
  guint8 data[10];

  g_return_if_fail (decoder != NULL);
  g_return_if_fail (decoder->socket != NULL);

  data[0] = 3;
  data[1] = incremental;
  RFB_SET_UINT16 (data + 2, x);
  RFB_SET_UINT16 (data + 4, y);
  RFB_SET_UINT16 (data + 6, width);
  RFB_SET_UINT16 (data + 8, height);

  rfb_decoder_send (decoder, data, 10);

  /* create a backup of the prev frame for copyrect encoding */
  if (decoder->use_copyrect) {
    memcpy (decoder->prev_frame, decoder->frame,
        decoder->rect_width * decoder->rect_height * decoder->bpp / 8);
  }

  decoder->state = rfb_decoder_state_normal;
}

void
rfb_decoder_send_key_event (RfbDecoder * decoder, guint key, gboolean down_flag)
{
  guint8 data[8];

  g_return_if_fail (decoder != NULL);
  g_return_if_fail (decoder->socket != NULL);

  data[0] = 4;
  data[1] = down_flag;
  RFB_SET_UINT16 (data + 2, 0);
  RFB_SET_UINT32 (data + 4, key);

  rfb_decoder_send (decoder, data, 8);
}

void
rfb_decoder_send_pointer_event (RfbDecoder * decoder,
    gint button_mask, gint x, gint y)
{
  guint8 data[6];

  g_return_if_fail (decoder != NULL);
  g_return_if_fail (decoder->socket != NULL);

  data[0] = 5;
  data[1] = button_mask;
  RFB_SET_UINT16 (data + 2, x);
  RFB_SET_UINT16 (data + 4, y);

  rfb_decoder_send (decoder, data, 6);
}

/**
 * rfb_decoder_state_wait_for_protocol_version:
 *
 * Negotiate the rfb version used
 *
 * \TODO Support for versions 3.7 and 3.8
 */
static gboolean
rfb_decoder_state_wait_for_protocol_version (RfbDecoder * decoder)
{
  rfb_decoder_read (decoder, 12);

  g_return_val_if_fail (memcmp (decoder->data, "RFB 003.00", 10) == 0, FALSE);
  g_return_val_if_fail (*(decoder->data + 11) == 0x0a, FALSE);

  GST_DEBUG ("\"%.11s\"", decoder->data);
  *(decoder->data) = 0x00;
  *(decoder->data + 11) = 0x00;
  decoder->protocol_major = atoi ((char *) (decoder->data + 4));
  decoder->protocol_minor = atoi ((char *) (decoder->data + 8));
  GST_DEBUG ("Major version : %d", decoder->protocol_major);
  GST_DEBUG ("Minor version : %d", decoder->protocol_minor);

  if (decoder->protocol_major != 3) {
    GST_INFO
        ("A major protocol version of %d is not supported, falling back to 3",
        decoder->protocol_major);
    decoder->protocol_major = 3;
  }
  switch (decoder->protocol_minor) {
    case 3:
      break;
    default:
      GST_INFO ("Minor version %d is not supported, using 3",
          decoder->protocol_minor);
      decoder->protocol_minor = 3;
  }
  rfb_decoder_send (decoder, (guint8 *) "RFB 003.003\n", 12);

  decoder->state = rfb_decoder_state_wait_for_security;
  return TRUE;
}

/*
 * a string describing the reason (where a string is specified as a length
 * followed by that many ASCII characters)
 **/
static gboolean
rfb_decoder_state_reason (RfbDecoder * decoder)
{
  gint reason_length;

  rfb_decoder_read (decoder, 4);

  reason_length = RFB_GET_UINT32 (decoder->data);
  rfb_decoder_read (decoder, reason_length);
  GST_WARNING ("Reason by server: %s", decoder->data);

  if (decoder->error == NULL) {
    decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
        "VNC server error: %s", decoder->data);
  }

  return FALSE;
}

static gboolean
rfb_decoder_state_wait_for_security (RfbDecoder * decoder)
{
  /*
   * Version 3.3 The server decides the security type and sends a single word
   *
   * The security-type may only take the value 0, 1 or 2. A value of 0 means that the
   * connection has failed and is followed by a string giving the reason, as described
   * above.
   */
  if (IS_VERSION_3_3 (decoder)) {
    rfb_decoder_read (decoder, 4);

    decoder->security_type = RFB_GET_UINT32 (decoder->data);
    GST_DEBUG ("security = %d", decoder->security_type);

    g_return_val_if_fail (decoder->security_type < 3, FALSE);
    g_return_val_if_fail (decoder->security_type != SECURITY_FAIL,
        rfb_decoder_state_reason (decoder));
  } else {
    /* \TODO Add behavior for the rfb 3.7 and 3.8 servers */
    GST_WARNING ("Other versions are not yet supported");
  }

  switch (decoder->security_type) {
    case SECURITY_NONE:
      GST_DEBUG ("Security type is None");
      if (IS_VERSION_3_8 (decoder)) {
        decoder->state = rfb_decoder_state_security_result;
      } else {
        decoder->state = rfb_decoder_state_send_client_initialisation;
      }
      break;
    case SECURITY_VNC:{
      unsigned char key[8], *challenge;
      DESContext des_ctx;
      gsize password_len;

      /*
       * VNC authentication is to be used and protocol data is to be sent
       * unencrypted. The server sends a random 16-byte challenge
       */
      GST_DEBUG ("Security type is VNC Authentication");
      /* VNC Authentication can't be used if the password is not set */
      if (!decoder->password) {
        GST_WARNING
            ("VNC Authentication can't be used if the password is not set");
        decoder->error =
            g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
            "VNC servers needs authentication, but no password set");
        return FALSE;
      }

      /* key is 8 bytes and made up of password (padded with 0s if needed) */
      memset (key, 0, 8);
      password_len = strlen (decoder->password);
      memcpy (key, decoder->password, MIN (password_len, 8));

      /* read challenge */
      challenge = rfb_decoder_read (decoder, 16);
      if (challenge == NULL)
        return FALSE;

      /* encrypt 16 challenge bytes in place using key */
      memset (&des_ctx, 0, sizeof (DESContext));
      deskey (&des_ctx, key, EN0);
      des (&des_ctx, challenge, challenge);
      des (&des_ctx, challenge + 8, challenge + 8);

      /* .. and send back to server */
      rfb_decoder_send (decoder, challenge, 16);

      GST_DEBUG ("Encrypted challenge sent to server");

      decoder->state = rfb_decoder_state_security_result;
      break;
    }
    default:
      GST_WARNING ("Security type is not known");
      return FALSE;
      break;
  }
  return TRUE;
}

/*
 * The server sends a word to inform the client whether the security handshaking was
 * successful.
 */
static gboolean
rfb_decoder_state_security_result (RfbDecoder * decoder)
{
  rfb_decoder_read (decoder, 4);
  if (RFB_GET_UINT32 (decoder->data) != 0) {
    GST_WARNING ("Security handshaking failed");
    if (IS_VERSION_3_8 (decoder)) {
      decoder->state = rfb_decoder_state_reason;
      return TRUE;
    }
    if (decoder->error == NULL) {
      decoder->error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
          "authentication failed");
    }
    return FALSE;
  }

  GST_DEBUG ("Security handshaking succesful");
  decoder->state = rfb_decoder_state_send_client_initialisation;

  return TRUE;
}

static guint8 *
rfb_decoder_message_set_encodings (GSList * encodings_list)
{

  guint8 *message = g_malloc0 (4 + 4 * g_slist_length (encodings_list));
  guint32 *encoding_type;

  message[0] = 0x02;            /* message type */
  RFB_SET_UINT16 (message + 2, g_slist_length (encodings_list));        /* number of encodings */

  /* write all the encoding types */
  encoding_type = (guint32 *) (message + 4);

  while (encodings_list) {
    RFB_SET_UINT32 (encoding_type, GPOINTER_TO_UINT (encodings_list->data));
    encoding_type++;
    encodings_list = encodings_list->next;
  }

  return message;
}

/*
 * rfb_decoder_state_set_encodings:
 * @decoder: The rfb context
 *
 * Sends the encoding types that the client can decode to the server
 *
 * Returns: TRUE if initialization was succesfull, FALSE on fail.
 */
static gboolean
rfb_decoder_state_set_encodings (RfbDecoder * decoder)
{
  GSList *encoder_list = NULL;
  guint8 *message;

  GST_DEBUG ("entered set encodings");

  encoder_list =
      g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_HEXTILE));
  encoder_list =
      g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_CORRE));
  encoder_list =
      g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_RRE));
  if (decoder->use_copyrect) {
    encoder_list =
        g_slist_append (encoder_list,
        GUINT_TO_POINTER (ENCODING_TYPE_COPYRECT));
  }
  encoder_list =
      g_slist_append (encoder_list, GUINT_TO_POINTER (ENCODING_TYPE_RAW));

  message = rfb_decoder_message_set_encodings (encoder_list);

  rfb_decoder_send (decoder, message, 4 + 4 * g_slist_length (encoder_list));

  g_free (message);

  decoder->state = rfb_decoder_state_normal;
  decoder->inited = TRUE;

  return TRUE;
}

static gboolean
rfb_decoder_state_send_client_initialisation (RfbDecoder * decoder)
{
  guint8 shared_flag;

  shared_flag = decoder->shared_flag;
  rfb_decoder_send (decoder, &shared_flag, 1);
  GST_DEBUG ("shared_flag is %d", shared_flag);

  decoder->state = rfb_decoder_state_wait_for_server_initialisation;
  return TRUE;
}

static gboolean
rfb_decoder_state_wait_for_server_initialisation (RfbDecoder * decoder)
{
  guint32 name_length;

  rfb_decoder_read (decoder, 24);

  decoder->width = RFB_GET_UINT16 (decoder->data + 0);
  decoder->height = RFB_GET_UINT16 (decoder->data + 2);
  decoder->bpp = RFB_GET_UINT8 (decoder->data + 4);
  decoder->depth = RFB_GET_UINT8 (decoder->data + 5);
  decoder->big_endian = RFB_GET_UINT8 (decoder->data + 6);
  decoder->true_colour = RFB_GET_UINT8 (decoder->data + 7);
  decoder->red_max = RFB_GET_UINT16 (decoder->data + 8);
  decoder->green_max = RFB_GET_UINT16 (decoder->data + 10);
  decoder->blue_max = RFB_GET_UINT16 (decoder->data + 12);
  decoder->red_shift = RFB_GET_UINT8 (decoder->data + 14);
  decoder->green_shift = RFB_GET_UINT8 (decoder->data + 15);
  decoder->blue_shift = RFB_GET_UINT8 (decoder->data + 16);

  GST_DEBUG ("Server Initialization");
  GST_DEBUG ("width      = %d", decoder->width);
  GST_DEBUG ("height     = %d", decoder->height);
  GST_DEBUG ("bpp        = %d", decoder->bpp);
  GST_DEBUG ("depth      = %d", decoder->depth);
  GST_DEBUG ("big_endian = %d", decoder->big_endian);
  GST_DEBUG ("true_colour= %d", decoder->true_colour);
  GST_DEBUG ("red_max    = %d", decoder->red_max);
  GST_DEBUG ("green_max  = %d", decoder->green_max);
  GST_DEBUG ("blue_max   = %d", decoder->blue_max);
  GST_DEBUG ("red_shift  = %d", decoder->red_shift);
  GST_DEBUG ("green_shift= %d", decoder->green_shift);
  GST_DEBUG ("blue_shift = %d", decoder->blue_shift);

  name_length = RFB_GET_UINT32 (decoder->data + 20);

  rfb_decoder_read (decoder, name_length);

  decoder->name = g_strndup ((gchar *) (decoder->data), name_length);
  GST_DEBUG ("name       = %s", decoder->name);

  /* check if we need cropping */

  if (decoder->offset_x > 0) {
    if (decoder->offset_x > decoder->width) {
      GST_WARNING
          ("Trying to crop more than the width of the server.  Setting offset-x to 0.");
      decoder->offset_x = 0;
    } else {
      decoder->width -= decoder->offset_x;
    }
  }
  if (decoder->offset_y > 0) {
    if (decoder->offset_y > decoder->height) {
      GST_WARNING
          ("Trying to crop more than the height of the server. Setting offset-y to 0.");
      decoder->offset_y = 0;
    } else {
      decoder->height -= decoder->offset_y;
    }
  }
  if (decoder->rect_width > 0) {
    if (decoder->rect_width > decoder->width) {
      GST_WARNING
          ("Trying to crop more than the width of the server. Setting width to %u.",
          decoder->width);
      decoder->rect_width = decoder->width;
    } else {
      decoder->width = decoder->rect_width;
    }
  }
  if (decoder->rect_height > 0) {
    if (decoder->rect_height > decoder->height) {
      GST_WARNING
          ("Trying to crop more than the height of the server. Setting height to %u.",
          decoder->height);
      decoder->rect_height = decoder->height;
    } else {
      decoder->height = decoder->rect_height;
    }
  }

  decoder->state = rfb_decoder_state_set_encodings;
  return TRUE;
}

static gboolean
rfb_decoder_state_normal (RfbDecoder * decoder)
{
  gint message_type;

  GST_DEBUG ("decoder_state_normal");

  rfb_decoder_read (decoder, 1);
  message_type = RFB_GET_UINT8 (decoder->data);

  switch (message_type) {
    case MESSAGE_TYPE_FRAMEBUFFER_UPDATE:
      GST_DEBUG ("Receiving framebuffer update");
      decoder->state = rfb_decoder_state_framebuffer_update;
      break;
    case 1:
      decoder->state = rfb_decoder_state_set_colour_map_entries;
      break;
    case 2:
      /* bell, ignored */
      decoder->state = rfb_decoder_state_normal;
      break;
    case 3:
      decoder->state = rfb_decoder_state_server_cut_text;
      break;
    default:
      g_critical ("unknown message type %d", message_type);
  }

  return TRUE;
}

static gboolean
rfb_decoder_state_framebuffer_update (RfbDecoder * decoder)
{

  rfb_decoder_read (decoder, 3);

  decoder->n_rects = RFB_GET_UINT16 (decoder->data + 1);
  GST_DEBUG ("Number of rectangles : %d", decoder->n_rects);

  decoder->state = rfb_decoder_state_framebuffer_update_rectangle;

  return TRUE;
}

static gboolean
rfb_decoder_state_framebuffer_update_rectangle (RfbDecoder * decoder)
{
  gint x, y, w, h;
  gint encoding;

  rfb_decoder_read (decoder, 12);

  x = RFB_GET_UINT16 (decoder->data + 0) - decoder->offset_x;
  y = RFB_GET_UINT16 (decoder->data + 2) - decoder->offset_y;
  w = RFB_GET_UINT16 (decoder->data + 4);
  h = RFB_GET_UINT16 (decoder->data + 6);
  encoding = RFB_GET_UINT32 (decoder->data + 8);

  GST_DEBUG ("update recieved");
  GST_DEBUG ("x:%d y:%d", x, y);
  GST_DEBUG ("w:%d h:%d", w, h);
  GST_DEBUG ("encoding: %d", encoding);

  if (((w * h) + (x * y)) > (decoder->width * decoder->height)) {
    GST_ERROR ("Desktop resize is unsupported.");
    decoder->state = NULL;
    decoder->disconnected = TRUE;
    return TRUE;
  }

  switch (encoding) {
    case ENCODING_TYPE_RAW:
      rfb_decoder_raw_encoding (decoder, x, y, w, h);
      break;
    case ENCODING_TYPE_COPYRECT:
      rfb_decoder_copyrect_encoding (decoder, x, y, w, h);
      break;
    case ENCODING_TYPE_RRE:
      rfb_decoder_rre_encoding (decoder, x, y, w, h);
      break;
    case ENCODING_TYPE_CORRE:
      rfb_decoder_corre_encoding (decoder, x, y, w, h);
      break;
    case ENCODING_TYPE_HEXTILE:
      rfb_decoder_hextile_encoding (decoder, x, y, w, h);
      break;
    default:
      g_critical ("unimplemented encoding\n");
      break;
  }
  decoder->n_rects--;
  if (decoder->n_rects == 0 || decoder->disconnected) {
    decoder->state = NULL;
  } else {
    decoder->state = rfb_decoder_state_framebuffer_update_rectangle;
  }
  return TRUE;
}

static void
rfb_decoder_raw_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
    gint rect_w, gint rect_h)
{
  gint size;
  guint8 *frame, *p;
  guint32 raw_line_size;

  raw_line_size = rect_w * decoder->bytespp;
  size = rect_h * raw_line_size;
  GST_DEBUG ("Reading %d bytes (%dx%d)", size, rect_w, rect_h);
  rfb_decoder_read (decoder, size);

  frame =
      decoder->frame + (((start_y * decoder->rect_width) +
          start_x) * decoder->bytespp);
  p = decoder->data;

  while (rect_h--) {
    memcpy (frame, p, raw_line_size);
    p += raw_line_size;
    frame += decoder->line_size;
  }
}

static void
rfb_decoder_copyrect_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
    gint rect_w, gint rect_h)
{
  guint16 src_x, src_y;
  gint line_width, copyrect_width;
  guint8 *src, *dst;

  rfb_decoder_read (decoder, 4);

  /* don't forget the offset */
  src_x = RFB_GET_UINT16 (decoder->data) - decoder->offset_x;
  src_y = RFB_GET_UINT16 (decoder->data + 2) - decoder->offset_y;
  GST_DEBUG ("Copyrect from %d %d", src_x, src_y);

  copyrect_width = rect_w * decoder->bytespp;
  line_width = decoder->line_size;
  src =
      decoder->prev_frame + ((src_y * decoder->rect_width) +
      src_x) * decoder->bytespp;
  dst =
      decoder->frame + ((start_y * decoder->rect_width) +
      start_x) * decoder->bytespp;

  while (rect_h--) {
    memcpy (dst, src, copyrect_width);
    src += line_width;
    dst += line_width;
  }
}

static void
rfb_decoder_fill_rectangle (RfbDecoder * decoder, gint x, gint y, gint w,
    gint h, guint32 color)
{
  /* fill the whole region with the same color */

  guint32 *offset;
  gint i, j;

  for (i = 0; i < h; i++) {
    offset =
        (guint32 *) (decoder->frame + ((x + (y +
                    i) * decoder->rect_width)) * decoder->bytespp);
    for (j = 0; j < w; j++) {
      *(offset++) = color;
    }
  }
}

static void
rfb_decoder_rre_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
    gint rect_w, gint rect_h)
{
  guint32 number_of_rectangles, color;
  guint16 x, y, w, h;

  rfb_decoder_read (decoder, 4 + decoder->bytespp);
  number_of_rectangles = RFB_GET_UINT32 (decoder->data);
  color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + 4)));

  GST_DEBUG ("number of rectangles :%d", number_of_rectangles);

  /* color the background of this rectangle */
  rfb_decoder_fill_rectangle (decoder, start_x, start_y, rect_w, rect_h, color);

  while (number_of_rectangles--) {

    rfb_decoder_read (decoder, decoder->bytespp + 8);
    color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
    x = RFB_GET_UINT16 (decoder->data + decoder->bytespp);
    y = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 2);
    w = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 4);
    h = RFB_GET_UINT16 (decoder->data + decoder->bytespp + 6);

    /* draw the rectangle in the foreground */
    rfb_decoder_fill_rectangle (decoder, start_x + x, start_y + y, w, h, color);
  }
}

static void
rfb_decoder_corre_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
    gint rect_w, gint rect_h)
{
  guint32 number_of_rectangles, color;
  guint8 x, y, w, h;

  rfb_decoder_read (decoder, 4 + decoder->bytespp);
  number_of_rectangles = RFB_GET_UINT32 (decoder->data);
  color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + 4)));
  g_free (decoder->data);

  GST_DEBUG ("number of rectangles :%d", number_of_rectangles);

  /* color the background of this rectangle */
  rfb_decoder_fill_rectangle (decoder, start_x, start_y, rect_w, rect_h, color);

  while (number_of_rectangles--) {

    rfb_decoder_read (decoder, decoder->bytespp + 4);
    color = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
    x = RFB_GET_UINT8 (decoder->data + decoder->bytespp);
    y = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 1);
    w = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 2);
    h = RFB_GET_UINT8 (decoder->data + decoder->bytespp + 3);

    /* draw the rectangle in the foreground */
    rfb_decoder_fill_rectangle (decoder, start_x + x, start_y + y, w, h, color);

    g_free (decoder->data);
  }
}

static void
rfb_decoder_hextile_encoding (RfbDecoder * decoder, gint start_x, gint start_y,
    gint rect_w, gint rect_h)
{
  gint32 x, x_count G_GNUC_UNUSED, x_end, x_max, x_max_16;
  gint32 y, y_count G_GNUC_UNUSED, y_end, y_max, y_max_16;
  guint8 subencoding, nr_subrect, xy, wh;
  guint32 background, foreground;

  foreground = background = 0;
  x_end = rect_w % 16;
  x_count = rect_w / 16 + (x_end > 0 ? 1 : 0);
  y_end = rect_h % 16;
  y_count = rect_h / 16 + (y_end > 0 ? 1 : 0);
  x_max = start_x + rect_w;
  y_max = start_y + rect_h;
  x_max_16 = x_max - 16;
  y_max_16 = y_max - 16;

  for (y = start_y; y < y_max; y += 16) {
    for (x = start_x; x < x_max; x += 16) {

      rfb_decoder_read (decoder, 1);
      subencoding = RFB_GET_UINT8 (decoder->data);

      if (subencoding & SUBENCODING_RAW) {
        rfb_decoder_raw_encoding (decoder, x, y,
            (x <= x_max_16 ? 16 : x_end), (y <= y_max_16 ? 16 : y_end));
        continue;
      }

      if (subencoding & SUBENCODING_BACKGROUND) {
        rfb_decoder_read (decoder, decoder->bytespp);
        background = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
      }
      rfb_decoder_fill_rectangle (decoder, x, y,
          (x <= x_max_16 ? 16 : x_end), (y <= y_max_16 ? 16 : y_end),
          background);

      if (subencoding & SUBENCODING_FOREGROUND) {
        rfb_decoder_read (decoder, decoder->bytespp);
        foreground = GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data)));
      }

      if (subencoding & SUBENCODING_ANYSUBRECTS) {
        rfb_decoder_read (decoder, 1);
        nr_subrect = RFB_GET_UINT8 (decoder->data);
      } else {
        continue;
      }

      if (subencoding & SUBENCODING_SUBRECTSCOLORED) {
        guint offset = 0;

        rfb_decoder_read (decoder, nr_subrect * (2 + decoder->bytespp));

        while (nr_subrect--) {
          foreground =
              GUINT32_SWAP_LE_BE ((RFB_GET_UINT32 (decoder->data + offset)));
          offset += decoder->bytespp;
          xy = RFB_GET_UINT8 (decoder->data + offset++);
          wh = RFB_GET_UINT8 (decoder->data + offset++);
          rfb_decoder_fill_rectangle (decoder, x + (xy >> 4), y + (xy & 0xF),
              1 + (wh >> 4), 1 + (wh & 0xF), foreground);
        }
      } else {
        guint offset = 0;

        rfb_decoder_read (decoder, 2 * nr_subrect);

        while (nr_subrect--) {
          xy = RFB_GET_UINT8 (decoder->data + offset++);
          wh = RFB_GET_UINT8 (decoder->data + offset++);
          rfb_decoder_fill_rectangle (decoder, x + (xy >> 4), y + (xy & 0xF),
              1 + (wh >> 4), 1 + (wh & 0xF), foreground);
        }
      }
    }
  }
}

static gboolean
rfb_decoder_state_set_colour_map_entries (RfbDecoder * decoder)
{
  g_critical ("not implemented");

  return FALSE;
}

static gboolean
rfb_decoder_state_server_cut_text (RfbDecoder * decoder)
{
  gint cut_text_length;

  /* 3 bytes padding, 4 bytes cut_text_length */
  rfb_decoder_read (decoder, 7);
  cut_text_length = RFB_GET_UINT32 (decoder->data + 3);

  rfb_decoder_read (decoder, cut_text_length);
  GST_DEBUG ("rfb_decoder_state_server_cut_text: throw away '%s'",
      decoder->data);

  decoder->state = rfb_decoder_state_normal;
  return TRUE;
}
