#include <math.h>
#include <stdio.h>

#include "fractal.h"

static float interpolate(float x, float x_min, float x_max,
		float y_min, float y_max)
{
	x = (x - x_min) / (x_max - x_min);
	return x * (y_max - y_min) + y_min;

}

/*
 * given a the i and i_max values from a point in our (x,y) coorinates,
 * compute the colour of the pixel at that point.
 *
 * This function does a simplified Hue,Saturation,Value transformation to
 * RGB. We take i/i_max as the Hue, and keep the saturation and value
 * components fixed.
 */
void colour_map(struct pixel *pix, float i, float i_max)
{
	const float saturation = 0.8;
	const float value = 0.8;
	float v_min, hue, desc, asc, step;

	hue = i / (i_max + 1);
	v_min = value * (1 - saturation);

	/* create two linear curves, between value and v_min, of the
	 * proportion of a colour to include in the rgb output. One
	 * is ascending over the 60 degrees, the other descending
	 */
	step = (float)((int)floor(hue) % 60) / 60.0;
	asc  = (step * value) + ((1.0 - step) * v_min);
	desc = (step * v_min) + ((1.0 - step) * value);

	if (hue < 0.25) {
		pix->r = value * 255;
		pix->g = interpolate(hue, 0.0, 0.25, v_min, value) * 255;
		pix->b = v_min * 255;

	} else if (hue < 0.5) {
		pix->r = interpolate(hue, 0.25, 0.5, value, v_min) * 255;
		pix->g = value * 255;
		pix->b = v_min * 255;

	} else if (hue < 0.75) {
		pix->r = v_min * 255;
		pix->g = value * 255;
		pix->b = interpolate(hue, 0.5, 0.75, v_min, value) * 255;

	} else {
		pix->r = v_min * 255;
		pix->g = interpolate(hue, 0.75, 1.0, value, v_min) * 255;
		pix->b = value * 255;
	}

	pix->a = 255;
}


/**
 * Render a fractal, given the parameters specified in @params
 */
void render_fractal(struct fractal_params *params)
{
	int i, x, y;
	/* complex numbers: c and z */
	float cr, ci, zr, zi;
	float x_min, y_min, tmp;

	x_min = params->x - (params->delta * params->cols / 2);
	y_min = params->y - (params->delta * params->rows / 2);

	for (y = 0; y < params->rows; y++) {
		ci = y_min + y * params->delta;

		for (x = 0; x < params->cols; x++) {
			cr = x_min + x * params->delta;

			zr = 0;
			zi = 0;

			for (i = 0; i < params->i_max; i++)  {
				/* z = z^2 + c */
				tmp = zr*zr - zi*zi + cr;
				zi =  2.0 * zr * zi + ci;
				zr = tmp;

				/* if abs(z) > 2.0 */
				if (zr*zr + zi*zi > 4.0)
					break;
			}

			colour_map(&params->imgbuf[y * params->cols + x],
					i, params->i_max);
		}
	}
}

