/* Goom Project
 * Copyright (C) <2003> iOS-Software
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include <stdlib.h>

#include "v3d.h"
#include "surf3d.h"
#include "goom_tools.h"
#include "goom_config.h"
#include "goom_plugin_info.h"
#include "tentacle3d.h"

#define D 256.0f

#define nbgrid 6
#define definitionx 15
#define definitionz 45

typedef struct _TENTACLE_FX_DATA
{
  PluginParam enabled_bp;
  PluginParameters params;

  float cycle;
  grid3d *grille[nbgrid];
  float *vals;

#define NB_TENTACLE_COLORS 4
  int colors[NB_TENTACLE_COLORS];

  int col;
  int dstcol;
  float lig;
  float ligs;

  /* statics from pretty_move */
  float distt;
  float distt2;
  float rot;                    /* entre 0 et 2 * G_PI */
  int happens;
  int rotation;
  int lock;
} TentacleFXData;

static void tentacle_new (TentacleFXData * data);
static void tentacle_update (PluginInfo * goomInfo, Pixel * buf, Pixel * back,
    int W, int H, short[2][512], float, int drawit, TentacleFXData * data);
static void tentacle_free (TentacleFXData * data);

/* 
 * VisualFX wrapper for the tentacles
 */

static void
tentacle_fx_init (VisualFX * _this, PluginInfo * info)
{

  TentacleFXData *data = (TentacleFXData *) malloc (sizeof (TentacleFXData));

  secure_b_param (&data->enabled_bp, "Enabled", 1);
  plugin_parameters (&data->params, "3D Tentacles", 1);
  data->params.params[0] = &data->enabled_bp;

  data->cycle = 0.0f;
  data->col =
      (0x28 << (ROUGE * 8)) | (0x2c << (VERT * 8)) | (0x5f << (BLEU * 8));
  data->dstcol = 0;
  data->lig = 1.15f;
  data->ligs = 0.1f;

  data->distt = 10.0f;
  data->distt2 = 0.0f;
  data->rot = 0.0f;             /* entre 0 et 2 * G_PI */
  data->happens = 0;

  data->rotation = 0;
  data->lock = 0;
  data->colors[0] =
      (0x18 << (ROUGE * 8)) | (0x4c << (VERT * 8)) | (0x2f << (BLEU * 8));
  data->colors[1] =
      (0x48 << (ROUGE * 8)) | (0x2c << (VERT * 8)) | (0x6f << (BLEU * 8));
  data->colors[2] =
      (0x58 << (ROUGE * 8)) | (0x3c << (VERT * 8)) | (0x0f << (BLEU * 8));
  data->colors[3] =
      (0x87 << (ROUGE * 8)) | (0x55 << (VERT * 8)) | (0x74 << (BLEU * 8));
  tentacle_new (data);

  _this->params = &data->params;
  _this->fx_data = (void *) data;
}

static void
tentacle_fx_apply (VisualFX * _this, Pixel * src, Pixel * dest,
    PluginInfo * goomInfo)
{
  TentacleFXData *data = (TentacleFXData *) _this->fx_data;

  if (BVAL (data->enabled_bp)) {
    tentacle_update (goomInfo, dest, src, goomInfo->screen.width,
        goomInfo->screen.height, goomInfo->sound.samples,
        (float) goomInfo->sound.accelvar,
        goomInfo->curGState->drawTentacle, data);
  }
}

static void
tentacle_fx_free (VisualFX * _this)
{
  tentacle_free ((TentacleFXData *) _this->fx_data);
  free (_this->fx_data);
}

void
tentacle_fx_create (VisualFX * fx)
{
  fx->init = tentacle_fx_init;
  fx->apply = tentacle_fx_apply;
  fx->free = tentacle_fx_free;
  fx->fx_data = NULL;
  fx->params = NULL;
}

/* ----- */

static void
tentacle_free (TentacleFXData * data)
{
  int tmp;

  /* FREE GRID */
  for (tmp = 0; tmp < nbgrid; tmp++)
    grid3d_free (data->grille[tmp]);
  free (data->vals);

  goom_plugin_parameters_free (&data->params);
}

static void
tentacle_new (TentacleFXData * data)
{
  int tmp;

  v3d center = { 0, -17.0, 0 };
  data->vals = (float *) malloc ((definitionx + 20) * sizeof (float));

  for (tmp = 0; tmp < nbgrid; tmp++) {
    int x, z;

    z = 45 + rand () % 30;
    x = 85 + rand () % 5;
    center.z = z;
    data->grille[tmp] =
        grid3d_new (x, definitionx, z, definitionz + rand () % 10, center);
    center.y += 8;
  }
}

static inline unsigned char
lighten (unsigned char value, float power)
{
  int val = value;
  float t = (float) val * log10 (power) / 2.0;

  if (t > 0) {
    val = (int) t;              /* (32.0f * log (t)); */
    if (val > 255)
      val = 255;
    if (val < 0)
      val = 0;
    return val;
  } else {
    return 0;
  }
}

static void
lightencolor (int *col, float power)
{
  unsigned char *color;

  color = (unsigned char *) col;
  *color = lighten (*color, power);
  color++;
  *color = lighten (*color, power);
  color++;
  *color = lighten (*color, power);
  color++;
  *color = lighten (*color, power);
}

/* retourne x>>s , en testant le signe de x */
#define ShiftRight(_x,_s) ((_x<0) ? -(-_x>>_s) : (_x>>_s))

static int
evolutecolor (unsigned int src, unsigned int dest,
    unsigned int mask, unsigned int incr)
{

  int color = src & (~mask);

  src &= mask;
  dest &= mask;

  if ((src != mask)
      && (src < dest))
    src += incr;

  if (src > dest)
    src -= incr;
  return (src & mask) | color;
}

