/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou 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.
 *
 *  lebiniou 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 lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include "bulfius.h"
#include "context.h"
#include "defaults.h"
#include "pthread_utils.h"


extern uint8_t max_fps;
extern uint16_t http_port;
extern char *http_instance;
extern uint8_t launch_browser;
static pthread_t ping_thread;
uint8_t vui_started = 0;


#define VUI_PAGES 7
static const char *vui_pages[VUI_PAGES] = { "/", "/colormaps", "/images", "/settings", "/plugins", "/remote", "/documentation" };


static void *
vui_thread(void *attr)
{
  Context_t *ctx = (Context_t *)attr;

  while (ctx->running) {
    json_t *ping = json_pack("{s{sisisIsi}}",
                             "ping",
                             "fps", Context_fps(ctx),
                             "maxFps", ctx->max_fps,
                             "uptime", (json_int_t)Timer_elapsed(ctx->timer),
                             "clients", g_slist_length(ctx->ws_clients));
#ifdef WITH_WEBCAM
    json_object_set_new(json_object_get(ping, "ping"), "currentWebcam", json_integer(ctx->cam));
#endif
    bulfius_websocket_broadcast_json_message(ctx, ping, NULL);
    json_decref(ping);
    ms_sleep(1000);
  }

  return NULL;
}


void
Context_start_vui(Context_t *ctx)
{
  xpthread_mutex_init(&ctx->ws_clients_mutex, NULL);

    // Yder
#ifdef DEBUG
  y_init_logs("Ulfius", Y_LOG_MODE_CONSOLE|Y_LOG_MODE_FILE, Y_LOG_LEVEL_DEBUG, "ulfius.log", "Logging Ulfius debug messages");
#endif

  // Initialize instance
  if (ulfius_init_instance(&ctx->vui_instance, http_port, NULL, NULL) != U_OK) {
    xerror("ulfius_init_instance error on port %u, aborting\n", http_port);
  }

  // Websockets
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_WEBSOCKET, NULL, 0, &callback_websocket, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_PREVIEW_WS, NULL, 0, &callback_preview_websocket, ctx);
  // Endpoints
  // OPTIONS
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_OPTIONS, NULL, BULFIUS_STAR, 0, &callback_options, NULL);
  // GET
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_PLUGINS, NULL, 0, &callback_get_plugins, NULL);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_COLORMAP, NULL, 0, &callback_get_colormap, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_IMAGE, NULL, 0, &callback_get_image, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_SETTINGS, NULL, 0, &callback_get_settings, ctx);
  // POST
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_PLUGINS, NULL, 0, &callback_post_plugins, NULL);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_SETTINGS, NULL, 0, &callback_post_settings, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_FAVORITES, NULL, 0, &callback_post_settings, ctx); // FIXME VUI favorites duplicate
  // Vui endpoints
  for (uint8_t i = 0; i < VUI_PAGES; i++) {
    ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, vui_pages[i], NULL, 0, &callback_vui_get_static, (void *)BULFIUS_INDEX);
  }
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_FAVICO_URL, NULL, 0, &callback_vui_get_static, (void *)BULFIUS_FAVICO);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_CSS_ROOT, BULFIUS_STATIC_FMT, 0, &callback_vui_get_static, (void *)BULFIUS_CSS);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_IMG_ROOT, BULFIUS_STATIC_FMT, 0, &callback_vui_get_static, (void *)BULFIUS_IMG);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_JS_ROOT, BULFIUS_STATIC_FMT, 0, &callback_vui_get_static, (void *)BULFIUS_JS);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_MEDIA_ROOT, BULFIUS_STATIC_FMT, 0, &callback_vui_get_static, (void *)BULFIUS_MEDIA);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_DEMOS_ROOT, BULFIUS_STATIC_FMT, 0, &callback_vui_get_static, (void *)BULFIUS_DEMOS);
  // REST API
  // GET
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_COMMANDS, NULL, 0, &callback_get_commands, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_FRAME, NULL, 0, &callback_get_frame, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_PARAMETERS, BULFIUS_PARAMETERS_FMT, 0, &callback_get_parameters, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_SEQUENCE, NULL, 0, &callback_get_sequence, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_STATISTICS, NULL, 0, &callback_get_statistics, ctx);
  // POST
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_SEQUENCES, NULL, 0, &callback_post_sequences, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_COMMAND, BULFIUS_COMMAND_FMT, 0, &callback_post_command, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_PARAMETERS, BULFIUS_PARAMETERS_FMT, 0, &callback_post_parameters, ctx);
  ulfius_add_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_SEQUENCE, NULL, 0, &callback_post_sequence, ctx);

  // Start the framework
  if (ulfius_start_framework(&ctx->vui_instance) == U_OK) {
    VERBOSE(printf("[i] Started ulfius framework on port %d\n", ctx->vui_instance.port));
  } else {
    xerror("Error starting ulfius framework on port %d\n", ctx->vui_instance.port);
  }
  xpthread_create(&ping_thread, NULL, vui_thread, (void *)ctx);

  vui_started = 1;

  if (launch_browser) {
    gchar *cmd = NULL;
    if (NULL != http_instance) {
      cmd = g_strdup_printf("xdg-open http://%s.localhost:%u", http_instance, http_port);
    } else {
      cmd = g_strdup_printf("xdg-open http://localhost:%u", http_port);
    }
    int ret = system(cmd);
    if (ret == -1) {
      xperror("system");
    }
    g_free(cmd);
  }
}


void
Context_stop_vui(Context_t *ctx)
{
  xpthread_join(ping_thread, NULL);
  // Websockets
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_WEBSOCKET, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_PREVIEW_WS, NULL);
  // Endpoints
  // OPTIONS
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_OPTIONS, NULL, BULFIUS_STAR);
  // GET
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_PLUGINS, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_COLORMAP, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_IMAGE, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_SETTINGS, NULL);
  // POST
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_PLUGINS, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_SETTINGS, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_FAVORITES, NULL);
  // Vui endpoints
  for (uint8_t i = 0; i < VUI_PAGES; i++) {
    ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, vui_pages[i], NULL);
  }
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_FAVICO_URL, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_CSS_ROOT, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_IMG_ROOT, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_JS_ROOT, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_MEDIA_ROOT, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_DEMOS_ROOT, NULL);
  // REST API
  // GET
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_COMMANDS, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_FRAME, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_PARAMETERS, BULFIUS_PARAMETERS_FMT);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_SEQUENCE, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_GET, BULFIUS_STATISTICS, NULL);
  // POST
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_SEQUENCES, NULL);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_COMMAND, BULFIUS_COMMAND_FMT);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_PARAMETERS, BULFIUS_PARAMETERS_FMT);
  ulfius_remove_endpoint_by_val(&ctx->vui_instance, BULFIUS_POST, BULFIUS_SEQUENCE, NULL);

  ulfius_stop_framework(&ctx->vui_instance);
  ulfius_clean_instance(&ctx->vui_instance);
  xpthread_mutex_destroy(&ctx->ws_clients_mutex);

  vui_started = 0;
}
