#include "c4.h"
#include <stdarg.h>

C4Fractal c4_fractal_create(struct Image *img,
                            int param_count,
                            ...)
{
  int i;
  C4Fractal fr;
  va_list list;

  if(!(fr = (C4Fractal)
       malloc(sizeof(struct Fractal)))) {
    fprintf(stderr,
            "c4_fractal_create: memory exhausted\n");
    return(NULL);
  }

  fr->image = img;
  fr->julia_p = 0;
  fr->r_min = -2;
  fr->r_max = 1;
  fr->i_min = -1;
  fr->i_max = 1;
  fr->itr_limit = 20;
  fr->julia_constant = c4_complex_create(0,0);

  va_start(list,param_count);

  for(i=0;i<param_count;i++) {
    enum FractalParam param;

    param = va_arg(list,enum FractalParam);
    switch(param) {
    case JuliaP:
      fr->julia_p = va_arg(list,unsigned int);

      if(fr->julia_p) {
        double r,i;

        r = va_arg(list,double);
        i = va_arg(list,double);
        c4_complex_set(fr->julia_constant,r,i);
      }

      break;
    case RealLimit:
      fr->r_min = va_arg(list,double);
      fr->r_max = va_arg(list,double);

      break;
    case ImaginaryLimit:
      fr->i_min = va_arg(list,double);
      fr->i_max = va_arg(list,double);

      break;
    case ItrLimit:
      fr->itr_limit = va_arg(list,unsigned int);

      break;
    default:
      fprintf(stderr,
              "c4_fractal_create: unknown param %d\n",
              (int)param);
    }
  }

  va_end(list);

  return(fr);
}

void c4_fractal_destroy(C4Fractal fr)
{
  c4_complex_destroy(fr->julia_constant);
  free(fr);
}

static double fractal_eval(C4Fractal fr,
                           double r,
                           double i)
{
  int k;
  C4Complex a_new,a_old,ic;

  /* create and initialize our values */

  if(fr->julia_p)
    ic = c4_complex_copy(NULL,fr->julia_constant);
  else
    ic = c4_complex_set(NULL,r,i);

  a_new = c4_complex_set(NULL,0,0);
  a_old = c4_complex_set(NULL,r,i);

  /* iterate the map on this point, to
   * determine its ultimate trajectory */

  for(k=0;k<fr->itr_limit;k++) {

    /* a_new = a_old^2 + ic */

    c4_complex_mul(a_old,a_old,a_new);
    c4_complex_add(a_new,ic,a_new);

    if(c4_complex_size(a_new) > 4)
      break;

    c4_complex_copy(a_old,a_new);
  }

  /* we created these complex numbers; now we
   * must destroy them */

  c4_complex_destroy(a_new);
  c4_complex_destroy(a_old);
  c4_complex_destroy(ic);

  /* the return value is a measure of how long
   * it took to get where it was going */

  return(1 - ((double)k / (double)fr->itr_limit));
}

void c4_fractal_render(C4Fractal fr)
{
  int x,y;
  double r_span,i_span;

  r_span = fr->r_max - fr->r_min;
  i_span = fr->i_max - fr->i_min;

  for(x=0;x<fr->image->width;x++)
    for(y=0;y<fr->image->height;y++) {
      double r,i,d;

      r = fr->r_min + (r_span * 
                       ((double)x /
                        (double)(fr->image->width - 1)));
      i = fr->i_min + (i_span *
                       ((double)y /
                        (double)(fr->image->height - 1)));
      d = fractal_eval(fr,r,i);

      ImagePixel(fr->image,x,y).r = (1-d) * 255;
      ImagePixel(fr->image,x,y).g = d * 255;
      ImagePixel(fr->image,x,y).b = (1-d) * 255;
    }
}