static void
pretty_move (PluginInfo * goomInfo, float cycle, float *dist, float *dist2,
    float *rotangle, TentacleFXData * fx_data)
{

  float tmp;

  /* many magic numbers here... I don't really like that. */
  if (fx_data->happens)
    fx_data->happens -= 1;
  else if (fx_data->lock == 0) {
    fx_data->happens =
        goom_irand (goomInfo->gRandom,
        200) ? 0 : 100 + goom_irand (goomInfo->gRandom, 60);
    fx_data->lock = fx_data->happens * 3 / 2;
  } else
    fx_data->lock--;

  tmp = fx_data->happens ? 8.0f : 0;
  *dist2 = fx_data->distt2 = (tmp + 15.0f * fx_data->distt2) / 16.0f;

  tmp = 30 + D - 90.0f * (1.0f + sin (cycle * 19 / 20));
  if (fx_data->happens)
    tmp *= 0.6f;

  *dist = fx_data->distt = (tmp + 3.0f * fx_data->distt) / 4.0f;

  if (!fx_data->happens) {
    tmp = G_PI * sin (cycle) / 32 + 3 * G_PI / 2;
  } else {
    fx_data->rotation =
        goom_irand (goomInfo->gRandom,
        500) ? fx_data->rotation : goom_irand (goomInfo->gRandom, 2);
    if (fx_data->rotation)
      cycle *= 2.0f * G_PI;
    else
      cycle *= -1.0f * G_PI;
    tmp = cycle - (G_PI * 2.0) * floor (cycle / (G_PI * 2.0));
  }

  if (fabs (tmp - fx_data->rot) > fabs (tmp - (fx_data->rot + 2.0 * G_PI))) {
    fx_data->rot = (tmp + 15.0f * (fx_data->rot + 2 * G_PI)) / 16.0f;
    if (fx_data->rot > 2.0 * G_PI)
      fx_data->rot -= 2.0 * G_PI;
    *rotangle = fx_data->rot;
  } else if (fabs (tmp - fx_data->rot) >
      fabs (tmp - (fx_data->rot - 2.0 * G_PI))) {
    fx_data->rot = (tmp + 15.0f * (fx_data->rot - 2.0 * G_PI)) / 16.0f;
    if (fx_data->rot < 0.0f)
      fx_data->rot += 2.0 * G_PI;
    *rotangle = fx_data->rot;
  } else
    *rotangle = fx_data->rot = (tmp + 15.0f * fx_data->rot) / 16.0f;
}

static void
tentacle_update (PluginInfo * goomInfo, Pixel * buf, Pixel * back, int W, int H,
    short data[2][512], float rapport, int drawit, TentacleFXData * fx_data)
{

  int tmp;
  int tmp2;

  int color;
  int colorlow;

  float dist, dist2, rotangle;

  if ((!drawit) && (fx_data->ligs > 0.0f))
    fx_data->ligs = -fx_data->ligs;

  fx_data->lig += fx_data->ligs;

  if (fx_data->lig > 1.01f) {
    if ((fx_data->lig > 10.0f) | (fx_data->lig < 1.1f))
      fx_data->ligs = -fx_data->ligs;

    if ((fx_data->lig < 6.3f) && (goom_irand (goomInfo->gRandom, 30) == 0))
      fx_data->dstcol = goom_irand (goomInfo->gRandom, NB_TENTACLE_COLORS);

    fx_data->col =
        evolutecolor (fx_data->col, fx_data->colors[fx_data->dstcol], 0xff,
        0x01);
    fx_data->col =
        evolutecolor (fx_data->col, fx_data->colors[fx_data->dstcol], 0xff00,
        0x0100);
    fx_data->col =
        evolutecolor (fx_data->col, fx_data->colors[fx_data->dstcol], 0xff0000,
        0x010000);
    fx_data->col =
        evolutecolor (fx_data->col, fx_data->colors[fx_data->dstcol],
        0xff000000, 0x01000000);

    color = fx_data->col;
    colorlow = fx_data->col;

    lightencolor (&color, fx_data->lig * 2.0f + 2.0f);
    lightencolor (&colorlow, (fx_data->lig / 3.0f) + 0.67f);

    rapport = 1.0f + 2.0f * (rapport - 1.0f);
    rapport *= 1.2f;
    if (rapport > 1.12f)
      rapport = 1.12f;

    pretty_move (goomInfo, fx_data->cycle, &dist, &dist2, &rotangle, fx_data);

    for (tmp = 0; tmp < nbgrid; tmp++) {
      for (tmp2 = 0; tmp2 < definitionx; tmp2++) {
        float val =
            (float) (ShiftRight (data[0][goom_irand (goomInfo->gRandom, 511)],
                10)) * rapport;

        fx_data->vals[tmp2] = val;
      }

      grid3d_update (fx_data->grille[tmp], rotangle, fx_data->vals, dist2);
    }
    fx_data->cycle += 0.01f;
    for (tmp = 0; tmp < nbgrid; tmp++)
      grid3d_draw (goomInfo, fx_data->grille[tmp], color, colorlow, dist, buf,
          back, W, H);
  } else {
    fx_data->lig = 1.05f;
    if (fx_data->ligs < 0.0f)
      fx_data->ligs = -fx_data->ligs;
    pretty_move (goomInfo, fx_data->cycle, &dist, &dist2, &rotangle, fx_data);
    fx_data->cycle += 0.1f;
    if (fx_data->cycle > 1000)
      fx_data->cycle = 0;
  }
}
