/*
 *  Copyright 1994-2022 Olivier Girondel
 *  Copyright 2014-2022 Frantz Balinski
 *
 *  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/>.
 */

/*
 * the idea is to simulate a broken mirror
 */

#include <glib.h>

#include "brandom.h"
#include "translation.h"


uint32_t version = 0;
uint32_t options = BO_DISPLACE | BO_LENS | BO_NOT_LENS;
char desc[] = "Broken mirror effect";
char dname[] = "Broken mirror";

#define NBCENTRES 32

typedef struct {
  uint16_t x, y;
} zPoint2D;


static Translation_t *t_zbroken = NULL;
static zPoint2D centres[NBCENTRES];


static Map_t
zbroken(const short x, const short y)
{
  Map_t m;
  float dist, dmin, dx, dy, lx, ly;
  int cc, sx, sy;

  cc   = -1;  // no (closest) center found
  dmin = 1e9; // initialize to large value

  /* Found closest center */
  for (int i = 0; i < NBCENTRES; i++) {
    dx = (int)x - centres[i].x;
    dy = (int)y - centres[i].y;

    dist = sqrtf(dx * dx + dy * dy);

    if (dist < dmin) {
      dmin = dist;
      cc   = i;
    } else if (dist == dmin) {
      if (b_rand_boolean() != 0) {
        cc = i;
      }
    }
  }

  if (cc != -1) {
    /* Compute displacement to closest center found */
    dx = (int)x - centres[cc].x;
    dy = (int)y - centres[cc].y;

    if (dx < 0) {
      lx = -dx;
      sx = -1;
    } else {
      lx = dx;
      sx = 1;
    }

    if (dy < 0) {
      ly = -dy;
      sy = -1;
    } else {
      ly = dy;
      sy = 1;
    }

    dx = sqrtf(lx) * sx;
    dy = sqrtf(ly) * sy;

    m.map_x = (float)x + dx;
    m.map_y = (float)y + dy;
  } else {
    /* else don't move ! */
    m.map_x = x;
    m.map_y = y;
  }

  return m;
}


static void
init_params(void)
{
  int i;

  for (i = 0; i < NBCENTRES; i++) {
    centres[i].x = b_rand_uint32_range(0, MAXX + 1);
    centres[i].y = b_rand_uint32_range(0, MAXY + 1);
  }
}


int8_t
create(Context_t *ctx)
{
  t_zbroken = Translation_new(&zbroken, &init_params);

  return 1;
}


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


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


void
on_switch_on(void)
{
  Translation_batch_init(t_zbroken);
}
