/*
    Living Realms is a primate evolution simulator.
    Copyright (C) 2011  Sterling Pickens

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "DEM.h"

void Adjust_Sea_Level(int load){
	static int sea_level_chng = 100;
	static int is_rising = 0;
	// 1m per 1K years
	if(load){
		sea_level_chng = stats_t.cur_sealevel;
		is_rising = stats_t.sea_rising;
		return;
	}
	if(is_rising){
		if(sea_level_chng == 100){
			is_rising = 0;
		}else{
			sea_level_chng++;
		}
	}else{
		if(sea_level_chng == -128){
			is_rising = 1;
		}else{
			sea_level_chng--;
		}
	}
	stats_t.cur_sealevel = sea_level_chng;
	stats_t.sea_rising = is_rising;
}

void Adjust_Climate(int load){
    static float temp_chng = 32; //starting at high temp
    static int is_rising = 0;
	//every 10K years you'll get a 2c temp change

    if(load){
        temp_chng = stats_t.cur_temp;
        is_rising = stats_t.temp_rising;
        return;
    }
	if(is_rising){
        if(temp_chng > 32){
             is_rising = 0;
         }else{
            temp_chng+=0.2;
         }
	}else{
       if(temp_chng < 28){
           is_rising = 1;
       }else{
           temp_chng-=0.2;
       }
	}
    stats_t.cur_temp = temp_chng;
    stats_t.temp_rising = is_rising;
}

int Load_Terrain(void){
	FILE *fp = fopen(filename_dem, "rb");
	if(!fp){
		printf("Error opening file: %s\n", filename_dem);
		return 1;
	}
	const int start_sealevel = 100;

	ia = malloc(sizeof(struct image_attr));
	ia->image = malloc(sizeof(unsigned char)*HEIGHT*WIDTH*3);
	stats_t.cur_sealevel = start_sealevel;
	stats_t.sea_rising = 0;
	stats_t.cur_temp = 32;
	stats_t.temp_rising = 0;

	char two_b[2];
	union{ char a[2]; int16_t b;}x;
	unsigned int count1, count2;
	unsigned char rgb[3];
	size_t total_read = 0;

	ia->dem = (int16_t **)malloc(sizeof(int16_t *)*HEIGHT);
	for(count1=0; count1<HEIGHT; count1++){
		ia->dem[count1] = (int16_t *)malloc(sizeof(int16_t)*WIDTH);
	}
	for(count1=0; count1<HEIGHT; count1++){
		for(count2=0; count2<WIDTH; count2++){
			total_read += fread(&two_b, 1, 2, fp);
			x.a[0] = two_b[0];
			x.a[1] = two_b[1];
			ia->dem[count1][count2] = x.b;
			if(x.b < start_sealevel){
				rgb[0] = 0;
				rgb[1] = 0;
				rgb[2] = 255;
			}else{
				rgb[0] = 0;
				rgb[1] = 255;
				rgb[2] = 0;
			}
			ia->image[count1*WIDTH*3+(count2*3)] = rgb[0];
			ia->image[count1*WIDTH*3+(count2*3+1)] = rgb[1];
			ia->image[count1*WIDTH*3+(count2*3+2)] = rgb[2];
		}
	}
	fclose(fp);
	if(total_read != 2*HEIGHT*WIDTH){
		printf("Error: fread sum does for '%s' not match with compile-time DEM dimensions!\n", filename_dem);
		printf("\tread %zu expected %u\n", total_read, 2*HEIGHT*WIDTH);
		for(count1=0; count1<HEIGHT; count1++){
			free(ia->dem[count1]);
		}
		free(ia->dem);
		free(ia->image);
		free(ia);
		return 1;
	}
	printf("Loaded DEM from '%s'\n", filename_dem);
	printf("\tread %zu bytes\n", total_read);

	return 0;
}

void Gen_Detailed_Counts(void){
	//printf 10 most numerous species info
	//and update stats_t
	uint32_t h; //, w;
	//uint32_t total_living = 0;
	uint32_t chng = 0;

	//for(h=0; h<HEIGHT; h++){
	//	for(w=0; w<WIDTH; w++){
	//		if(life[h][w]->alive){
	//			total_living++;
	//		}
	//	}
	//}
	for(h=0; h<Threads; h++){
            stats_t.disease += stats[h].disease;
            stats_t.drown += stats[h].drown;
			stats_t.exposure += stats[h].exposure;
            stats_t.old_age += stats[h].old_age;
            stats_t.died_attacking += stats[h].died_attacking;
            stats_t.died_defending += stats[h].died_defending;
            stats_t.new_life += stats[h].new_life;
            stats_t.new_species += stats[h].new_species;
			//stats_t.total_runtime += runtime
			//stats_t.total_living += stats[h].total_living;
	}
	stats_t.total_runtime += Backup_Freq_Runtime;
	//printf("Total Living: %u\n\n", total_living);
	printf("Year: %u\n", stats_t.year); 
	if(stats_t.sea_rising){
		printf("Current: Sec/Year %.04lfs cur_sealevel %dm rising +1m/1Kyears", Backup_Freq_Runtime/1000, stats_t.cur_sealevel);
	}else{
		printf("Current: Sec/Year %.04lfs cur_sealevel %dm falling -1m/1Kyears", Backup_Freq_Runtime/1000, stats_t.cur_sealevel);
	}
	printf(" equatorial mean temp: %.02fc changing: %.02fc/1Kyears\n", stats_t.cur_temp, (stats_t.temp_rising ? 0.2 : -0.2) );
	printf("Total: Runtime %.02lf hrs Avg Sec/Year %lfs\n\n", (double)stats_t.total_runtime/60/60, (double)stats_t.total_runtime/stats_t.year);

	printf("\tdisease: %lu\n", stats_t.disease);
	for(h=0; h<Threads; h++){
		chng += stats[h].disease;
	}
	printf("\t\tchng: %u\n",chng); 

	printf("\tdrown: %lu\n", stats_t.drown);
	chng=stats[0].drown;
	for(h=1; h<Threads; h++){
		chng += stats[h].drown;
	}
	printf("\t\tchng: %u\n",chng);

    printf("\texposure: %lu\n", stats_t.exposure);
    chng=stats[0].exposure;
    for(h=1; h<Threads; h++){
        chng += stats[h].exposure;
    }
    printf("\t\tchng: %u\n",chng);

	printf("\told_age: %lu\n", stats_t.old_age);
	chng=stats[0].old_age;
	for(h=1; h<Threads; h++){
		chng += stats[h].old_age;
	}
	printf("\t\tchng: %u\n",chng);

	printf("\tdied_attacking: %lu\n", stats_t.died_attacking);
	chng=stats[0].died_attacking;
	for(h=1; h<Threads; h++){
		chng += stats[h].died_attacking;
	}
	printf("\t\tchng: %u\n",chng);

	printf("\tdied_defending: %lu\n", stats_t.died_defending);
	chng=stats[0].died_defending;
	for(h=1; h<Threads; h++){
		chng += stats[h].died_defending;
	}
	printf("\t\tchng: %u\n",chng);

	printf("\tnew_life: %lu\n", stats_t.new_life);
	chng=stats[0].new_life;
	for(h=1; h<Threads; h++){
		chng += stats[h].new_life;
	}
	printf("\t\tchng: %u\n",chng);

	printf("\tnew_species: %lu\n", stats_t.new_species);
	chng=stats[0].new_species;
	for(h=1; h<Threads; h++){
		chng += stats[h].new_species;
	}
	printf("\t\tchng: %u\n\n",chng);
	//for(h=0; h<HEIGHT; H++
	//printf("\tTotal_Population: %lu\n", stats_t.total_living);
	//chng=stats[0].total_living;
	//for(h=1; h<Threads; h++){
	//	chng += stats[h].total_living;
	//}
	//printf("\t\tchng: %u\n\n",chng);

	//printf("Total Living: %u\n\n", total_living);
	Backup_Freq_Runtime = 0;
	Year_Runtime = 0;
	//stats_t.total_living = 0;
	//reset thread stats
	for(h=0; h<Threads; h++){
		(void)memset((void *)&stats[h], '\0', sizeof(struct Stats) );
	}
}

int Save_Current_State(void){
	FILE *fp = fopen(filename_backup_save, "wb");
    if(!fp){
        printf("Error opening file: %s\n", filename_backup_save);
        return 1;
    }
	uint32_t w = 0, h = 0;
	size_t bytes = 0;
	uint64_t total_living = 0;
	for(h=0; h<HEIGHT; h++){
		if(!life_speed_up_Y[h])
			continue;
		for(w=0; w<WIDTH; w++){
			if(!life_speed_up_X[w])
				continue;
			//if(life[h][w]->alive)
			if( IS_SET(Alive, h, w) )
				total_living++;
		}
	}
	stats_t.total_living = total_living;

	bytes += fwrite((const void *)&stats_t, 1, sizeof(struct Stats_T), fp);
	//bytes += fwrite((const void *)Alive, 1, sizeof(unsigned char)*HEIGHT*(size_t)ceil(WIDTH/8), fp);
	//bytes += fwrite((const void *)life, 1, sizeof(struct Species)*HEIGHT*WIDTH, fp);

	for(h=0; h<HEIGHT; h++){
		bytes += fwrite((const void *)Alive[h], 1, sizeof(unsigned char)*ceil(WIDTH/8), fp);
	}
	for(h=0; h<HEIGHT; h++){
		for(w=0; w<WIDTH; w++){
			bytes += fwrite((const void *)life[h][w], 1, sizeof(struct Species), fp);
		}
	}
/*
	bytes += fwrite((const void *)&stats_t.year, sizeof(uint32_t), 1, fp);
	bytes += fwrite((const void *)&stats_t.cur_temp, sizeof(float), 1, fp);
	bytes += fwrite((const void *)&stats_t.cur_sealevel, sizeof(int16_t), 1, fp);
	bytes += fwrite((const void *)&stats_t.sea_rising, sizeof(char), 1, fp);
	bytes += fwrite((const void *)&stats_t.temp_rising, sizeof(char), 1, fp);
*/
	printf("Saved current state to '%s'!\n", filename_backup_save);
	printf("\tbytes wrote: %zu\n", bytes);
	printf("\tTotal_Population: %lu\n\n", total_living);
	//free(filename);
	fclose(fp);
	//if(bytes != (sizeof(uint32_t) + sizeof(float) + sizeof(int16_t) + 2 + sizeof(struct Species)) )
	//	return 1;
	//add crc later

	return 0;
}

