#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cmath>

/*
 * Generate bitmaps for Julia fractals corresponding to maps of the type z_{n+1} = z_n^2 + c
 * Arguments are
 * <sizex> <sizey> <xmin> <xmax> <ymin> <ymax> <cx> <cy> <max_iterations> <color_scheme>
 * sizex, sizey           --- dimensions of the resulting bitmap
 * xmin, xmax, ymin, ymax --- these control the explored area.
 *                           the bitmap will display the rectangle [xmin, xmax]\times[ymin, ymax]
 * cx, cy                 --- c in the formula
 * color_scheme           --- there are currently three choices for the color_scheme.
 *                           you can make your own if you'd like.
 */

using namespace std;
/*
	24-bit BMP files are very easy to write.
	They are made up of the following header, followed by the raw data --- the color of each pixel written on 3 bytes, in the structure of a matrix.
	I got this from the net somewhere, I don't remember exactly where.
*/
struct bmpHeader
{
	unsigned short MagicWord;
	unsigned int fileSize;
	unsigned int noIdea;
	unsigned int startOfTheBitmap;
	unsigned int sizeOfTheHeader;
	unsigned int width;
	unsigned int height;
	unsigned short planes;
	unsigned short bitsPerPixel;
	unsigned int compression;
	unsigned int compressedSize;
	unsigned int horizontalResolution;
	unsigned int verticalResolution;
	unsigned int numberOfColorsInPalette;
	unsigned int importantColors;
} __attribute__ ((packed));

int writeBMP(const char *filename, int width, int height, char *data, int hR=2835, int vR=2835)
{
	bmpHeader h;
	h.MagicWord = 19778;
	h.fileSize = 54 + width*height*3;
	h.noIdea =0;
	h.startOfTheBitmap = 54;
	h.sizeOfTheHeader = 40;
	h.width = width;
	h.height = height;
	h.planes =1;
	h.bitsPerPixel = 24;
	h.compression = 0;
	h.compressedSize = h.fileSize-54;
	h.horizontalResolution = hR;
	h.verticalResolution = vR;
	h.numberOfColorsInPalette = 0;
	h.importantColors = 0;
	ofstream output;
	output.open(filename);
	output.write((char*)(&h),54);
	output.write(data,h.compressedSize);
	output.close();
	
	return 0;
}

double get_julia_iterations(double x0, double y0, double cx, double cy, int max_iterations)
{
	int i;
	double x = x0;
	double y = y0;
	double xtmp = x;
	for (i=0; i < max_iterations; i++)
	{
		xtmp = cx + x*x - y*y;
		y = cy + 2*x*y;
		x = xtmp;
		if ((x*x+y*y)>4) break;
	}
	return i*1.0/max_iterations;
}

inline void sqrt_colors(double point_level, unsigned char *rgb)
{
	rgb[0] = sqrt(point_level)*255;
	rgb[1] = point_level*255;
	rgb[2] = point_level*point_level*255;
}

inline void ice_colors(double point_level, unsigned char *rgb)
{
	if (point_level<.4)
	{
		rgb[0] = 637.5*point_level;
	}
	else if (point_level<.8)
	{
		rgb[0] = 255;
		rgb[1] = 637.5*(point_level-.4);
	}
	else
	{
		rgb[0] = 255;
		rgb[1] = 255;
		rgb[2] = 1275*(point_level-.8);
	}
}

inline void hot_colors(double point_level, unsigned char *rgb)
{
	if (point_level<.4)
	{
		rgb[2] = 637.5*point_level;
	}
	else if (point_level<.8)
	{
		rgb[2] = 255;
		rgb[1] = 637.5*(point_level-.4);
	}
	else
	{
		rgb[2] = 255;
		rgb[1] = 255;
		rgb[1] = 1275*(point_level-.8);
	}
}

int main(int argc, char *argv[])
{
	unsigned char *map, *tmpadd;
	int i, j, max_iterations, nx, ny, color_scheme;
	double x, y, x0, y0, x1, y1, cx, cy, point_level;
	
	if (argc != 11)
	{
		cout << "\nI want my params!\n";
		return 0;
	}

	nx = atoi(argv[1]);
	ny = atoi(argv[2]);
	x0 = atof(argv[3]);
	x1 = atof(argv[4]);
	y0 = atof(argv[5]);
	y1 = atof(argv[6]);
	cx = atof(argv[7]);
	cy = atof(argv[8]);
	max_iterations = atoi(argv[9]);
	color_scheme = atoi(argv[10]);
	
	map = (unsigned char *) malloc(nx*ny*3);

	for (i=0;i<nx;i++)
	{
		x = x0 + i*(x1-x0)/nx;
		for (j=0;j<ny;j++)
		{
			y = y0 + j*(y1-y0)/ny;
			point_level = get_julia_iterations(x,y,cx,cy,max_iterations);
			tmpadd = map + (i+j*nx)*3;
			switch(color_scheme%3)
			{
				case 0:
					sqrt_colors(point_level, tmpadd);
					break;
				case 1:
					ice_colors (point_level, tmpadd);
					break;
				case 2:
					hot_colors (point_level, tmpadd);
					break;
			}
		}
	}
	writeBMP("rawbmp_new.bmp",nx,ny,(char*)map);
	free(map);

	return 0;
}

