/*
 *  Copyright 1994-2022 Olivier Girondel
 *  Copyright 2019-2022 Laurent Marsac
 *
 *  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/>.
 */

/*
 * Plot selected path
 */

#include "context.h"
#include "paths.h"
#include "parameters.h"
#include "path.h"
#include "pthread_utils.h"


uint32_t options = BO_GFX | BO_SFX | BO_SCHEMES;
uint32_t version = 0;
enum LayerMode mode = LM_OVERLAY;

char dname[] = "Path";
char desc[]  = "Path";


static uint32_t plot_length   = 50; /* length of path to plot on each run */
static double   radius_factor = 1;  /* changes line thickness */
static pthread_mutex_t mutex;


void
init_path(uint16_t id)
{
  if (!xpthread_mutex_lock(&mutex)) {
    xfree(path);

    path_length = paths->paths[id]->size;
    path        = xcalloc(path_length, sizeof(Path_point_t));

    Path_scale_and_center(path, paths->paths[id]->data, path_length, scale);
    xpthread_mutex_unlock(&mutex);
  }
}


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

  plugin_parameters_add_int(params, BPP_LENGTH, (int)plot_length, 1, 1000, 1, "Length");
  plugin_parameters_add_double(params, BPP_RADIUS_FACTOR, radius_factor, 0, 100, 0.1, "Radius factor");

  return params;
}


void
set_parameters(const Context_t *ctx, const json_t *in_parameters)
{
  uint8_t reinit_path = 0;

  reinit_path |= set_parameters_path(ctx, in_parameters);
  plugin_parameter_parse_int_range(in_parameters, BPP_LENGTH, (int *)&plot_length);
  plugin_parameter_parse_double_range(in_parameters, BPP_RADIUS_FACTOR, &radius_factor);

  if (reinit_path) {
    init_path(path_id);
  }
}


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);
}


uint8_t
create(Context_t *ctx)
{
  if (NULL == paths) {
    return 0;
  } else {
    xpthread_mutex_init(&mutex, NULL);
    init_path(path_id);

    return 1;
  }
}


void
destroy(Context_t *ctx)
{
  xfree(path);
  xpthread_mutex_destroy(&mutex);
}


void
run(Context_t *ctx)
{
  Buffer8_t *dst = passive_buffer(ctx);
  Point2d_t  last;

  Buffer8_clear(dst);

  /* reinit path if selection changed */
  if (path_idx == 0) {
    if (path_id_changed) {
      init_path(path_id);
      path_id_changed = 0;
    }

    last.x = path[path_length - 1].x;
    last.y = path[path_length - 1].y;
  } else {
    last.x = path[path_idx - 1].x;
    last.y = path[path_idx - 1].y;
  }

  if (!xpthread_mutex_lock(&ctx->input->mutex)) {
    /* if end of path is crossed durring this round, reduce length
       so that the for loop ends exactly at path_length-1 */
    uint32_t length = MIN(plot_length, path_length - path_idx);

    /* window overlap and size for color computation (approx) */
    uint32_t wo = ctx->input->size >> 1; /* overlap */
    uint32_t ws = floor((double)(ctx->input->size - wo) / (double)length) + wo;

    for (uint32_t l = 0; l < length; l++, path_idx++) {
      uint32_t end = (l == length - 1) ? ctx->input->size : l * (ws - wo) + ws;
      double win_avg = compute_avg_abs(ctx->input->data[A_MONO], l * (ws - wo), end);
      Pixel_t c = MIN(1.0, color_scale * win_avg) * PIXEL_MAXVAL;

      uint16_t radius     = path[path_idx].radius * radius_factor;
      uint16_t radius_sqr = radius * radius;

      for (int16_t y = -radius; y <= radius; y++) {
        for (int16_t x = -radius; x <= radius; x++) {
          if (x * x + y * y <= radius_sqr) {
            if (path[path_idx].connect) {
              draw_line(dst, last.x, last.y, path[path_idx].x + x, path[path_idx].y + y, c);
            } else {
              set_pixel(dst, path[path_idx].x + x, path[path_idx].y + y, c);
            }
          }
        }
      }

      last.x = path[path_idx].x;
      last.y = path[path_idx].y;
    }

    xpthread_mutex_unlock(&ctx->input->mutex);
  }

  if (path_idx == path_length) {
    path_idx = 0;
  }
}


void
on_switch_on(Context_t *ctx)
{
  // generic path parameters
  path_idx    = 0;
  color_scale = 1;
  scale = 1;
  // path parameters
  path_id = Shuffler_get(paths->shuffler);
  plot_length   = 50;
  radius_factor = 1;
  // re-init
  init_path(path_id);
}
