#include "SDL.h"

#include "log.h"
#include "rng.h"
#include "opengl.h"
#include "plasmaf.h"

static double* pf_genFractalMap(const int w, const int h, double rug);
static void pf_divFractal(double* map, const double x, const double y,
                          const double w, const double h, const 
                          double rw, const double rh, double c1,
                          double c2, double c3, double c4, double rug);

/* Acutally generate the fractal and loads it up in an opengl texture. */
glTexture* pf_genFractal(const int w, const int h, double rug) {
  int i;
  double* map;
  SDL_Surface* sur;
  uint32_t* pix;
  glTexture* tex;
  double c;

  map = pf_genFractalMap(w, h, rug);

  sur = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RGBAMASK);
  pix = sur->pixels;

  /* Convert from mapping to actual colours. */
  SDL_LockSurface(sur);
  for(i = 0; i < h*w; i++) {
    c = map[i];
    pix[i] = RMASK + BMASK + GMASK + (AMASK & (uint32_t)(AMASK*c));
  }
  SDL_UnlockSurface(sur);

  free(map);

  tex = gl_loadImage(sur);

  return tex;
}

static double* pf_genFractalMap(const int w, const int h, double rug) {
  double* map; /* We'll use it to map out the fractal before saving. */
  double cx, cy;

  map = malloc(w*h * sizeof(double));
  if(map == NULL) {
    WARN("Out of memory");
    return NULL;
  }

  /* Set up initial values. */
  cx = (double)w/2;
  cy = (double)h/2;

  /* Start by doing the four squares. */
  pf_divFractal(map, 0,   0,  cx, cy, w, h, 0., 0., 1., 0., rug);
  pf_divFractal(map, cx,  0,  cx, cy, w, h, 0., 0., 0., 1., rug);
  pf_divFractal(map, cx,  cy, cx, cy, w, h, 1., 0., 0., 0., rug);
  pf_divFractal(map, 0,   cy, cx, cy, w, h, 0., 1., 0., 0., rug); 

  return map;
}

static void pf_divFractal(double* map, const double x, const double y,
                          const double w, const double h, const double rw,
                          const double rh, double c1, double c2, double c3,
                          double c4, double rug) {

  double nw, nh; /* New dimensions. */
  double m, e1, e2, e3, e4; /* Middle and edges. */

  /* Still need to subdivide. */
  if((w>1.) || (h>1.)) {
    /* Calculate new dimensions. */
    nw = w/2.;
    nh = h/2.;

    /* Edges. */
    m   = (c1 + c2 + c3 + c4)/4.;
    e1  = (c1 + c2)/2.;
    e2  = (c2 + c3)/2.;
    e3  = (c3 + c4)/2.;
    e4  = (c4 + c1)/2.;

    /* Now change the middle colour. */
    /*DEBUG("%f + %f", m, rug*(RNGF()-0.5) * ((nw+nh)/(rw+rh)*1000)); */
    m += rug*(RNGF()-0.5)*((nw+nh)/(rw+rh)*3.);
    if(m < 0.) m = 0.;
    else if(m>1.) m = 1.;

    /* Recursivation. */
    pf_divFractal(map, x,    y,     nw, nh, rw, rh, c1, e1, m,  e4, rug);
    pf_divFractal(map, x+nw, y,     nw, nh, rw, rh, e1, c2, e2, m,  rug);
    pf_divFractal(map, x+nw, y+nh,  nw, nh, rw, rh, m,  e2, c3, e3, rug);
    pf_divFractal(map, x,    y+nh,  nw, nh, rw, rh, e4, m,  e3, c4, rug);
  } else
    /* Actually write the pixel. */
    map[(int)y*(int)rw + (int)x] = (c1 + c2 + c3 + c4)/4.;
}