/*************************************************************************************

This file is part of FragMend.

Written by Florian Buchholz, Glenn Henderson, David Horvath, and Jeff Jones.

Copyright (c) 2006, Florian Buchholz, Glenn Henderson, David Horvath, and Jeff Jones.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.     
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.       
    * Neither the name of Florian Buchholz, Glenn Henderson, David Horvath, Jeff
      Jones, nor the names of its contributors may be used to endorse or promote
      products derived from this software without specific prior written
      permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*************************************************************************************/

import java.io.File;
import java.io.InputStream;
import java.io.IOException;

import java.util.Enumeration;

import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

public class ZIPUtilities {

	public static boolean containsHeader(byte[] data) {
	
		return ((data[0] == 0x50) &&
			(data[1] == 0x4B) &&
			(data[2] == 0x03) &&
			(data[3] == 0x04));
	
	}

	public static boolean containsFooter(byte[] data) {
	
		for (int i = 0; i < data.length-3; i++) {
			if ((data[i] == 0x50) &&
				(data[i+1] == 0x4B) &&
				(data[i+2] == 0x05) &&
				(data[i+3] == 0x06))
				return true;
		}
		
		return false;
		
	}
	
	public static int getSize(byte[] data) {
		
		int pos;
		boolean found = false;
				
		pos = 0;
				
		while (pos <= data.length-30) {
			
			// pos should point at one of the following here: 
			//	a local file header
			//	central directory
			
			// break out of this loop if we find central directory
			
			if ((data[pos] == 0x50) &&
			(data[pos+1] == 0x4B) &&
			(data[pos+2] == 0x01) &&
			(data[pos+3] == 0x02)) {
				found = true;
				break;
			}
			
			if (!((data[pos] == 0x50) &&
			(data[pos+1] == 0x4B) &&
			(data[pos+2] == 0x03) &&
			(data[pos+3] == 0x04))) {
				System.err.println("Local file header expected at byte " + pos);
				return -1;
			}
			
			// determine if there is a data descriptor at the end of the data
			
			System.out.println("Found local file header: " + pos);
			
			boolean data_descriptor = (((data[pos+6]&0xff) & 0x8) > 0);
			
			System.out.println("Descriptor field: " + data[pos+6] + "  " + data_descriptor);
			
			long data_size;
			
			if (data_descriptor)
				data_size = 0;
			else
				data_size = DataUtilities.getWordLittle(data, pos+18);
			
			// determine where file data starts
			
			int name_length = DataUtilities.getShortLittle(data, pos+26);
			int extra_length = DataUtilities.getShortLittle(data, pos+28);
			
			System.out.println("Name length: " + name_length);
			System.out.println("Name: " + new String(data, pos+30, name_length));
			System.out.println("Extra field length: " + extra_length);
			
			pos += 30 + name_length + extra_length;
			
			// we are now at start of data, advance until we find a header
			
			int data_start = pos;
			System.out.println("Data starts at byte " + data_start);
			
			long file_size = 0;
			
			while (pos < data.length-3) {
				if (((data[pos] == 0x50) && (data[pos+1] == 0x4B)) &&
					(((data[pos+2] == 0x03) && (data[pos+3] == 0x04)) ||
					 ((data[pos+2] == 0x01) && (data[pos+3] == 0x02)) ||
					 ((data[pos+2] == 0x07) && (data[pos+3] == 0x08))))
					 
					break;
				file_size++;
				pos++;
			}
			
			if (pos >= data.length-5) {
				System.err.println("Premature end of zip file");
				return -1;
			}
			
			if ((data[pos+2] == 0x07) && (data[pos+3] == 0x08)) {
			
				System.out.println("Found span header at byte " + pos);
				
				// data descriptor follows span header
				pos += 4;
				if (pos >= data.length-12) {
					System.err.println("Premature end of zip file");
					return -1;
				}
				
				if (data_descriptor) {
					data_size = DataUtilities.getWordLittle(data, pos+4);
					pos += 12;
				}
				
			}
			else {
				
				// data descriptor precedes this header
				
				if (data_descriptor) {
					data_size = DataUtilities.getWordLittle(data, pos-8);
					file_size -= 12;
				}
			}
						
			System.out.println("Counted size: " + file_size + " reported size: " + data_size);
			if (file_size != data_size) {
				System.out.println("File size differs from reported size");
				return -1;
			}
			
		}
		
		if (!found) {
			System.err.println("No central directory found");
			return -1;
		}
		
		System.out.println("Central directory starts at byte " + pos);
		
		// TODO: parse central directory, compare with local file headers
		
		found = false;
		
		while (pos < data.length-3) {
					
			if ((data[pos] == 0x50) &&
				(data[pos+1] == 0x4B) &&
				(data[pos+2] == 0x05) &&
				(data[pos+3] == 0x06)) {
					found = true;
					break;
			}
			
			pos++;
		}
		
		if (!found) {
			System.err.println("End of central directory not found");
			return -1;
		}
		
		pos += 20;
		if (pos >= data.length-1) {
			System.err.println("Zip footer too short");
			return -1;
		}
		
		int comment_size = DataUtilities.getShortLittle(data, pos);
					
		System.out.println("Comment size: " + comment_size);
		
		pos += 2 + comment_size;
		
		if (pos >= data.length) {
			System.err.println("Zip footer too short");
			return -1;
		}

		return pos;
		
	}

/*	
	public static boolean verify(String filename) {
		
		ZipFile zf;
		
		try {
			File f = new File(filename);
			if (!f.exists())
				System.err.println("File " + filename + " doesn't exist");
			zf = new ZipFile(f);
		}
		catch (IOException ioe) {
			System.err.println(ioe);
			ioe.printStackTrace();
			return false;
		}
		
		for (Enumeration<? extends ZipEntry> ze = zf.entries(); ze.hasMoreElements();) {
			ZipEntry entry = ze.nextElement();
			System.out.println(entry.getName());
			try {
				InputStream is = zf.getInputStream(entry);
			}
			catch (ZipException zie) {
				System.err.println(zie);
//				zf.close();
				return false;
			}
			catch (IOException ioe) {
//				zf.close();
				System.err.println(ioe);
				return false;
			}
		}
		
//		zf.close();
		
		return true;
		
	}
*/	

}

