/*
  Plasma 8bit demos-scene effect

  Copyright (C) 2002 W.P. van Paassen - peter@paassen.tmfweb.nl
            (C) 2009 Denis Roio - jaromil@dyne.org

  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 3 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., 675 Mass Ave, Cambridge, MA 02139, USA.
    
*/


#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <string.h>
#include <inttypes.h>

#include <frei0r.hpp>


typedef struct {
  int16_t x; ///< x axis position coordinate
  int16_t y; ///< y axis position coordinate
  uint16_t w; ///< width of frame in pixels
  uint16_t h; ///< height of frame in pixels
  uint8_t bpp; ///< bits per pixel
  uint16_t pitch; ///< width of frame in bytes
  uint32_t size; ///< size of the whole frame in bytes
} ScreenGeometry;

typedef struct {
  uint8_t r;
  uint8_t g;
  uint8_t b;
} Palette;

class Plasma: public frei0r::source {

public:
  Plasma(int wdt, int hgt);
  ~Plasma();

  virtual void update(double time, uint32_t* out);

private:

  ScreenGeometry geo;

  void _init(int wdt, int hgt);


  uint16_t pos1, pos2, pos3, pos4;
  uint16_t tpos1, tpos2, tpos3, tpos4;

  int aSin[512];

  Palette colors[256];

  uint32_t palette2rgb(uint8_t idx);

  // vectors (exposed parameters from 0 to 1)
  double speed1; // 5
  double speed2; // 3
  double speed3; // 3
  double speed4; // 1
  double move1; // 9
  double move2; // 8

  // scalars
  double _speed1; // 5
  double _speed2; // 3
  double _speed3; // 3
  double _speed4; // 1
  double _move1; // 9
  double _move2; // 8

};

void Plasma::_init(int wdt, int hgt) {
  geo.w = wdt;
  geo.h = hgt;
  geo.bpp = 32;
  geo.size = geo.w*geo.h*(geo.bpp/8);
  geo.pitch = geo.w*(geo.bpp/8);
}

Plasma::Plasma(int wdt, int hgt) {

  register_param(speed1, "1_speed", " ");
  register_param(speed2, "2_speed", " ");
  register_param(speed3, "3_speed", " ");
  register_param(speed4, "4_speed", " ");

  register_param(move1, "1_move", " ");
  register_param(move2, "2_move", " ");
  
  
  int i;
  float rad;

  _init(wdt, hgt);

 
  /*create sin lookup table */
  for (i = 0; i < 512; i++)
    {
      /* 360 / 512 * degree to rad,
	 360 degrees spread over 512 values
	 to be able to use AND 512-1 instead
	 of using modulo 360*/
      rad =  ((float)i * 0.703125) * 0.0174532;

      /*using fixed point math with 1024 as base*/
      aSin[i] = sin(rad) * 1024;
      
    }
  
  /* create palette */
  for (i = 0; i < 64; ++i)
    {
      colors[i].r = i << 2;
      colors[i].g = 255 - ((i << 2) + 1); 
      colors[i+64].r = 255;
      colors[i+64].g = (i << 2) + 1;
      colors[i+128].r = 255 - ((i << 2) + 1);
      colors[i+128].g = 255 - ((i << 2) + 1);
      colors[i+192].g = (i << 2) + 1; 
    } 

  speed1 = 1.;
  speed2 = 1.;
  speed3 = 1.;
  speed4 = 1.;
  move1  = 1.;
  move2  = 1.;
  
  _speed1 = 5;
  _speed2 = 3;
  _speed3 = 3;
  _speed4 = 1;
  _move1  = 9;
  _move2  = 8;

}

Plasma::~Plasma() {

}

void Plasma::update(double time, uint32_t* out) {
  uint16_t i, j;
  uint8_t index;
  int x;

  uint32_t *image = (uint32_t*)out;

  // number parameters are not good in frei0r
  // we need types defining multipliers and min/max values
  // so for now we leave this generator like this
  // but TODO ASAP new parameter types: fader and minmax
  // then fix also parameters of the partik0l which is suffering
  // of the same problem - jaromil

  // update parameters
  _speed1 = _speed1 * speed1;
  _speed2 = _speed2 * speed2;
  _speed3 = _speed3 * speed3;
  _speed4 = _speed4 * speed4;
  
  _move1 = _move1 * move1;
  _move2 = _move2 * move2;

  tpos4 = pos4;
  tpos3 = pos3;
  
  for (i = 0; i < geo.h; ++i) {
    tpos1 = pos1 + (int)_speed1;
    tpos2 = pos2 + (int)_speed2;
    
    tpos3 &= 511;
    tpos4 &= 511;
    
    for (j = 0; j < geo.w; ++j) {
      tpos1 &= 511;
      tpos2 &= 511;
      
      x = aSin[tpos1] + aSin[tpos2] + aSin[tpos3] + aSin[tpos4];
      /*actual plasma calculation*/
      
      index = 128 + (x >> 4);
      /* fixed point multiplication but optimized so basically
	 it  says (x  * (64  * 1024)  / (1024  * 1024)),  x is
	 already multiplied by 1024*/
      
      *image++ = palette2rgb(index);
      
      tpos1 += (int)_speed1; 
      tpos2 += (int)_speed2; 
    }
    
    tpos4 += (int)_speed3;
    tpos3 += (int)_speed4;
  }
  
  /* move plasma */
  
  pos1 += (int)_move1;
  pos3 += (int)_move2;
}

uint32_t Plasma::palette2rgb(uint8_t idx) {
  uint32_t rgba;
  // just for little endian
  // TODO: big endian
  rgba = (colors[idx].r << 16) 
    | (colors[idx].g << 8)
    | (colors[idx].b )
    | (0xff << 24);
  
  return rgba;
}

frei0r::construct<Plasma> plugin("Plasma",
				   "Demo scene 8bit plasma",
				   "Jaromil",
				   0,3);
