/*
 *  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 "translation.h"
#include "parameters.h"


uint32_t version = 0;
uint32_t options = BO_DISPLACE | BO_WARP;
char dname[] = "Hurricane";
char desc[] = "Hurricane effect";


static Translation_t *t_hurricane = NULL;

/* parameters */
static int random_mode = 1;
static int speed = 100;
static int slowX = 1;
static int slowY = 1;
static int reverse = 0;
static double xCenter = 0.5;
static double yCenter = 0.5;

static int Randomness;


json_t *
get_parameters(const uint8_t fetch_all)
{
  json_t *params = json_object();

  plugin_parameters_add_boolean(params, BPP_RANDOM_MODE, random_mode, "Random mode");
  if (fetch_all || !random_mode) {
    plugin_parameters_add_int(params, BPP_SPEED, speed, 30, 300, 1, "Speed");
    plugin_parameters_add_boolean(params, BPP_SLOW_X, slowX, "Slowdown on X axis");
    plugin_parameters_add_boolean(params, BPP_SLOW_Y, slowY, "Slowdown on Y axis");
    plugin_parameters_add_boolean(params, BPP_REVERSE, reverse, "Reverse rotation");
    plugin_parameters_add_double(params, BPP_CENTER_X, xCenter, 1./8., 7./8., 0.01, NULL);
    plugin_parameters_add_double(params, BPP_CENTER_Y, yCenter, 1./8., 7./8., 0.01, NULL);
  }

  return params;
}


void
set_parameters(const Context_t *ctx, const json_t *in_parameters)
{
  int reload = 0;

  reload |= plugin_parameter_parse_int_range(in_parameters, BPP_SPEED, &speed) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_double_range(in_parameters, BPP_CENTER_X, &xCenter) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_double_range(in_parameters, BPP_CENTER_Y, &yCenter) & PLUGIN_PARAMETER_CHANGED;

  // v1 API compat
  reload |= plugin_parameter_parse_int_range(in_parameters, BPP_RANDOM_MODE, &random_mode) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_int_range(in_parameters, BPP_SLOW_X, &slowX) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_int_range(in_parameters, BPP_SLOW_Y, &slowY) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_int_range(in_parameters, BPP_REVERSE, &reverse) & PLUGIN_PARAMETER_CHANGED;

  // v2 API
  reload |= plugin_parameter_parse_boolean(in_parameters, BPP_RANDOM_MODE, &random_mode) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_boolean(in_parameters, BPP_SLOW_X, &slowX) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_boolean(in_parameters, BPP_SLOW_Y, &slowY) & PLUGIN_PARAMETER_CHANGED;
  reload |= plugin_parameter_parse_boolean(in_parameters, BPP_REVERSE, &reverse) & PLUGIN_PARAMETER_CHANGED;

  if (reload) {
    Translation_batch_init(t_hurricane);
  }
}


json_t *
parameters(const Context_t *ctx, const json_t *in_parameters, const uint8_t fetch_all)
{
  if (NULL != in_parameters) {
    set_parameters(ctx, in_parameters);
  }

  return get_parameters(fetch_all);
}


static Map_t
hurricane(const short x, const short y)
{
  int  dx, dy, map_x, map_y;
  int  speedFactor;
  long sp = speed;
  Map_t m;

  if (Randomness) {
    speedFactor = b_rand_uint32_range(0, (Randomness + 1)) - Randomness / 3;
    sp = speed * (100L + speedFactor) / 100L;
  } else {
    sp = speed;
  }

  dx = x - (int)(xCenter * WIDTH);
  dy = y - (int)(yCenter * HEIGHT);

  if (slowX || slowY) {
    long dSquared = (long)dx*dx + (long)dy*dy + 1;

    if (slowY) {
      dx = (int)(dx * 2500L / dSquared);
    }
    if (slowX) {
      dy = (int)(dy * 2500L / dSquared);
    }
  }

  if (reverse) {
    sp = -sp;
  }

  map_x = (int)(x + (dy * sp) / 700);
  map_y = (int)(y - (dx * sp) / 700);

  m.map_x = map_x;
  m.map_y = map_y;

  return m;
}


static void
init_params(void)
{
  Randomness = b_rand_uint32_range(0, 100);

  if (random_mode == 1) {
    speed = b_rand_uint32_range(30, 300);
    slowX = b_rand_boolean();
    slowY = b_rand_boolean();
    reverse = b_rand_boolean();
    xCenter = b_rand_double_range(1./8., 7./8.);
    yCenter = b_rand_double_range(1./8., 7./8.);
  }
}


void
on_switch_on(Context_t *ctx)
{
  random_mode = 1;
  Translation_batch_init(t_hurricane);
}


int8_t
create(Context_t *ctx)
{
  t_hurricane = Translation_new(&hurricane, &init_params);

  return 1;
}


void
destroy(Context_t *ctx)
{
  Translation_delete(t_hurricane);
}


void
run(Context_t *ctx)
{
  Translation_run(t_hurricane, ctx);
}
