/*
 *  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 "oscillo.h"
#include "brandom.h"
#include "pthread_utils.h"


void
Transform_init(Transform_t *t)
{
  t->v_before.x = t->v_before.y = 0;
  t->v_i.x = t->v_i.y = 0;
  t->v_after.x = t->v_after.y = 0;
}


Porteuse_t *
Porteuse_new(uint32_t size, uint8_t channel)
{
  uint32_t i;
  Porteuse_t *p = xcalloc(1, sizeof(Porteuse_t));

  p->size = size;
  p->color = xcalloc(p->size, sizeof(Pixel_t));
  p->trans = xcalloc(size, sizeof(Transform_t));
  p->connect = xcalloc(size, sizeof(uint8_t));
  p->channel = channel;

  for (i = 0; i < size; i++) {
    Transform_init(&p->trans[i]);
    p->color[i] = PIXEL_MAXVAL;
  }

  return p;
}


void
Porteuse_delete(Porteuse_t *p)
{
  if (p) {
    xfree(p->color);
    xfree(p->trans);
    xfree(p->connect);
  }
  xfree(p);
}


void
Porteuse_init_alpha(Porteuse_t *p)
{
  uint32_t i;
  Point2d_t p0;

  p0 = p->origin;
  for (i = 0; i < p->size; i++) {
    float dx, dy, dx2, dy2, d;
    Transform_t *t;
    Point2d_t p1;

    t = &p->trans[i];
    p1 = p2d_add(&p0, &t->v_i);
    dx = p1.x - p0.x;
    dy = p1.y - p0.y;
    dx2 = dx * dx;
    dy2 = dy * dy;
    d = sqrtf(dx2 + dy2);
    t->cos_alpha = dx / d;
    t->sin_alpha = dy / d;
    p0 = p1;
  }
}


void
Porteuse_draw(const Porteuse_t *p, Context_t *ctx, const int connect)
{
  uint32_t i = 0;
  Point2d_t origin = p->origin, last;
  Transform_t *t = &p->trans[i];
  Point2d_t sigval;
  float vy, sina, cosa, x, y, xi, yi;

  Buffer8_t *dst = passive_buffer(ctx);

  if (NULL == ctx->input) {
    printf("[!] Porteuse_draw called without input\n");
    return;
  }

  assert(p->size <= ctx->input->size);

  if (!xpthread_mutex_lock(&ctx->input->mutex)) {
    origin = p2d_add(&origin, &t->v_before);
    vy = ctx->input->data[p->channel][i];

    sigval.x = 0;
    sigval.y = vy * t->v_j_factor;

    sina = t->sin_alpha;
    cosa = t->cos_alpha;

    x = sigval.x;
    y = sigval.y;

    xi = x * cosa - y * sina;
    yi = x * sina + y * cosa;

    xi += origin.x;
    yi += origin.y;

    last.x = xi;
    last.y = yi;

    origin = p2d_add(&origin, &t->v_i);
    origin = p2d_add(&origin, &t->v_after);

    for (i = 1; i < p->size; i++) {
      Pixel_t color = p->color[i];

      t = &p->trans[i];

      origin = p2d_add(&origin, &t->v_before);
      vy = ctx->input->data[p->channel][i];

      sigval.x = 0;
      sigval.y = vy * t->v_j_factor;

      sina = t->sin_alpha;
      cosa = t->cos_alpha;

      x = sigval.x;
      y = sigval.y;

      xi = x * cosa - y * sina;
      yi = x * sina + y * cosa;

      xi += origin.x;
      yi += origin.y;

      if ((connect == 1) || (connect == 2 && p->connect[i])) {
        draw_line(dst, last.x, last.y, (short)xi, (short)yi, color);
      } else {
        set_pixel(dst, (short)xi,   (short)yi-1, color/2);
        set_pixel(dst, (short)xi,   (short)yi+1, color/2);
        set_pixel(dst, (short)xi-1, (short)yi,   color/2);
        set_pixel(dst, (short)xi+1, (short)yi,   color/2);
        set_pixel(dst, (short)xi,   (short)yi,   color);
      }

      last.x = xi;
      last.y = yi;

      origin = p2d_add(&origin, &t->v_i);
      origin = p2d_add(&origin, &t->v_after);
    }
    xpthread_mutex_unlock(&ctx->input->mutex);
  }
}
