/*
 *  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"

#define BATCH_LINES 20   /* lines per turn */
#define FADE_STEPS  50.0 /* fade stuff */


static void
Translation_build(Translation_t *t)
{
  uint16_t i, j;
  M_wPoint_t *wp = t->point;

  for (j = 0; j < HEIGHT; j++)
    for (i = 0; i < WIDTH; i++) {
      wp->x = i;
      wp->y = j;
      wp->dx = 0;
      wp->dy = 0;
      wp++;
    }
}


Translation_t *
Translation_new(Map_t (*f)(const short, const short), void (*init)(void))
{
  Translation_t *t = NULL;

  assert(NULL != init);

  t = xcalloc(1, sizeof(Translation_t));
  t->f = f;
  t->point = xcalloc(BUFFSIZE, sizeof(M_wPoint_t));
  t->line = t->fading = 0;
  t->init = init;

  Translation_build(t);
  (*init)();

  return t;
}


static void
Translation_init_fade(Translation_t *t)
{
  t->fading = FADE_STEPS;
}


static void
Translation_idle(const Translation_t *t, const Context_t *ctx)
{
  const Buffer8_t *src = active_buffer(ctx);
  Buffer8_t *dst = passive_buffer(ctx);
  M_wPoint_t *p = t->point;
  uint16_t i, j;

  for (j = 0; j < HEIGHT; j++)
    for (i = 0; i < WIDTH; i++) {
      set_pixel_nc(dst, i, j,
                   get_pixel_nc(src, (uint16_t)p[j * WIDTH + i].x,
                                (uint16_t)p[j * WIDTH + i].y));
    }
}


static void
Translation_fade(Translation_t *t, const Context_t *ctx)
{
  const Buffer8_t *src = active_buffer(ctx);
  Buffer8_t *dst = passive_buffer(ctx);
  M_wPoint_t *p = t->point;
  uint16_t i, j;

  for (j = 0; j < HEIGHT; j++)
    for (i = 0; i < WIDTH; i++) {
      p[j * WIDTH + i].x += p[j * WIDTH + i].dx;
      p[j * WIDTH + i].y += p[j * WIDTH + i].dy;

      set_pixel_nc(dst, i, j, get_pixel_nc(src,
                                           (uint16_t)(p[j * WIDTH + i].x + WIDTH ) % WIDTH,
                                           (uint16_t)(p[j * WIDTH + i].y + HEIGHT) % HEIGHT));
    }

  /* save coordinages cropped to screen size */
  if (--t->fading == 0)
    for (j = 0; j < HEIGHT; j++)
      for (i = 0; i < WIDTH; i++) {
        p[j * WIDTH + i].x = (uint16_t)(p[j * WIDTH + i].x + WIDTH) % WIDTH;
        p[j * WIDTH + i].y = (uint16_t)(p[j * WIDTH + i].y + HEIGHT) % HEIGHT;
      }
}


void
Translation_compute(Translation_t *t)
{
  uint16_t k;

  assert(NULL != t);
  for (k = 0; (k < BATCH_LINES) && !Translation_batch_done(t); k++)
    if (Translation_batch_line(t)) {
      return;
    }
}


int
Translation_run(Translation_t *t, const Context_t *ctx)
{
  if (!Translation_batch_done(t)) {
    Translation_compute(t);
  }

  if (t->fading) {
    Translation_fade(t, ctx);
  } else {
    Translation_idle(t, ctx);
  }

  return t->fading;
}


void
Translation_delete(Translation_t *t)
{
  if (NULL != t) {
    xfree(t->point);
    xfree(t);
  }
}


void
Translation_batch_init(Translation_t *t)
{
  t->line = t->fading = 0;
  (*t->init)();
  Translation_build(t);
}


uint8_t
Translation_batch_line(Translation_t *t)
{
  M_wPoint_t *wp = &t->point[t->line * WIDTH];

  for (uint16_t i = 0; i < WIDTH; i++) {
    Map_t m = t->f(wp->x, wp->y);
    wp->dx  = (float)((float)m.map_x - (float)wp->x) / FADE_STEPS;
    wp->dy  = (float)((float)m.map_y - (float)wp->y) / FADE_STEPS;
    wp++;
  }

  if (++t->line == HEIGHT) {
    Translation_init_fade(t);
    return 1;
  } else {
    return 0;
  }
}


uint8_t
Translation_batch_done(const Translation_t *t)
{
  return (t->line == HEIGHT);
}
