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

typedef struct wavhdr_struct {
	uint16_t audioformat;
	uint16_t numchannels;
	uint32_t samplerate;
	uint32_t byterate;
	uint16_t blockalign;
	uint16_t bitspersample;
	uint32_t datasize;
} wavhdr_t;

int readwavheader(FILE* fd, wavhdr_t* hdr) {
	char buf[5];
	uint32_t oclen;
	uint32_t clen;
	buf[4] = '\0';
	if (fread(buf, 4, 1, fd) != 1) {
		return 1;
	}
	if (strcmp(buf, "RIFF") != 0) {
		printf("Not RIFF data!\n");
		return 1;
	}
	if (fread(&oclen, 4, 1, fd) != 1) {
		return 1;
	}
	if (fread(buf, 4, 1, fd) != 1) {
		return 1;
	}
	if (strcmp(buf, "WAVE") != 0) {
		printf("Not WAV format!\n");
		return 1;
	}
	if (fread(buf, 4, 1, fd) != 1) {
		return 1;
	}
	if (strcmp(buf, "fmt ") != 0) {
		printf("No fmt chunk found: %s!\n", buf);
		return 1;
	}
	if (fread(&clen, 4, 1, fd) != 1) {
		return 1;
	}
	if (clen != 16) {
		printf("Chunk length %d is not 16!\n", clen);
		return 1;
	}
	if (fread(&hdr->audioformat, 2, 1, fd) != 1) {
		return 1;
	}
	if (fread(&hdr->numchannels, 2, 1, fd) != 1) {
		return 1;
	}
	if (fread(&hdr->samplerate, 4, 1, fd) != 1) {
		return 1;
	}
	if (fread(&hdr->byterate, 4, 1, fd) != 1) {
		return 1;
	}
	if (fread(&hdr->blockalign, 2, 1, fd) != 1) {
		return 1;
	}
	if (fread(&hdr->bitspersample, 2, 1, fd) != 1) {
		return 1;
	}
	if (fread(buf, 4, 1, fd) != 1) {
		return 1;
	}
	if (strcmp(buf, "data") != 0) {
		printf("Chunk %s is not data!\n", buf);
		return 1;
	}
	if (fread(&hdr->datasize, 4, 1, fd) != 1) {
		return 1;
	}
	return 0;	
}

void printwavheader(wavhdr_t* hdr) {
	printf("WAV header %d bytes format %d, %d channels, %d samples/%d bytes, %d bps, aligned %d\n", hdr->datasize,
		hdr->audioformat, hdr->numchannels, hdr->samplerate, hdr->byterate, hdr->bitspersample, hdr->blockalign);
}

uint64_t comparesamples(wavhdr_t* hdr, FILE* fd, FILE* fd2) {
	uint64_t ret = 0;
	uint32_t i;
	uint16_t bytes = hdr->bitspersample/8;
	char* buf = malloc(bytes);
	char* buf2 = malloc(bytes);
	for (i = 0; i < hdr->datasize; i += bytes) {
		if (fread(buf, bytes, 1, fd) != 1) {
			free(buf);
			free(buf2);
			printf("Early read fail file 1!\n");
			return 1;
		}
		if (fread(buf2, bytes, 1, fd2) != 1) {
			free(buf);
			free(buf2);
			printf("Early read fail file 2!\n");
			return 1;
		}
		if (memcmp(buf, buf2, bytes) != 0) {
			ret = i/bytes + 1;
			printf("Differing sample %d!\n", i/bytes+1);
		}
	}
	free(buf);
	free(buf2);
	return ret;
}

int main(int argc, char** argv) {
	wavhdr_t hdr1, hdr2;
	if (argc < 3) {
		printf("Usage: wavdiff [file].wav [file].wav\n");
		return 1;
	}
	FILE* fd = fopen(argv[1], "r");
	if (fd == NULL) {
		perror("fopen file 1");
		return 1;
	}
	FILE* fd2 = fopen(argv[2], "r");
	if (fd2 == NULL) {
		perror("fopen file 2");
		fclose(fd);
		return 1;
	}
	
	if (readwavheader(fd, &hdr1)) {
		printf("Failed reading WAV header 1\n");
		fclose(fd);
		fclose(fd2);
		return 1;
	}	
	
	if (readwavheader(fd2, &hdr2)) {
		printf("Failed reading WAV header 2\n");
		fclose(fd);
		fclose(fd2);
		return 1;
	}	

	printwavheader(&hdr1);
	printwavheader(&hdr2);

	if (memcmp(&hdr1, &hdr2, sizeof(wavhdr_t)) != 0) {
		printf("WAV headers differ!\n");
		fclose(fd);
		fclose(fd2);
		return 1;
	}

	uint64_t res = comparesamples(&hdr1, fd, fd2);

	if (res != 0) {
		fclose(fd);
		fclose(fd2);
		return 1;
	} else {
		printf("Files do not differ.\n");
	}

	fclose(fd);
	fclose(fd2);
	return 0;
}
