/*
 *  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 "globals.h"
#include "context.h"
#include "biniou.h"
#include "ui_commands.h"


// #define DEBUG_COMMANDS

extern uint8_t max_fps;


json_t *
Context_process_ui_command(Context_t *ctx, const enum Command cmd, const json_t *arg)
{
  json_t *res = NULL;

  switch (cmd) {
    case UI_CMD_CONNECT:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_CONNECT\n");
#endif
      res = Context_get_state(ctx);
      break;

    case UI_CMD_APP_SELECT_PLUGIN: {
      const char *param = json_string_value(arg);

      if (NULL != param) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_APP_SELECT_PLUGIN %s\n", param);
#endif
        Plugin_t *p = Plugins_find(param);

        if (NULL != p) {
          Plugins_select(plugins, p);
          res = json_pack("{ss ss so*}",
                          "selectedPlugin", p->name,
                          "selectedPluginDname", p->dname,
                          "parameters", NULL != p->parameters ? p->parameters(ctx, NULL, 0) : NULL);
        }
      }
    }
    break;

    case UI_CMD_APP_SET_AUTO_MODE: {
      json_t *j_what = json_object_get(arg, "what");
      json_t *j_mode = json_object_get(arg, "mode");

      if (json_is_string(j_what) && json_is_string(j_mode)) {
        const char *what = json_string_value(j_what);
        const char *mode = json_string_value(j_mode);

#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_APP_SET_AUTO_MODE %s %s\n", what, mode);
#endif
        if (is_equal(what, "colormaps")) {
          Shuffler_set_mode(ctx->cf->shf, Shuffler_parse_mode(mode));
          Shuffler_restart(ctx->cf->shf);
          res = json_pack("{ssss}", "what", what, "mode", mode);
        } else if (is_equal(what, "images")) {
          Shuffler_set_mode(ctx->imgf->shf, Shuffler_parse_mode(mode));
          Shuffler_restart(ctx->imgf->shf);
          res = json_pack("{ssss}", "what", what, "mode", mode);
        } else if (is_equal(what, "sequences")) {
          Shuffler_set_mode(sequences->shuffler, Shuffler_parse_mode(mode));
          Shuffler_restart(sequences->shuffler);
          res = json_pack("{ssss}", "what", what, "mode", mode);
        }
#ifdef WITH_WEBCAM
        else if ((ctx->webcams > 1) && is_equal(what, "webcams")) {
          Shuffler_set_mode(ctx->webcams_shuffler, Shuffler_parse_mode(mode));
          Shuffler_restart(ctx->webcams_shuffler);
          res = json_pack("{ssss}", "what", what, "mode", mode);
        }
#endif
      }
    }
    break;

  case UI_CMD_APP_SET_BANDPASS: {
      json_t *j_min = json_object_get(arg, "min");
      json_t *j_max = json_object_get(arg, "max");

      if (json_is_integer(j_min) && json_is_integer(j_max)) {
        int min = json_integer_value(j_min);
        int max = json_integer_value(j_max);

#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_SET_BANDPASS min= %d, max= %d\n", min, max);
#endif
        ctx->bandpass_min = ctx->sm->cur->bandpass_min = min;
        ctx->bandpass_max = ctx->sm->cur->bandpass_max = max;
        res = json_pack("[ii]", min, max);
      }
    }
    break;

    case UI_CMD_APP_SET_DELAY: {
      json_t *j_what = json_object_get(arg, "what");
      json_t *j_min = json_object_get(arg, "min");
      json_t *j_max = json_object_get(arg, "max");
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_APP_SET_DELAY %p %p %p\n", j_what, j_min, j_max);
#endif

      if ((NULL != j_what) && (NULL != j_min) && (NULL != j_max)) {
        const char *what = json_string_value(j_what);
        int min = json_integer_value(j_min);
        int max = json_integer_value(j_max);

        if (NULL != what) {
#ifdef DEBUG_COMMANDS
          printf(">>> UI_CMD_APP_SET_DELAY what= %s, min= %d, max= %d\n", what, min, max);
#endif
          if (is_equal(what, "colormaps")) {
            biniou_set_delay(BD_COLORMAPS, min, max);
            biniou_get_delay(BD_COLORMAPS, &min, &max);
            Alarm_update(ctx->a_cmaps, min, max);
          } else if (is_equal(what, "images")) {
            biniou_set_delay(BD_IMAGES, min, max);
            biniou_get_delay(BD_IMAGES, &min, &max);
            Alarm_update(ctx->a_images, min, max);
          } else if (is_equal(what, "sequences")) {
            biniou_set_delay(BD_SEQUENCES, min, max);
            biniou_get_delay(BD_SEQUENCES, &min, &max);
            Alarm_update(ctx->a_random, min, max);
          }
#ifdef WITH_WEBCAM
          else if (is_equal(what, "webcams")) {
            biniou_set_delay(BD_WEBCAMS, min, max);
            biniou_get_delay(BD_WEBCAMS, &min, &max);
            Alarm_update(ctx->a_webcams, min, max);
          }
#endif
          res = json_pack("{ss s[ii]}",
                          "what", what,
                          "range", min, max);
        }
      }
    }
    break;

    case UI_CMD_APP_SET_FADE_DELAY:
      if (json_is_integer(arg)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_APP_SET_FADE_DELAY %f\n", json_integer_value(arg) / 1000.0);
#endif
        fade_delay = json_integer_value(arg) / 1000.0;
        res = json_pack("{si}", "fadeDelay", json_integer_value(arg));
      }
      break;

    case UI_CMD_APP_SET_MAX_FPS:
      if (json_is_integer(arg)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_APP_SET_MAX_FPS %lld\n", json_integer_value(arg));
#endif
        Context_set_max_fps(ctx, json_integer_value(arg));
        res = json_pack("{si}", "maxFps", ctx->max_fps);
      }
      break;

    case UI_CMD_APP_SET_VOLUME_SCALE:
      if ((NULL != ctx->input) && json_is_integer(arg)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_APP_SET_VOLUME_SCALE %f\n", json_integer_value(arg) / 1000.0);
#endif
        Input_set_volume_scale(ctx->input, json_integer_value(arg) / 1000.0);
        res = json_pack("{si}", "volumeScale", json_integer_value(arg));
      }
      break;

    case UI_CMD_APP_SET_3D_ROTATION_AMOUNT:
      if (json_is_real(arg)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_APP_SET_3D_ROTATION_AMOUNT %f\n", json_real_value(arg));
#endif
        ctx->params3d.rotate_amount = json_real_value(arg);
        res = Params3d_to_json(&ctx->params3d);
      }
      break;

    case UI_CMD_APP_SET_3D_ROTATION_FACTOR:
      if (json_is_object(arg)) {
        json_t *axis = json_object_get(arg, "axis");
        json_t *value = json_object_get(arg, "value");

        if ((NULL != axis) && (NULL != value)) {
#ifdef DEBUG_COMMANDS
          printf(">>> UI_CMD_APP_SET_3D_ROTATION_FACTOR %s: %lld\n", json_string_value(axis), json_integer_value(value));
#endif
          if (is_equal(json_string_value(axis), "x")) {
            Params3d_set_rotate_factor(&ctx->params3d, X_AXIS, json_integer_value(value));
          } else if (is_equal(json_string_value(axis), "y")) {
            Params3d_set_rotate_factor(&ctx->params3d, Y_AXIS, json_integer_value(value));
          } else if (is_equal(json_string_value(axis), "z")) {
            Params3d_set_rotate_factor(&ctx->params3d, Z_AXIS, json_integer_value(value));
          }
          res = Params3d_to_json(&ctx->params3d);
        }
      }
      break;

    case UI_CMD_COL_PREVIOUS_N:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_COL_PREVIOUS_N\n");
#endif
      if (json_is_integer(arg)) {
        CmapFader_prev_n(ctx->cf, json_integer_value(arg));
        res = CmapFader_command_result(ctx->cf);
      }
      break;

    case UI_CMD_COL_NEXT_N:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_COL_NEXT_N\n");
#endif
      if (json_is_integer(arg)) {
        CmapFader_next_n(ctx->cf, json_integer_value(arg));
        res = CmapFader_command_result(ctx->cf);
      }
      break;

    case UI_CMD_SELECT_ITEM:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_SELECT_ITEM\n");
#endif
      if (json_is_object(arg)) {
        json_t *item = json_object_get(arg, "item");
        json_t *index = json_object_get(arg, "index");
        json_t *mode = json_object_get(arg, "mode");

        if (NULL != item) {
          const char *it = json_string_value(item);

          if (NULL != index) {
            if (is_equal(it, "colormap")) {
              CmapFader_use(ctx->cf, json_integer_value(index));
              res = CmapFader_command_result(ctx->cf);
              json_object_set(res, "item", item);
            } else if (is_equal(it, "image")) {
              ImageFader_use(ctx->imgf, json_integer_value(index));
              res = ImageFader_command_result(ctx->imgf);
              json_object_set(res, "item", item);
            }
          } else if (json_is_string(mode) && is_equal(it, "boundaryMode")) {
            const Boundaries_t boundary_mode = Params3d_str2boundary(json_string_value(mode));
            ctx->params3d.boundary = boundary_mode;
            res = json_pack("{ssss}", "item", it, "mode", Params3d_boundary2str(boundary_mode));
          }
        }
      }
      break;

    case UI_CMD_IMG_PREVIOUS_N:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_IMG_PREVIOUS_N\n");
#endif
      if (json_is_integer(arg)) {
        ImageFader_prev_n(ctx->imgf, json_integer_value(arg));
        res = ImageFader_command_result(ctx->imgf);
      }
      break;

    case UI_CMD_IMG_NEXT_N:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_IMG_NEXT_N\n");
#endif
      if (json_is_integer(arg)) {
        ImageFader_next_n(ctx->imgf, json_integer_value(arg));
        res = ImageFader_command_result(ctx->imgf);
      }
      break;

    case UI_CMD_NOOP:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_NOOP\n");
#endif
      res = json_pack("{sO}", "command", arg);
      break;

    case UI_CMD_OUTPUT: {
      json_t *plugin = json_object_get(arg, "plugin");
      json_t *command = json_object_get(arg, "command");

      if (json_is_string(plugin)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_OUTPUT\n");
#endif
        for (GSList *outputs = ctx->outputs ; NULL != outputs; outputs = g_slist_next(outputs)) {
          Plugin_t *output = (Plugin_t *)outputs->data;

          if (is_equal(output->name, json_string_value(plugin)) && (NULL != output->command)) {
            res = json_pack("{ssso}", "plugin", output->name, "result", output->command(ctx, command));
          }
        }
      }
    }
    break;

    case UI_CMD_SEQ_RENAME: {
      if (json_is_string(arg)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_SEQ_RENAME: arg= '%s'\n", json_string_value(arg));
#endif
        const char *str = json_string_value(arg);
        if ((NULL != ctx->sm->curseq) && safe_filename(str)) {
          Sequence_t *s = ctx->sm->cur;
          if (NULL != s->name) {
            Sequence_t *curseq = (Sequence_t *)ctx->sm->curseq->data;
            gchar *old_name = g_strdup_printf("%s/%s.json", Sequences_get_dir(), s->name);
            gchar *new_name = g_strdup_printf("%s/%s.json", Sequences_get_dir(), str);

            if (rename(old_name, new_name) == 0) {
              res = json_pack("{ss}", "newName", str);
              xfree(s->name);
              s->name = strdup(str);
              xfree(curseq->name);
              curseq->name = strdup(json_string_value(arg));
            } else {
              res = json_pack("{ss}", "error", strerror(errno), "name", s->name);
            }
            g_free(old_name);
            g_free(new_name);
          }
        } else {
          res = json_pack("{ss ss}",
                          "error", UNSAVED_SEQUENCE_ERROR,
                          "name", UNSAVED_SEQUENCE);
        }
      }
    }
      break;

    case UI_CMD_SEQ_REORDER:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_SEQ_REORDER\n");
#endif
      SequenceManager_reorder(ctx->sm->cur, arg);
      res = json_pack("{so}", "sequence", Sequence_to_json(ctx, ctx->sm->cur, 1, 0, ctx->sm->cur->name));
      break;

  case UI_CMD_SEQ_SET_LAYER_MODE: {
    const json_t *j_plugin = json_object_get(arg, "plugin");
    const json_t *j_mode = json_object_get(arg, "mode");

    if (json_is_string(j_plugin) && json_is_integer(j_mode)) {
#ifdef DEBUG_COMMANDS
      printf(">>> CMD_SEQ_SET_LAYER_MODE plugin: %s, mode: %s\n", json_string_value(j_plugin), LayerMode_to_string(json_integer_value(j_mode)));
#endif
      SequenceManager_set_layer_mode(ctx->sm->cur, json_string_value(j_plugin), json_integer_value(j_mode));
      res = json_pack("{so}", "sequence", Sequence_to_json(ctx, ctx->sm->cur, 1, 0, (NULL == ctx->sm->cur->name) ? UNSAVED_SEQUENCE : ctx->sm->cur->name));
    }
  }
  break;

    case UI_CMD_SEQ_SET_PARAM_PLAYLIST_VALUE: {
      json_t *j_param = json_object_get(arg, "selectedParam");
      json_t *j_value = json_object_get(arg, "value");

      if (json_is_array(j_value)) {
#ifdef DEBUG_COMMANDS
        char *tmp = json_dumps(j_value, 0);
        printf(">>> UI_CMD_SEQ_SET_PARAM_PLAYLIST_VALUE %lld: %s\n", json_integer_value(j_param), tmp);
        xfree(tmp);
#endif
        plugins->selected->selected_param = json_integer_value(j_param);
        res = json_pack("{so}", "parameters", plugin_parameter_set_selected_from_playlist(ctx, j_value));
      }
    }
    break;

    case UI_CMD_SEQ_SET_PARAM_SLIDER_VALUE: {
      json_t *j_param = json_object_get(arg, "selectedParam");
      json_t *j_value = json_object_get(arg, "value");

      if (json_is_integer(j_value)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_SEQ_SET_PARAM_SLIDER_VALUE %lld: %lld\n", json_integer_value(j_param), json_integer_value(j_value));
#endif
        plugins->selected->selected_param = json_integer_value(j_param);
        res = json_pack("{so}", "parameters", plugin_parameter_set_selected_from_slider(ctx, json_integer_value(j_value)));
      }
    }
    break;

    case UI_CMD_SEQ_SET_PARAM_SELECT_VALUE: {
      json_t *j_param = json_object_get(arg, "selectedParam");
      json_t *j_value = json_object_get(arg, "value");

      if (json_is_string(j_value)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_SEQ_SET_PARAM_SELECT_VALUE %lld: %s\n", json_integer_value(j_param), json_string_value(j_value));
#endif
        plugins->selected->selected_param = json_integer_value(j_param);
        res = json_pack("{so}", "parameters", plugin_parameter_set_selected_from_select(ctx, json_string_value(j_value)));
      }
    }
    break;

    case UI_CMD_SEQ_SET_PARAM_CHECKBOX_VALUE: {
      json_t *j_param = json_object_get(arg, "selectedParam");
      json_t *j_value = json_object_get(arg, "value");

      if (json_is_boolean(j_value)) {
#ifdef DEBUG_COMMANDS
        printf(">>> UI_CMD_SEQ_SET_PARAM_CHECKBOX_VALUE %lld: %s\n", json_integer_value(j_param), json_boolean_value(j_value) ? "on" : "off");
#endif
        plugins->selected->selected_param = json_integer_value(j_param);
        res = json_pack("{so}", "parameters", plugin_parameter_set_selected_from_checkbox(ctx, json_boolean_value(j_value)));
      }
    }
    break;

    case UI_CMD_APP_TOGGLE_RANDOM_SCHEMES:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_APP_TOGGLE_RANDOM_SCHEMES\n");
#endif
      if (ctx->random_mode == BR_NONE) {
        ctx->random_mode = BR_SCHEMES;
      } else if (ctx->random_mode == BR_SEQUENCES) {
        ctx->random_mode |= BR_SCHEMES;
      } else {
        ctx->random_mode &= ~BR_SCHEMES;
      }
      res = json_pack("{sb sb}",
                      "randomSchemes", ctx->random_mode & BR_SCHEMES,
                      "randomSequences", ctx->random_mode & BR_SEQUENCES);
      break;

    case UI_CMD_APP_TOGGLE_RANDOM_SEQUENCES:
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_APP_TOGGLE_RANDOM_SEQUENCES\n");
#endif
      if (ctx->random_mode == BR_NONE) {
        ctx->random_mode = BR_SEQUENCES;
      } else if (ctx->random_mode == BR_SCHEMES) {
        ctx->random_mode |= BR_SEQUENCES;
      } else {
        ctx->random_mode &= ~BR_SEQUENCES;
      }
      res = json_pack("{sb sb}",
                      "randomSchemes", ctx->random_mode & BR_SCHEMES,
                      "randomSequences", ctx->random_mode & BR_SEQUENCES);
      break;

    case UI_CMD_TRACKBALL_ON_DRAG_START: {
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_TRACKBALL_ON_DRAG_START\n");
#endif
      json_t *json_x = json_object_get(arg, "x");
      json_t *json_y = json_object_get(arg, "y");

      if ((NULL != json_x) && (NULL != json_y)) {
        ctx->params3d.xs = json_number_value(json_x);
        ctx->params3d.ys = json_number_value(json_y);
      }
    }
    break;

    case UI_CMD_TRACKBALL_ON_DRAG_MOVE: {
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_TRACKBALL_ON_DRAG_MOVE\n");
#endif
      json_t *json_x = json_object_get(arg, "x");
      json_t *json_y = json_object_get(arg, "y");

      if ((NULL != json_x) && (NULL != json_y)) {
        ctx->params3d.xe = json_number_value(json_x);
        ctx->params3d.ye = json_number_value(json_y);
        Params3d_rotate(&ctx->params3d);
      }
    }
    break;

    case UI_CMD_TRACKBALL_ON_MOUSE_WHEEL: {
#ifdef DEBUG_COMMANDS
      printf(">>> UI_CMD_TRACKBALL_ON_MOUSE_WHEEL\n");
#endif
      if (json_is_integer(arg)) {
        if (json_integer_value(arg) > 0) { // scroll up
          Params3d_zoom_in(&ctx->params3d);
        } else { // scroll down
          Params3d_zoom_out(&ctx->params3d);
        }
      }
    }
    break;

  case UI_CMD_BANK:
    return Bank_command(ctx, arg);
    break;

  default:
    break;
  }

  return res;
}
