/*
 *  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 "constants.h"
#include "brandom.h"
#include "cmapfader.h"
#include "colormaps.h"
#include "globals.h"


CmapFader_t *
CmapFader_new(const uint16_t size)
{
  CmapFader_t *cf = xcalloc(1, sizeof(CmapFader_t));

  cf->on = 0;
  cf->cur = Cmap8_new();
  cf->dst = colormaps->cmaps[0];
  cf->fader = Fader_new(256*4);
  cf->shf = Shuffler_new(size);
  Shuffler_set_mode(cf->shf, Context_get_shuffler_mode(BD_COLORMAPS));
  cf->refresh = 1;

  CmapFader_set(cf);

  return cf;
}


void
CmapFader_delete(CmapFader_t *cf)
{
  Cmap8_delete(cf->cur);
  Fader_delete(cf->fader);
  Shuffler_delete(cf->shf);
  xfree(cf);
}


void
CmapFader_init(CmapFader_t *cf)
{
  Fader_t *fader = cf->fader;
  uint16_t i;

  Fader_init(fader);

  for (i = 0; i < 256; i++) {
    /* TODO pointer chain optim */
    const RGBA_t col_src = cf->cur->colors[i].col;
    const RGBA_t col_dst = cf->dst->colors[i].col;

    /* delta values */
    /*     cf->fader->delta[i*3+0] = */
    /*       ((float)col_dst.r-(float)col_src.r) */
    /*       / (float)(cf->fader->max); */
    /*     cf->fader->delta[i*3+1] = */
    /*       ((float)col_dst.g-(float)col_src.g) */
    /*       / (float)(cf->fader->max); */
    /*     cf->fader->delta[i*3+2] = */
    /*       ((float)col_dst.b-(float)col_src.b) */
    /*       / (float)(cf->fader->max); */
    fader->delta[i*3+0] = (long)
                          ((float)col_dst.r-(float)col_src.r)
                          / (float)(fader->max)*MFACTOR;
    fader->delta[i*3+1] = (long)
                          ((float)col_dst.g-(float)col_src.g)
                          / (float)(fader->max)*MFACTOR;
    fader->delta[i*3+2] = (long)
                          ((float)col_dst.b-(float)col_src.b)
                          / (float)(fader->max)*MFACTOR;
    fader->delta[i*3+3] = (long)
                          ((float)col_dst.a-(float)col_src.a)
                          / (float)(fader->max)*MFACTOR;

    /* initial values */
    /*     cf->fader->tmp[i*3+0] = (float)col_src.r; */
    /*     cf->fader->tmp[i*3+1] = (float)col_src.g; */
    /*     cf->fader->tmp[i*3+2] = (float)col_src.b; */
    fader->tmp[i*3+0] = (uint32_t)col_src.r*MFACTOR;
    fader->tmp[i*3+1] = (uint32_t)col_src.g*MFACTOR;
    fader->tmp[i*3+2] = (uint32_t)col_src.b*MFACTOR;
    fader->tmp[i*3+3] = (uint32_t)col_src.a*MFACTOR;
  }

  Fader_start(fader);
}


void
CmapFader_run(CmapFader_t *cf)
{
  Fader_t *fader = cf->fader;
  Cmap8_t *cur = cf->cur;
  const uint32_t elapsed = Fader_elapsed(fader);

#ifdef DEBUG_FADERS
  printf("Cf ");
#endif
  Fader_start(fader);
  fader->faded += elapsed;

  if (fader->faded >= fader->max) {
    /* we're done */
    fader->fading = 0;
    /* copy, just in case */
    Cmap8_copy(cf->dst, cur);
  } else {
    uint16_t i;

    for (i = 256; i--; ) {
      /* FADE DA HOUSE */
      /* TODO optimize pointer version array toussa --oliv3 */
      cur->colors[i].col.r =
        (uint8_t)((fader->tmp[i*3+0] +=
                     elapsed*fader->delta[i*3+0])/MFACTOR);
      cur->colors[i].col.g =
        (uint8_t)((fader->tmp[i*3+1] +=
                     elapsed*fader->delta[i*3+1])/MFACTOR);
      cur->colors[i].col.b =
        (uint8_t)((fader->tmp[i*3+2] +=
                     elapsed*fader->delta[i*3+2])/MFACTOR);

      /* FIXME alpha fading seems broken */
      cur->colors[i].col.a = 255;
      /* cur->colors[i].col.a = */
      /*   (uint8_t)((fader->tmp[i*3+3] += */
      /*       elapsed*fader->delta[i*3+3])/MFACTOR); */
    }
  }
  Cmap8_findMinMax(cur);
  cf->refresh = 1;
}


void
CmapFader_set(CmapFader_t *cf)
{
  cf->dst = colormaps->cmaps[cf->fader->target];

  if (NULL != cf->dst->name) {
#ifdef DEBUG_CMAPFADER
    printf("[i] Using colormap '%s'\n", cf->dst->name);
#endif
  } else {
    xerror("Colormap without name, WTF #@!\n");
  }

  CmapFader_init(cf);
  cf->fader->fading = 1;
  Cmap8_findMinMax(cf->cur);
  cf->refresh = 1;
}


void
CmapFader_prev(CmapFader_t *cf)
{
  DEC(cf->fader->target, colormaps->size);
  CmapFader_set(cf);
}


void
CmapFader_prev_n(CmapFader_t *cf, const uint16_t n)
{
  for (uint16_t i = 0; i < n; i++) {
    DEC(cf->fader->target, colormaps->size);
  }
  CmapFader_set(cf);
}


void
CmapFader_next(CmapFader_t *cf)
{
  INC(cf->fader->target, colormaps->size);
  CmapFader_set(cf);
}


void
CmapFader_next_n(CmapFader_t *cf, const uint16_t n)
{
  for (uint16_t i = 0; i < n; i++) {
    INC(cf->fader->target, colormaps->size);
  }
  CmapFader_set(cf);
}


void
CmapFader_random(CmapFader_t *cf)
{
  cf->fader->target = Shuffler_get(cf->shf);
  CmapFader_set(cf);
}


void
CmapFader_use(CmapFader_t *cf, const uint16_t index)
{
  if (index < colormaps->size) {
    cf->fader->target = index;
    CmapFader_set(cf);
  }
}


int
CmapFader_ring(const CmapFader_t *cf)
{
  const Fader_t *fader = cf->fader;

  return (fader->fading && ((Timer_elapsed(fader->timer) * MFACTOR) > 0));
}