int Load_State(void){
    FILE *fp = fopen(filename_backup_load, "rb");
    if(!fp){
        printf("Error opening file: %s\n", filename_backup_load);
        return 1;
    }
	uint32_t w = 0, h = 0;
	size_t bytes = 0;

	bytes += fread((void *)&stats_t, 1, sizeof(struct Stats_T), fp);
	//bytes += fread((void *)Alive, 1, sizeof(unsigned char)*HEIGHT*(size_t)ceil(WIDTH/8), fp);
	/*
	struct Species_old{
	    unsigned char alive;
	   	unsigned char max_age;
	    unsigned char cur_age;
	    unsigned char imunity; //each time contact with another tribe doesn't kill them this goes up
	    unsigned char water_turns;
	    unsigned char migration_factor;
	    unsigned char interspecies_violence;
	    unsigned char social_factor;
	    unsigned char fight_flight_balance;
	    unsigned char intelligence;
	    unsigned char strength;
	    unsigned char speed;
	    unsigned char temp_l;
	    unsigned char temp_h;
		unsigned char water_direction;
	}tmp;
	*/
	// *life_old[HEIGHT][WIDTH];


	//for(h=0; h<HEIGHT; h++){
	//	for(w=0; w<WIDTH; w++){
	//		life_old[h][w] = (struct Species_old *)malloc(sizeof(struct Species_old));
	//	}
	//}
	/*
    for(h=0; h<HEIGHT; h++){
        for(w=0; w<WIDTH; w++){
            bytes += fread((void *)&tmp, 1, sizeof(struct Species_old), fp);
			if(tmp.alive){
				SET_ONE(Alive, h, w);
				life[h][w]->max_age = tmp.max_age;
				life[h][w]->cur_age = tmp.cur_age;
				life[h][w]->imunity = tmp.imunity;
				life[h][w]->water_turns = tmp.water_turns;
				life[h][w]->migration_factor = tmp.migration_factor;
				life[h][w]->interspecies_violence = tmp.interspecies_violence;
				life[h][w]->social_factor = tmp.social_factor;
				life[h][w]->fight_flight_balance = tmp.fight_flight_balance;
				life[h][w]->intelligence = tmp.intelligence;
				life[h][w]->strength = tmp.strength;
				life[h][w]->speed = tmp.speed;
				life[h][w]->temp_l = tmp.temp_l;
				life[h][w]->temp_h = tmp.temp_h;
				life[h][w]->water_direction = tmp.water_direction;
			}
        }
    }
	*/
	//for(h=0; h<HEIGHT; h++){
	//	for(w=0; w<WIDTH; w++){
	//		free(life_old[h][w]);
	//	}
	//}



	for(h=0; h<HEIGHT; h++){
		bytes += fread((void *)Alive[h], 1, sizeof(unsigned char)*ceil(WIDTH/8), fp);
	}

	//bytes += fread((void *)life, 1, sizeof(struct Species)*HEIGHT*WIDTH, fp);
	for(h=0; h<HEIGHT; h++){
		for(w=0; w<WIDTH; w++){
			bytes += fread((void *)life[h][w], 1, sizeof(struct Species), fp);
		}
	}



/*
	bytes += fread((void *)&stats_t.year, sizeof(uint32_t), 1, fp);
	bytes += fread((void *)&stats_t.cur_temp, sizeof(float), 1, fp);
	bytes += fread((void *)&stats_t.cur_sealevel, sizeof(int16_t), 1, fp);
	bytes += fread((void *)&stats_t.sea_rising, sizeof(char), 1, fp);
	bytes += fread((void *)&stats_t.temp_rising, sizeof(char), 1, fp);
*/
	//bytes += fread((void *)life_speed_up_H, sizeof(char)*height, 1, fp);
	//bytes += fread((void *)life_speed_up_W, sizeof(char)*width, 1, fp);
	//stats_t.cur_sealevel = -115;
	//stats_t.sea_rising = 0;

	printf("Loaded state from '%s'\n", filename_backup_load);
	printf("\tbytes read: %zu\n", bytes);
	bytes = 0;
	for(h=0; h<HEIGHT; h++){
		for(w=0; w<WIDTH; w++){
			if(IS_SET(Alive, h, w))
				bytes++;
		}
	}
	printf("\ttotal population: %zu\n", bytes);
	//free(filename);
	fclose(fp);
	return 0;
}