/***************************************************************************
 *   Copyright (C) 2000-2004 by                                            *
 *   Jean-Christophe Hoelt <jeko@ios-software.com>                         *
 *   Guillaume Borios <gyom@ios-software.com>                              *
 *                                                                          *
 *   This program 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.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "goom_fx.h"
#include "goom_plugin_info.h"
#include "goom_tools.h"

#include "mathtools.h"

/* TODO:-- FAIRE PROPREMENT... BOAH... */
#define NCOL 15

/*static const int colval[] = {
0xfdf6f5,
0xfae4e4,
0xf7d1d1,
0xf3b6b5,
0xefa2a2,
0xec9190,
0xea8282,
0xe87575,
0xe46060,
0xe14b4c,
0xde3b3b,
0xdc2d2f,
0xd92726,
0xd81619,
0xd50c09,
0
};
*/
static const int colval[] = {
    0x1416181a,
    0x1419181a,
    0x141f181a,
    0x1426181a,
    0x142a181a,
    0x142f181a,
    0x1436181a,
    0x142f1819,
    0x14261615,
    0x13201411,
    0x111a100a,
    0x0c180508,
    0x08100304,
    0x00050101,
    0x0
};


/* The different modes of the visual FX.
 * Put this values on fx_mode */
#define FIREWORKS_FX 0
#define RAIN_FX 1
#define FOUNTAIN_FX 2
#define LAST_FX 3

typedef struct _FS_STAR {
    float x,y;
    float vx,vy;
    float ax,ay;
    float age,vage;
} Star;

typedef struct _FS_DATA{

    int fx_mode;
    int nbStars;

    int maxStars;
    Star *stars;

    float min_age;
    float max_age;

    PluginParam min_age_p;
    PluginParam max_age_p;
    PluginParam nbStars_p;
    PluginParam nbStars_limit_p;
    PluginParam fx_mode_p;

    PluginParameters params;
} FSData;

