#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define HEIGHT 1800
#define WIDTH 3600


static void Set_Bit(uint16_t *b, size_t bit, unsigned char value){
	uint16_t cur = *(uint16_t *)b;
//	uint16_t one = 1;
	unsigned char b0  = (bit == 0) ? value : (((cur<<15) & 128) >= 128);
	unsigned char b1  = (bit == 1) ? value : (((cur<<14) & 128) >= 128)<<1;
	unsigned char b2  = (bit == 2) ? value : (((cur<<13) & 128) >= 128)<<2;
	unsigned char b3  = (bit == 3) ? value : (((cur<<12) & 128) >= 128)<<3;
	unsigned char b4  = (bit == 4) ? value : (((cur<<11) & 128) >= 128)<<4;
	unsigned char b5  = (bit == 5) ? value : (((cur<<10) & 128) >= 128)<<5;
	unsigned char b6  = (bit == 6) ? value : (((cur<<9) & 128) >= 128)<<6;
	unsigned char b7  = (bit == 7) ? value : (((cur<<8) & 128)  >= 128)<<7;
	unsigned char b8  = (bit == 8) ? value : (((cur<<7) & 128)  >= 128)<<8;
	unsigned char b9  = (bit == 9) ? value : (((cur<<6) & 128)  >= 128)<<9;
	unsigned char b10 = (bit == 10) ? value : (((cur<<5) & 128)  >= 128)<<10;
	unsigned char b11 = (bit == 11) ? value : (((cur<<4) & 128)  >= 128)<<11;
	unsigned char b12 = (bit == 12) ? value : (((cur<<3) & 128)  >= 128)<<12;
	unsigned char b13 = (bit == 13) ? value : (((cur<<2) & 128)  >= 128)<<13;
	unsigned char b14 = (bit == 14) ? value : (((cur<<1) & 128)  >= 128)<<14;
	unsigned char b15 = (bit == 15) ? value : (((cur) & 128)  >= 128)<<15;

	*(uint16_t *)b = b0+b1+b2+b3+b4+b5+b6+b7+b8+b9+b10+b11+b12+b13+b14+b15;

}

// 4 bit describing width
	// 0 - 15

// 4 bit describing members sharing this width
	// 0 - 15

// series of variable bit length pairs containing a number

// 1 byte describing 

//1-8 can fit in 16bit

//8 8 8 8 8 

//16*a + 8*b + 4*c + 2*d + e





//1111111111111111
//111111111111112


//16

