package dan2097.org.bitbucket.uspto;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Nodes;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import dan2097.org.bitbucket.utility.StringUtils;
import dan2097.org.bitbucket.utility.Utils;

public class ExtractOrganicChemistryPatents {
	private static Logger LOG = Logger.getLogger(ExtractOrganicChemistryPatents.class);
	
	static{
		LOG.setLevel(Level.DEBUG);
	}
	
	private final File inputDirectory;
	private final File outputDirectory;
	
	/**
	 * An input directory in which zip/tar files from
	 * http://www.google.com/googlebooks/uspto-patents-applications-text-with-embedded-images.html
	 * are placed and an output location for appropriate patents
	 * @param inputDirectoryLocation
	 * @param outputDirectoryLocation
	 * @throws IOException 
	 */
	public ExtractOrganicChemistryPatents(String inputDirectoryLocation, String outputDirectoryLocation) throws IOException {
		this(new File(inputDirectoryLocation), new File(outputDirectoryLocation));
	}
	
	/**
	 * An input directory in which zip/tar files from
	 * http://www.google.com/googlebooks/uspto-patents-applications-text-with-embedded-images.html
	 * are placed and an output location for appropriate patents
	 * @param inputDirectory
	 * @param outputDirectory
	 * @throws IOException 
	 */
	public ExtractOrganicChemistryPatents(File inputDirectory, File outputDirectory) throws IOException {
		checkInputs(inputDirectory, outputDirectory);
		FileUtils.forceMkdir(outputDirectory);
		this.inputDirectory = inputDirectory;
		this.outputDirectory = outputDirectory;
	}

	public void extractOrganicPatents() throws IOException{
		Iterator<File> fileIterator = FileUtils.iterateFiles(inputDirectory, new String[]{"ZIP", "zip", "Zip", "TAR", "tar", "Tar"}, false);
		while (fileIterator.hasNext()) {
			processPatentArchiveFile((File) fileIterator.next());
		}
		File tempDirectory = new File(outputDirectory.getAbsolutePath() +"/temp");
		if(tempDirectory.exists()){
			FileUtils.forceDeleteOnExit(tempDirectory);
		}
	}
	private void processPatentArchiveFile(File patentArchiveFile) throws IOException {
		LOG.debug(patentArchiveFile.getAbsolutePath());
		File tempDirectory = new File(outputDirectory.getAbsolutePath() +"/temp/" + patentArchiveFile.getName());
		FileUtils.forceMkdir(tempDirectory);
		File archiveOutputDirectory = new File(outputDirectory.getAbsolutePath() +"/" + patentArchiveFile.getName());
		FileUtils.forceMkdir(archiveOutputDirectory);
		if (StringUtils.endsWithCaseInsensitive(patentArchiveFile.getName(), "zip")){
			extractCandidateZipFilesFromZipFile(patentArchiveFile, tempDirectory);
		}
		else if (StringUtils.endsWithCaseInsensitive(patentArchiveFile.getName(), "tar")){
			extractCandidateZipFilesFromTarFile(patentArchiveFile, tempDirectory);
		}
		else{
			throw new RuntimeException("Unexpected file extension: " + patentArchiveFile.getName());
		}
		processZipFiles(tempDirectory, archiveOutputDirectory);
		FileUtils.forceDeleteOnExit(tempDirectory);
	}

	/**
	 * Extracts all zip files present within the given zip file
	 * @param patentArchiveFile
	 * @param tempDirectory
	 * @throws IOException
	 */
	private void extractCandidateZipFilesFromZipFile(File patentArchiveFile, File tempDirectory) throws IOException {
		ZipFile zipFile = new ZipFile(patentArchiveFile);
		Enumeration<? extends ZipEntry> entries = zipFile.entries();
		while(entries.hasMoreElements()) {
			ZipEntry zipEntry = entries.nextElement();
			if(StringUtils.endsWithCaseInsensitive(zipEntry.getName(), "zip")){
				InputStream inputStream =  zipFile.getInputStream(zipEntry);
				File f = new File(tempDirectory +"/" + FilenameUtils.getName(zipEntry.getName()));
				FileOutputStream fos = new FileOutputStream(f);
				IOUtils.copy(inputStream, fos);
				IOUtils.closeQuietly(fos);
			}
		}
		zipFile.close();
	}
	
