/*
 * Copyright (C) 2013 Red Hat, Inc.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#include "config.h"

#include "websocket.h"

static WebSocketConnection *web_socket = NULL;
static GString *buffer = NULL;
static GMainLoop *loop = NULL;

static void
on_release_buffer (gpointer user_data)
{
  if (buffer == NULL)
    buffer = user_data;
  else
    g_string_free (user_data, TRUE);
}

static gboolean
on_input_data (GIOChannel *channel,
               GIOCondition cond,
               gpointer user_data)
{
  GError *error = NULL;
  GIOStatus status;
  GBytes *msg;
  gsize line;

  if (buffer == NULL)
    buffer = g_string_sized_new (1024);
  status = g_io_channel_read_line_string (channel, buffer, &line, &error);
  switch (status)
    {
    case G_IO_STATUS_ERROR:
      g_critical ("Failed to read input: %s", error->message);
      g_error_free (error);
      g_main_loop_quit (loop);
      return FALSE;
    case G_IO_STATUS_EOF:
      web_socket_connection_close (web_socket, WEB_SOCKET_CLOSE_GOING_AWAY, "going away");
      return FALSE;
    case G_IO_STATUS_AGAIN:
      return TRUE;
    case G_IO_STATUS_NORMAL:
      msg = g_bytes_new_with_free_func (buffer->str, line, on_release_buffer, buffer);
      buffer = NULL;
      web_socket_connection_send (web_socket, WEB_SOCKET_DATA_TEXT, NULL, msg);
      g_bytes_unref (msg);
      return TRUE;
    default:
      g_assert_not_reached ();
    }
}

static void
on_web_socket_open (WebSocketConnection *ws,
                    gpointer unused)
{
  GIOChannel *channel;

  g_printerr ("WebSocket: opened %s with %s\n",
              web_socket_connection_get_protocol (ws),
              web_socket_connection_get_url (ws));

  channel = g_io_channel_unix_new (0);
  g_io_add_watch (channel, G_IO_IN, on_input_data, NULL);
  g_io_channel_unref (channel);
}

static void
on_web_socket_message (WebSocketConnection *ws,
                       WebSocketDataType type,
                       GBytes *message)
{
  const gchar *data;
  gsize len;

  g_printerr ("WebSocket: message 0x%x\n", (int)type);

  data = g_bytes_get_data (message, &len);
  g_print ("%.*s\n", (int)len, data);
}

static void
on_web_socket_close (WebSocketConnection *ws)
{
  gushort code;

  code = web_socket_connection_get_close_code (ws);
  if (code != 0)
    g_printerr ("WebSocket: close: %d %s\n", code,
                web_socket_connection_get_close_data (ws));
  else
    g_printerr ("WebSocket: close\n");

  g_main_loop_quit (loop);
}

int
main (int argc,
      char *argv[])
{
  GOptionContext *options;
  gchar **protocols = NULL;
  gchar *origin = NULL;
  GError *error = NULL;

  GOptionEntry entries[] = {
    { "origin", 0, 0, G_OPTION_ARG_STRING, &origin, "Web Socket Origin", "url" },
    { "protocol", 0, 0, G_OPTION_ARG_STRING_ARRAY, &protocols, "Web Socket Protocols", "proto" },
    { NULL }
  };

  signal (SIGPIPE, SIG_IGN);
  options = g_option_context_new ("URL");
  g_option_context_add_main_entries (options, entries, NULL);
  if (!g_option_context_parse (options, &argc, &argv, &error))
    {
      g_printerr ("frob-websocket: %s\n", error->message);
      return 2;
    }

  if (argc != 2)
    {
      g_printerr ("frob-websocket: specify the url to connect to\n");
      return 2;
    }

  loop = g_main_loop_new (NULL, FALSE);

  web_socket = web_socket_client_new (argv[1], origin, (const gchar **)protocols);
  g_signal_connect (web_socket, "open", G_CALLBACK (on_web_socket_open), NULL);
  g_signal_connect (web_socket, "message", G_CALLBACK (on_web_socket_message), NULL);
  g_signal_connect (web_socket, "close", G_CALLBACK (on_web_socket_close), NULL);

  g_main_loop_run (loop);

  g_option_context_free (options);
  g_object_unref (web_socket);
  g_free (origin);
  if (buffer)
    g_string_free (buffer, TRUE);
  g_strfreev (protocols);

  return 0;
}
