#include "main.h"

#include <math.h>

#ifndef MAX
	#define max( a,b ) ( ((a) > (b) ) ? (a) : (b) )
#endif
	
//variables
int width, height, size;
float corner_value[4];

//functions
int closest_pow_2(int);
void init_height_map(float**);
void start_diamond_square(float**);
void diamond_square(float**,int,int,int,int);
float average(float,float);
float average_noisy(float,float);
float average_t(float*, int);
void apply_noise(float*);

void create_map(int w, int h){
	width = w;
	height = h;
	
	int i;
	float** height_map;
	
	size=closest_pow_2(max(w,h));
	
	height_map = (float**) malloc(sizeof(float*)*(size+1));
	for(i=0;i<=size;i++)
		height_map[i]=malloc(sizeof(float)*(size+1));
	
	printf("init height map...\n");
	init_height_map(height_map);
	
	printf("generate height map...\n");
	//generate height map
	start_diamond_square(height_map);
	
	printf("erosion\n");
	
	
	
	printf("finished generation! \n");
	
}

int closest_pow_2(int x){
	return pow(2,floor(log(x)/log(2))+1);
}

void init_height_map(float** h_map){
	h_map[0][0]=0.7;
	h_map[size][0]=0.3;
	h_map[size][size]=0.9;
	h_map[0][size]=0.8;
}

void start_diamond_square(float ** h_map){
	diamond_square(h_map,0,size,0,size);
}

void dummy(int x1, int y1, int x2, int y2, int avg_x,int avg_y){
}

void diamond_square(float** h_map,int x1,int x2,int y1, int y2){
	int avg_x, avg_y;
	float avg_value;
	
	//get value of corner from h_map
	corner_value[0] = h_map[x1][y1]; //up_left
	corner_value[1] = h_map[x2][y1]; //up_right
	corner_value[2] = h_map[x2][y2]; //down_right
	corner_value[3] = h_map[x1][y2]; //down_left
	
	//process coordinate of center
	avg_x = average(x1,x2);
	avg_y = average(y1,y2);
	
	//dummy(x1,y1,x2,y2,avg_x,avg_y);
	//Diamond Step
	//process average value of the corner
	avg_value = average_t(corner_value,4);
	
	apply_noise(&avg_value);
	//affect value
	h_map[avg_x][avg_y]=avg_value;
	
	// Square Step
	//update value of the four mid-point between corner, following clockwise orde, and adding noise
	h_map[avg_x][y1] = average_noisy(corner_value[0], corner_value[1]); // up
	h_map[x2][avg_y] = average_noisy(corner_value[1], corner_value[2]); // right
	h_map[avg_x][y2] = average_noisy(corner_value[2], corner_value[3]); // down
	h_map[x1][avg_y] = average_noisy(corner_value[3], corner_value[0]); // left
	
	//recursive call to diamond_square
	int offset = x2-x1;
	
	if (offset > 2){
		diamond_square(h_map, x1, avg_x, y1, avg_y);  //up_left square
		diamond_square(h_map, avg_x, x2, y1, avg_y); // up_right square
		diamond_square(h_map, avg_x, x2, avg_y, y2); // down_right square
		diamond_square(h_map, x1, avg_x, avg_y, y2); // down_left square
	}
}

float average(float a, float b){
	return (a+b)/2;
}

float average_noisy(float a, float b){
	float value = average(a,b);
	apply_noise(&value);
	return value;
}

float average_t(float* tab, int size){
	int i;
	float avg_value=0;
	for(i=0;i<size;i++)
		avg_value += tab[i];
	return avg_value/size;
}

void apply_noise(float* value){
	float noise = (float)rand()/(float)RAND_MAX;
	*value = *value + noise - 1;
}