int main(int argc, char **argv){

/*

2 bytes first value
//32 byte chunks ?
4 bit size(bits) chunk
16x values of size(bits)








high: 6389 low: -10436
most repeats: 567

16384 14 bits
1 pos/neg
1 repeat

if(repeat)
10 bit # of repeats  //has to be 16 :(

1024 max repeats



*/
	const int16_t twos[14] = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384};

	char *filename_dem = "data.dem";
	char *compressed_dem = "compressed.dem";
	FILE *fpin = fopen(filename_dem, "rb");
	FILE *fpout = fopen(compressed_dem, "wb");
    if(!fpin){
        printf("Error opening file: %s\n", filename_dem);
        return 1;
    }
    if(!fpout){
        printf("Error opening file: %s\n", compressed_dem);
        return 1;
    }

	//char used[65536];
	//(void)memset(&used, '\0', 65536);

	char used_f[256];
	char used_l[256];
	(void)memset(&used_f, '\0', 256);
	(void)memset(&used_l, '\0', 256);

	size_t total_read = 0;
	size_t total_wrote = 0;
	char two_b[2];

	union{ char a[2]; int16_t b;}x;

	uint16_t used_tf = 0;
	uint16_t used_tl = 0;
	int16_t prefetch[16];
	int16_t pre_c = 0;
	uint32_t c1 = 0;
	uint32_t c2 = 0;
	uint32_t c3 = 0;
	uint32_t c4 = 0;
	int16_t largest_pre = 0;
	int16_t tmp = 0;
	int16_t last = 0; //assumption made that first value is not 0
	uint16_t cur_repeats = 0;
	uint16_t tmp_u = 0;
	//uint16_t one = 1;
	uint32_t zeroes = 0;
	int16_t diff = 0;
	int16_t diff_h = 0;
	uint32_t num_1k = 0;
	uint64_t saved = 0;
	uint64_t headers = 0;
	unsigned char header_size = 0;
	unsigned char write_byte = 0;
	unsigned char bit_count = 0;
	unsigned char bit_pos = 0;

	for(c1=0; c1<HEIGHT; c1++){
		for(c2=0; c2<WIDTH; c2++){
			//pre_c++;
			total_read += fread(&two_b, 1, 2, fpin);
			x.a[0] = two_b[0];
			x.a[1] = two_b[1];
			tmp = x.b;
			//if(tmp == 0)
			//	zeroes++;
			if(c1==0 && c2 == 0){
				last=tmp;
				//write complete first 16bit value
				total_wrote += fwrite((const void *)&tmp, 1, 2, fpout);
				continue;
			}
			if(tmp > last)
				diff = tmp - last;
			else
				diff = last - tmp;

			if(diff == 0)
				zeroes++;
			prefetch[pre_c] = diff;
			pre_c++;
			
			if(pre_c == 15 || (c1==HEIGHT-1 && c2 == WIDTH-1)){
				//pre_c = 0;
				//find largest of 16
				largest_pre = prefetch[0];
				for(c4=1; c4<=pre_c; c4++){
					if(prefetch[c4] > largest_pre)
						largest_pre = prefetch[c4];
				}
				for(c3=0; c3<14; c3++){
					if( largest_pre < twos[c3]){
						header_size = (16 - (c3+1));
						saved += (16 - (c3+1))*(pre_c+1);
						break;
					}
				}
				headers += 4;
				//pre_c = 0;
				//last = tmp;
				
				//write this 16(pre_c) component set 1 byte at a time, starting with 4 bit size

				write_byte = header_size; //4 lsb filled
				bit_count += 4;
				header_size*pre_c+header_size


				pre_c = 0;
			}
			last = tmp;

			//write this 16(pre_c) component set 1 byte at a time, starting with 4 bit size
			//
			

			continue;

			if(c1==0 && c2 == 0)
				last=tmp;
			//diff = abs(tmp - last);

			if(tmp > last)
				diff = tmp - last;
			else
				diff = last - tmp;

			if(diff > 8191)
				num_1k++;
			if(diff > diff_h)
				diff_h = diff;


			//if(tmp < 0){
			//	tmp_u = tmp * -1;
			//}
			//used_f[(unsigned char)x.a[0]] = 1;
			//used_l[(unsigned char)x.a[1]] = 1;
			//continue;

			cur_repeats++;
			if(last != tmp || (c2 == WIDTH-1 && c1 == HEIGHT-1)){
				//prepare 2 bytes
				
				//y.b = tmp; //will use 14 least significant bits
				if(tmp < 0){
					//set 0 to 15th bit
					tmp_u = tmp * -1;
					//one>>14
					//tmp_u &= 16384;
					Set_Bit(&tmp_u, 14, 0);
				}else
					Set_Bit(&tmp_u, 14, 1);
				if(cur_repeats>1)
					//set 1 to 16th bit
					Set_Bit(&tmp_u, 15, 1);
				else
					Set_Bit(&tmp_u, 15, 0);
				
				//write
				total_wrote += fwrite((const void *)&tmp_u, 1, 2, fpout);

				//write 16 bytes if repeats
				if(cur_repeats>1){
					//tmp_u = 
					total_wrote += fwrite((const void *)&cur_repeats, 1, 2, fpout);
					
				}
				last = tmp;
				cur_repeats = 0;
				
			}
		}
	}


	//for(c1=0; c1<HEIGHT; c1++){
	//	for(c2=0; c2<WIDTH; c2++){
	for(c1=0; c1<256; c1++){
		if(used_f[c1] == 1)
			used_tf++;
		if(used_l[c1] == 1)
			used_tl++;
	}

	fclose(fpin);
	fclose(fpout);

	printf("Totals:\n");
	//printf("\tIn: %zu\n", total_read);
	//printf("\tOut: %zu\n", total_wrote);
	//printf("USED: f %u l %u\n", used_tf, used_tl);
	//printf("biggest diff: %d\n", diff_h);
	//printf("num_1k: %u\n", num_1k);

	printf("saved bytes: %lf\n", (double)saved/8);
	printf("headers: %lf\n", (double)headers/8);
	printf("estimated size: %lf\n", ((double)HEIGHT*WIDTH*2) - ((double)saved/8) + ((double)headers/8) );
	printf("zeroes: %u\n", zeroes);
	return 0;
}