#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>

#include "eeptypes.h"

struct header_t header;
struct atom_t atom;
struct vendor_info_d vinf;
struct gpio_map_d gpiomap;
unsigned char* data;

int read_bin(char *in, char *outf) {

	uint16_t crc;
	FILE *fp, *out;
	int i,j;
	
	fp=fopen(in, "r");
	if (!fp) {
		printf("Error reading file %s\n", in);
		return -1;
	}
	
	out=fopen(outf, "w");
	if (!out) {
		printf("Error writing file %s\n", outf);
		return -1;
	}
	
	if (!fread(&header, sizeof(header), 1, fp)) goto err;
	
	fprintf(out, "# ---------- Dump generated by eepdump handling format version 0x%02x ----------\n#\n", FORMAT_VERSION);
	
	if (FORMAT_VERSION!=header.ver) fprintf(out, "# WARNING: format version mismatch!!!\n");
	
	fprintf(out, "# --Header--\n# signature=0x%08x\n# version=0x%02x\n# reserved=%u\n# numatoms=%u\n# eeplen=%u\n# ----------\n\n\n", header.signature, header.ver, header.res, header.numatoms, header.eeplen);
				
	
	for (i = 0; i<header.numatoms; i++) {
		
		if (!fread(&atom, ATOM_SIZE-CRC_SIZE, 1, fp)) goto err;
		
		printf("Reading atom %d...\n", i);
		
		fprintf(out, "# Start of atom #%u of type 0x%04x and length %u\n", atom.count, atom.type, atom.dlen);
		
		if (atom.count != i) {
			printf("Error: atom count mismatch\n");
			fprintf(out, "# Error: atom count mismatch\n");
		}
		
		long pos = ftell(fp);
		char *atom_data = (char *) malloc(atom.dlen + ATOM_SIZE-CRC_SIZE);
		memcpy(atom_data, &atom, ATOM_SIZE-CRC_SIZE);
		if (!fread(atom_data+ATOM_SIZE-CRC_SIZE, atom.dlen, 1, fp)) goto err;
		uint16_t calc_crc = getcrc(atom_data, atom.dlen-CRC_SIZE+ATOM_SIZE-CRC_SIZE);
		fseek(fp, pos, SEEK_SET);
		
		if (atom.type==ATOM_VENDOR_TYPE) {
			//decode vendor info
			
			if (!fread(&vinf, VENDOR_SIZE, 1, fp)) goto err;
			
			fprintf(out, "# Vendor info\n");
			fprintf(out, "product_uuid %08x-%04x-%04x-%04x-%04x%08x\n", vinf.serial_4, vinf.serial_3>>16, vinf.serial_3 & 0xffff, vinf.serial_2>>16, vinf.serial_2 & 0xffff, vinf.serial_1);
			fprintf(out, "product_id 0x%04x\n", vinf.pid);
			fprintf(out, "product_ver 0x%04x\n", vinf.pver);
			
			vinf.vstr = (char *) malloc(vinf.vslen+1);
			vinf.pstr = (char *) malloc(vinf.pslen+1);
			
			if (!fread(vinf.vstr, vinf.vslen, 1, fp)) goto err;
			if (!fread(vinf.pstr, vinf.pslen, 1, fp)) goto err;
			//close strings
			vinf.vstr[vinf.vslen] = 0;
			vinf.pstr[vinf.pslen] = 0;
			
			fprintf(out, "vendor \"%s\"   # length=%u\n", vinf.vstr, vinf.vslen);
			fprintf(out, "product \"%s\"   # length=%u\n", vinf.pstr, vinf.pslen);
			
			if (!fread(&crc, CRC_SIZE, 1, fp)) goto err;
			
		} else if (atom.type==ATOM_GPIO_TYPE) {
			//decode GPIO map
			if (!fread(&gpiomap, GPIO_SIZE, 1, fp)) goto err;
			
			fprintf(out, "# GPIO map info\n");
			fprintf(out, "gpio_drive %d\n", gpiomap.flags & 15); //1111
			fprintf(out, "gpio_slew %d\n", (gpiomap.flags & 48)>>4); //110000
			fprintf(out, "gpio_hysteresis %d\n", (gpiomap.flags & 192)>>6); //11000000
			fprintf(out, "back_power %d\n", gpiomap.power);
			fprintf(out, "#        GPIO  FUNCTION  PULL\n#        ----  --------  ----\n");

			for (j = 0; j<28; j++) {
				if (gpiomap.pins[j] & (1<<7)) {
					//board uses this pin
					
					char *pull_str = "INVALID";
					switch ((gpiomap.pins[j] & 96)>>5) { //1100000
						case 0:	pull_str = "DEFAULT";
								break;
						case 1: pull_str = "UP";
								break;
						case 2: pull_str = "DOWN";
								break;
						case 3: pull_str = "NONE";
								break;
					}
					
					char *func_str = "INVALID";
					switch ((gpiomap.pins[j] & 7)) { //111
						case 0:	func_str = "INPUT";
								break;
						case 1: func_str = "OUTPUT";
								break;
						case 4: func_str = "ALT0";
								break;
						case 5: func_str = "ALT1";
								break;
						case 6: func_str = "ALT2";
								break;
						case 7: func_str = "ALT3";
								break;
						case 3: func_str = "ALT4";
								break;
						case 2: func_str = "ALT5";
								break;
					}
					
					fprintf(out, "setgpio  %d      %s     %s\n", j, func_str, pull_str);
				}
			}
			
			if (!fread(&crc, CRC_SIZE, 1, fp)) goto err;
			
		} else if (atom.type==ATOM_DT_TYPE) {
			//decode DT blob
			
			fprintf(out, "dt_blob");
			data = (char *) malloc(atom.dlen-CRC_SIZE);
			if (!fread(data, atom.dlen-CRC_SIZE, 1, fp)) goto err;
			
			for (j = 0; j<atom.dlen-CRC_SIZE; j++) {
				if (j % 16 == 0) fprintf(out, "\n");
				fprintf(out, "%02X ", *(data+j));
			}
			
			fprintf(out, "\n");
			
			if (!fread(&crc, CRC_SIZE, 1, fp)) goto err;
			
		} else if (atom.type==ATOM_CUSTOM_TYPE) {
			//decode custom data
			
			fprintf(out, "custom_data");
			data = (char *) malloc(atom.dlen-CRC_SIZE);
			if (!fread(data, atom.dlen-CRC_SIZE, 1, fp)) goto err;
			
			for (j = 0; j<atom.dlen-CRC_SIZE; j++) {
				if (j % 16 == 0) fprintf(out, "\n");
				fprintf(out, "%02X ", *(data+j));
			}
			
			fprintf(out, "\n");
			
			if (!fread(&crc, CRC_SIZE, 1, fp)) goto err;
			
			
		} else {
			printf("Error: unrecognised atom type\n");
			fprintf(out, "# Error: unrecognised atom type\n");
			goto err;
		}
		
		fprintf(out, "# End of atom. CRC16=0x%04x\n", crc);
		
		if (calc_crc != crc) {
			printf("Error: atom CRC16 mismatch\n");
			fprintf(out, "# Error: atom CRC16 mismatch. Calculated CRC16=0x%02x", crc);
		} else printf("CRC OK\n");
		
		fprintf(out, "\n\n");
	
	}
	
	//Total length checks. We need header.eeplen=current_position=file_length.
	long pos = ftell(fp);
	fseek(fp, 0L, SEEK_END);
	
	if (pos!=ftell(fp)) printf("Warning: Dump finished before EOF\n");
	if (pos!=header.eeplen) printf("Warning: Dump finished before length specified in header\n");
	if (ftell(fp)!=header.eeplen) printf("Warning: EOF does not match length specified in header\n");
	
	printf("Done.\n");
	
	fclose(fp);
	fclose(out);
	return 0;
	
err:
	printf("Unexpected EOF or error occurred\n");
	fclose(fp);
	fclose(out);
	return 0;
}


int main(int argc, char *argv[]) {
	int ret;
	int i;
	
	if (argc<3) {
		printf("Wrong input format.\n");
		printf("Try 'eepdump input_file output_file'\n");
		return 0;
	}
	
	
	ret = read_bin(argv[1], argv[2]);
	if (ret) {
		printf("Error reading input, aborting\n");
		return 0;
	}
	

	return 0;
}