static void fs_init(VisualFX *_this, PluginInfo *UNUSED(info)) {

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

    data->fx_mode = FIREWORKS_FX;
    data->maxStars = 4096;
    data->stars = (Star*)malloc(data->maxStars * sizeof(Star));
    data->nbStars = 0;

    data->max_age_p = secure_i_param ("Fireworks Smallest Bombs");
    IVAL(data->max_age_p) = 80;
    IMIN(data->max_age_p) = 0;
    IMAX(data->max_age_p) = 100;
    ISTEP(data->max_age_p) = 1;

    data->min_age_p = secure_i_param ("Fireworks Largest Bombs");
    IVAL(data->min_age_p) = 99;
    IMIN(data->min_age_p) = 0;
    IMAX(data->min_age_p) = 100;
    ISTEP(data->min_age_p) = 1;

    data->nbStars_limit_p = secure_i_param ("Max Number of Particules");
    IVAL(data->nbStars_limit_p) = 512;
    IMIN(data->nbStars_limit_p) = 0;
    IMAX(data->nbStars_limit_p) = data->maxStars;
    ISTEP(data->nbStars_limit_p) = 64;

    data->fx_mode_p = secure_i_param ("FX Mode");
    IVAL(data->fx_mode_p) = data->fx_mode;
    IMIN(data->fx_mode_p) = 1;
    IMAX(data->fx_mode_p) = 3;
    ISTEP(data->fx_mode_p) = 1;

    data->nbStars_p = secure_f_feedback ("Number of Particules (% of Max)");

    data->params = plugin_parameters ("Particule System", 7);
    data->params.params[0] = &data->fx_mode_p;
    data->params.params[1] = &data->nbStars_limit_p;
    data->params.params[2] = 0;
    data->params.params[3] = &data->min_age_p;
    data->params.params[4] = &data->max_age_p;
    data->params.params[5] = 0;
    data->params.params[6] = &data->nbStars_p;

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

static void fs_free(VisualFX *_this) {
    FSData *data = (FSData*)_this->fx_data;
    free (data->stars);
    free (data->params.params);
    free (data);
}


/**
 * Cree une nouvelle 'bombe', c'est a dire une particule appartenant a une fusee d'artifice.
 */
static void addABomb (FSData *fs, int mx, int my, float radius, float vage, float gravity, PluginInfo *info) {

    int i = fs->nbStars;
    float ro;
    int theta;

    if (fs->nbStars >= fs->maxStars)
        return;
    fs->nbStars++;

    fs->stars[i].x = mx;
    fs->stars[i].y = my;

    ro = radius * (float)goom_irand(info->gRandom,100) / 100.0f;
    ro *= (float)goom_irand(info->gRandom,100)/100.0f + 1.0f;
    theta = goom_irand(info->gRandom,256);

    fs->stars[i].vx = ro * cos256[theta];
    fs->stars[i].vy = -0.2f + ro * sin256[theta];

    fs->stars[i].ax = 0;
    fs->stars[i].ay = gravity;

    fs->stars[i].age = 0;
    if (vage < fs->min_age)
        vage=fs->min_age;
    fs->stars[i].vage = vage;
}


/**
 * Met a jour la position et vitesse d'une particule.
 */
static void updateStar (Star *s) {
    s->x+=s->vx;
    s->y+=s->vy;
    s->vx+=s->ax;
    s->vy+=s->ay;
    s->age+=s->vage;
}


/**
 * Ajoute de nouvelles particules au moment d'un evenement sonore.
 */
static void fs_sound_event_occured(VisualFX *_this, PluginInfo *info) {

    FSData *data = (FSData*)_this->fx_data;
    int i;

    int max = (int)((1.0f+info->sound.goomPower)*goom_irand(info->gRandom,150)) + 100;
    float radius = (1.0f+info->sound.goomPower) * (float)(goom_irand(info->gRandom,150)+50)/300;
    int mx;
    int my;
    float vage, gravity = 0.02f;

    switch (data->fx_mode) {
        case FIREWORKS_FX:
      {
      double dx,dy;
      do {
        mx = goom_irand(info->gRandom,info->screen.width);
            my = goom_irand(info->gRandom,info->screen.height);
        dx = (mx - info->screen.width/2);
        dy = (my - info->screen.height/2);
      } while (dx*dx + dy*dy < (info->screen.height/2)*(info->screen.height/2));
            vage = data->max_age * (1.0f - info->sound.goomPower);
      }
            break;
        case RAIN_FX:
      mx = goom_irand(info->gRandom,info->screen.width);
      if (mx > info->screen.width/2)
        mx = info->screen.width;
      else
        mx = 0;
            my = -(info->screen.height/3)-goom_irand(info->gRandom,info->screen.width/3);
            radius *= 1.5;
            vage = 0.002f;
            break;
        case FOUNTAIN_FX:
            my = info->screen.height+2;
            vage = 0.001f;
            radius += 1.0f;
            mx = info->screen.width / 2;
            gravity = 0.04f;
            break;
        default:
            return;
            /* my = i R A N D (info->screen.height); vage = 0.01f; */
    }

    radius *= info->screen.height / 200.0f; /* why 200 ? because the FX was developped on 320x200 */
    max *= info->screen.height / 200.0f;

    if (info->sound.timeSinceLastBigGoom < 1) {
        radius *= 1.5;
        max *= 2;
    }
    for (i=0;i<max;++i)
        addABomb (data,mx,my,radius,vage,gravity,info);
}


/**
 * Main methode of the FX.
 */
static void fs_apply(VisualFX *_this, Pixel *UNUSED(src), Pixel *dest, PluginInfo *info) {

    int i;
    int col;
    FSData *data = (FSData*)_this->fx_data;

    /* Get the new parameters values */
    data->min_age = 1.0f - (float)IVAL(data->min_age_p)/100.0f;
    data->max_age = 1.0f - (float)IVAL(data->max_age_p)/100.0f;
    FVAL(data->nbStars_p) = (float)data->nbStars / (float)data->maxStars;
    data->nbStars_p.change_listener(&data->nbStars_p);
    data->maxStars = IVAL(data->nbStars_limit_p);
    data->fx_mode = IVAL(data->fx_mode_p);

    /* look for events */
    if (info->sound.timeSinceLastGoom < 1) {
        fs_sound_event_occured(_this, info);
        if (goom_irand(info->gRandom,20)==1) {
            IVAL(data->fx_mode_p) = goom_irand(info->gRandom,(LAST_FX*3));
            data->fx_mode_p.change_listener(&data->fx_mode_p);
        }
    }

    /* update particules */
    for (i=0;i<data->nbStars;++i) {
        updateStar(&data->stars[i]);

        /* dead particule */
        if (data->stars[i].age>=NCOL)
            continue;

        /* choose the color of the particule */
        col = colval[(int)data->stars[i].age];

        /* draws the particule */
        info->methods.draw_line(dest,(int)data->stars[i].x,(int)data->stars[i].y,
                (int)(data->stars[i].x-data->stars[i].vx*6),
                (int)(data->stars[i].y-data->stars[i].vy*6),
                col,
                (int)info->screen.width, (int)info->screen.height);
        info->methods.draw_line(dest,(int)data->stars[i].x,(int)data->stars[i].y,
                (int)(data->stars[i].x-data->stars[i].vx*2),
                (int)(data->stars[i].y-data->stars[i].vy*2),
                col,
                (int)info->screen.width, (int)info->screen.height);
    }

    /* look for dead particules */
    for (i=0;i<data->nbStars;) {

        if ((data->stars[i].x > info->screen.width + 64)
                ||((data->stars[i].vy>=0)&&(data->stars[i].y - 16*data->stars[i].vy > info->screen.height))
                ||(data->stars[i].x < -64)
                ||(data->stars[i].age>=NCOL)) {
            data->stars[i] = data->stars[data->nbStars-1];
            data->nbStars--;
        }
        else ++i;
    }
}

VisualFX flying_star_create(void) {
    VisualFX vfx = {
        .init = fs_init,
        .free = fs_free,
        .apply = fs_apply,
        .fx_data = 0
    };
    return vfx;
}
