package megan.kegg.lib;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.zip.CRC32;

import megan.kegg.KeggViewerConstants;

// insert coin
public class PNGValidator 
{
	// high bit, P, N, G, crlf, eof, lf
	public static final Integer[] head_verify = {137, 80, 78, 71, 13, 10, 26, 10};

	// the PNG game... returns true only if f is full of win
	public static boolean validate(File f)
	{
		// level 1: this should be easy :)
		if (!f.exists() || !f.canRead())
		{
			return false;
		}
		
		try 
		{
			FileInputStream fis = new FileInputStream(f);
			
			byte[] header = new byte[8];
			fis.read(header);
			
			// level 2: comparing headers
			for (int i = 0; i < 8; i++)
			{
				if (header[i] != head_verify[i].byteValue())
				{
					return false;
				}
			}
			
			byte[] length = new byte[4];
			byte[] type = new byte[4]; 
			byte[] data;
			byte[] crc = new byte[4];
			long lcrc;
			
			// level 3: CRC checks
			while (fis.available() > 0)
			{
				fis.read(length);
				fis.read(type);
				
				data = new byte[b2i(length)];
				
				fis.read(data);
				fis.read(crc);
				
				lcrc = b2i(crc) & 0x00000000ffffffffL;
				
				CRC32 crc32 = new CRC32();
				crc32.update(type);
				crc32.update(data);
				
				if (KeggViewerConstants.VERBOSE)
				{
					String t = new String(type, "UTF8");
					System.out.println("[VERBOSE] PNG Validator:" + t + ": " + fis.available());
				}
				
				if (lcrc != crc32.getValue())
				{
					return false;
				}
			}
			
			// level 4: final boss
			String t = new String(type, "UTF8");
			if (fis.available() != 0 && t.equals("IEND"))
			{
				return false;
			}
		} 
		catch (FileNotFoundException e) 
		{
			// makes lines 20-24 redundant
			return false;
		} 
		catch (IOException e) 
		{
			// game over... insert coin to try again
			return false;
		}
		
		// a valid PNG image should reach this return :)
		return true;							
	}
	
	private static int b2i(byte[] b)
	{
		return b[0] << 24 | (b[1] & 0xff) << 16 | (b[2] & 0xff) << 8 | (b[3] & 0xff);
	}
}

