#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>

//ref_start[http://www.w3.org/TR/PNG/]
/* Table of CRCs of all 8-bit messages. */
unsigned long crc_table[256];

/* Flag: has the table been computed? Initially false. */
int crc_table_computed = 0;

/* Make the table for a fast CRC. */
void make_crc_table(void)
{
	unsigned long c;
	int n, k;

	for (n = 0; n < 256; n++) {
		c = (unsigned long) n;
		for (k = 0; k < 8; k++) {
			if (c & 1)
				c = 0xedb88320L ^ (c >> 1);
			else
				c = c >> 1;
		}
		crc_table[n] = c;
	}
	crc_table_computed = 1;
}

/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below). */

unsigned long update_crc(unsigned long crc, unsigned char *buf,
						 int len)
{
	unsigned long c = crc;
	int n;

	if (!crc_table_computed)
		make_crc_table();
	for (n = 0; n < len; n++) {
		c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
	}
	return c;
}

/* Return the CRC of the bytes buf[0..len-1]. */
unsigned long crc(unsigned char *buf, int len)
{
	return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
}
//ref_end[http://www.w3.org/TR/PNG/]

struct DATASTREAM {
	unsigned char ucSignature[8];
	unsigned char ucChunkDataLength[4];
	unsigned long ulChunkTypeAndChunkDataLength;
	unsigned char ucChunkTypeAndChunkData[4 + 32768];
	unsigned char ucCrcInFile[4]; // CRC of ChunkType and ChunkData[In PNG Files]
	unsigned long ulCrcInFile;    // CRC of ChunkType and ChunkData[In PNG Files]	
} datastream;

int main(int argc, char* argv[]) {
	unsigned long ulCrcByCalc;    // CRC of ChunkType and ChunkData[By Calculate]
	size_t freadsize = 0;         // The number of full items actually read
	bool bCrcCheckResult = true;

	if(2 == argc) { // Check arg OK
#ifdef _DEBUG
		printf("arg:%s %s[%d]\n", argv[0], argv[1], __LINE__);
#endif
	} else { // Check arg NG
		printf("usage:%s path/file.png\n", argv[0], __LINE__);
		return false;
	}

	// Open for read (will fail if file does not exist)
	FILE *filestream = NULL;	
	if( NULL != (filestream  = fopen( argv[1], "rb" )) ) {
#ifdef _DEBUG
		printf( "fopen OK[%d]\n", __LINE__);
#endif
	} else {
		printf( "fopen NG[%d]\n", __LINE__);
		return false;
	}

	//Signature_start
	freadsize = fread(
		datastream.ucSignature,
		sizeof( char ),
		sizeof( datastream.ucSignature ),
		filestream);
	if(freadsize == sizeof( datastream.ucSignature )) {
#ifdef _DEBUG
		printf( "fread OK:%s[%d]\n", "Signature", __LINE__);
#endif
		if (datastream.ucSignature[0] == 0x89 &&
			datastream.ucSignature[1] == 0x50 &&
			datastream.ucSignature[2] == 0x4E &&
			datastream.ucSignature[3] == 0x47 &&
			datastream.ucSignature[4] == 0x0D &&
			datastream.ucSignature[5] == 0x0A &&
			datastream.ucSignature[6] == 0x1A &&
			datastream.ucSignature[7] == 0x0A)
		{
#ifdef _DEBUG
			printf( "check OK:%s[%d]\n", "Signature", __LINE__);
#endif
		} else {
			printf( "check NG:%s[%d]\n", "Signature", __LINE__);
			return false;
		}
	} else {
		printf( "fread NG:%s[%d]\n", "Signature", __LINE__);
		return false;
	}
	//Signature_end

	do {
		//ChunkDataLength_start
		freadsize = fread(
			datastream.ucChunkDataLength,
			sizeof( char ),
			sizeof( datastream.ucChunkDataLength ),
			filestream);
		if(freadsize == sizeof( datastream.ucChunkDataLength )) {
#ifdef _DEBUG
			printf( "fread OK:%s[%d]\n", "ChunkDataLength", __LINE__);
#endif
			datastream.ulChunkTypeAndChunkDataLength = 4 + (
				(datastream.ucChunkDataLength[0] << 24) | 
				(datastream.ucChunkDataLength[1] << 16) | 
				(datastream.ucChunkDataLength[2] <<  8) | 
				(datastream.ucChunkDataLength[3]      )
				);

			//ChunkTypeAndChunkData_start
			freadsize = fread(
				datastream.ucChunkTypeAndChunkData,
				sizeof( char ),
				datastream.ulChunkTypeAndChunkDataLength,
				filestream);
			if(freadsize == datastream.ulChunkTypeAndChunkDataLength) {
#ifdef _DEBUG
				printf( "fread OK:%s[%d]\n", "ChunkTypeAndChunkData", __LINE__);
#endif
				for(int ii = 0; ii < 4; ii++) {
					printf("%c", datastream.ucChunkTypeAndChunkData[ii]);
				}

				//CrcInFile_start
				freadsize = fread(
					datastream.ucCrcInFile,
					sizeof( char ),
					sizeof( datastream.ucCrcInFile ),
					filestream);
				if(freadsize == sizeof( datastream.ucCrcInFile )) {
#ifdef _DEBUG
					printf( "fread OK:%s[%d]\n", "CrcInFile", __LINE__);
#endif
					datastream.ulCrcInFile = (
						(datastream.ucCrcInFile[0] << 24) |
						(datastream.ucCrcInFile[1] << 16) |
						(datastream.ucCrcInFile[2] <<  8) |
						(datastream.ucCrcInFile[3]      )
						);

					ulCrcByCalc = crc(
						datastream.ucChunkTypeAndChunkData,
						datastream.ulChunkTypeAndChunkDataLength);

					if( ulCrcByCalc == datastream.ulCrcInFile ){
						printf(" check OK!\n");
					} else {
						bCrcCheckResult = false;
						printf(" check NG!\n");
						printf("->CrcInFile:%08X\n", datastream.ulCrcInFile);
						printf("->CrcByCalc:%08X\n", ulCrcByCalc);
					}
				} else {
					printf( "fread NG:%s[%d]\n", "CrcInFile", __LINE__);
					return false;
				}
				//CrcInFile_end
			} else {
				printf( "fread NG:%s[%d]\n", "ChunkTypeAndChunkData", __LINE__);
				return false;
			}
			//ChunkTypeAndChunkData_end
		} else {
			printf( "fread NG:%s[%d]\n", "ChunkDataLength", __LINE__);
			return false;
		}
		//ChunkDataLength_end

	} while (!(
		datastream.ucChunkTypeAndChunkData[0] == 0x49 && // I
		datastream.ucChunkTypeAndChunkData[1] == 0x45 && // E
		datastream.ucChunkTypeAndChunkData[2] == 0x4E && // N
		datastream.ucChunkTypeAndChunkData[3] == 0x44)); // D

	// Close stream if it is not NULL
	if( filestream ) {
		if ( !fclose( filestream ) ) {
#ifdef _DEBUG
			printf( "close OK[%d]\n", __LINE__);
#endif
		} else {
			printf( "close NG(close failed)[%d]\n", __LINE__);
			return false;
		}
	} else {
		printf( "close NG(file pointer is null)[%d]\n", __LINE__);
		return false;
	}

	if( bCrcCheckResult ) {
		printf( "Everything OK!\n", __LINE__);
	} else {
		printf( "Something  NG!\n", __LINE__);
	}
	return 0;
}