	/**
	 * Extracts all zip files present within the given tar file
	 * @param patentArchiveFile
	 * @param tempDirectory
	 * @throws IOException
	 */
	private void extractCandidateZipFilesFromTarFile(File patentArchiveFile, File tempDirectory) throws IOException {
		TarArchiveInputStream tin = new TarArchiveInputStream(new FileInputStream(patentArchiveFile));
		TarArchiveEntry tarEntry = tin.getNextTarEntry();
		while(tarEntry !=null){
			if(StringUtils.endsWithCaseInsensitive(tarEntry.getName(), "zip")){
				File f = new File(tempDirectory +"/" + FilenameUtils.getName(tarEntry.getName()));
				FileOutputStream fos = new FileOutputStream(f);
				IOUtils.copy(tin, fos);
				IOUtils.closeQuietly(fos);
			}
			tarEntry = tin.getNextTarEntry();
		}
	}

	private void processZipFiles(File inputDirectory, File archiveOutputDirectory) throws ZipException, IOException {
		Iterator<File> fileIterator = FileUtils.iterateFiles(inputDirectory, new String[]{"ZIP", "zip", "Zip"}, false);
		while (fileIterator.hasNext()) {
			File f = (File) fileIterator.next();
			ZipFile zipFile = new ZipFile(f);
			Enumeration<? extends ZipEntry> entries = zipFile.entries();
			while(entries.hasMoreElements()) {
				ZipEntry zipEntry = entries.nextElement();
				if(StringUtils.endsWithCaseInsensitive(zipEntry.getName(), FilenameUtils.getBaseName(zipFile.getName()) + ".XML")){
					InputStream inputStream =  zipFile.getInputStream(zipEntry);
					Document doc;
					try{
						doc = Utils.buildXmlFile(inputStream);
					}
					catch (Exception e) {
						LOG.fatal(f.getAbsolutePath());
						throw new RuntimeException("Failed to read document", e);
					}
					if (isOrganicChemistryDocument(doc)){
						FileUtils.copyFile(f, new File(archiveOutputDirectory +"/" +f.getName()));
					}
				}
			}
			zipFile.close();
		}
	}

	private boolean isOrganicChemistryDocument(Document doc) {
		Nodes classifications = doc.query("//classification-ipcr");
		for (int i = 0; i < classifications.size(); i++) {
			Element classification = (Element) classifications.get(i);
			Element section = classification.getFirstChildElement("section");
			if (section != null && section.getValue().equalsIgnoreCase("C")){
				Element claz = classification.getFirstChildElement("class");
				if (claz != null && (claz.getValue().equalsIgnoreCase("07") || claz.getValue().equalsIgnoreCase("7"))){
					return true;
				}
			}
		}
		return false;
	}

	private void checkInputs(File in, File out) {
		if (!in.exists()){
			throw new IllegalArgumentException("input directory does not exist");
		}
		if (!in.isDirectory()){
			throw new IllegalArgumentException("input directory is not a directory");
		}
		if (out.exists() && !out.isDirectory()){
			throw new IllegalArgumentException("output directory is not a directory");
		}
	}
	
	
	public static void main(String[] args) throws IOException {
		String inputDirectory  = "C:/Users/dl387/Desktop/newUSPTO/2011";
		String outputDirectory  = "C:/Users/dl387/Desktop/newUSPTOout/2011";
		ExtractOrganicChemistryPatents eocp= new ExtractOrganicChemistryPatents(inputDirectory, outputDirectory);
		eocp.extractOrganicPatents();
	}
}
