import gov.noaa.ncdc.publications.exception.ConvertException;
import gov.noaa.ncdc.publications.exception.CreatePDFFileException;
import gov.noaa.ncdc.publications.exception.CreateTempWorkingDirException;
import gov.noaa.ncdc.publications.exception.FTPServerException;
import gov.noaa.ncdc.publications.exception.FTPServerTimeoutException;
import gov.noaa.ncdc.publications.exception.createListFileException;
import gov.noaa.ncdc.util.FileUtils;
import gov.noaa.ncdc.util.Registry;
import gov.noaa.ncdc.util.WssrdFileComparator;
import gov.noaa.ncdc.util.ftpServer.FtpServer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
/**
 * <p>
 * <CODE>RemotePublication</CODE> RemotePublication is a publication
 * stored as TIF files in the WSSRD database.
 * </p>
 * <pre>
 * Revision 1.1  2003/10/15 13:11:18  othomann
 * Fix for 44839
 *
 * Revision 1.6  2003/10/09 22:07:28  jduska
 * Merged Chaged from 1.3 Branch into  Head
 *
 * Revision 1.5.2.2  2003/10/01 22:48:03  jduska
 * Added support, so that the publication can be retrieved by WSSRD cabinet 
 * and WSSRD docID
 *
 * Revision 1.5.2.1  2003/09/17 18:20:48  jduska
 * Change FTP Errors to Warnings
 *
 * Revision 1.5  2003/07/25 20:49:52  jduska
 * - Updated the Publication Library to use a new renameFile method 
 * - Created the new FileUtils class by moving the copyFile and adding the renameFile
 * methods
 *
 * Revision 1.4  2003/07/17 23:56:58  jduska
 * - Updated to support new support files in the command line tool	
 * - Update the properties to support the dynamic changing the FTP library
 * - Cleaned up the exceptions and few other files with outdate or unneed imports and etc
 * 
 * Revision 1.3  2003/07/07 15:42:48  jduska
 * - Corrected bugs in new changes to support the Publication Library
 * 
 * Revision 1.2  2003/07/02 17:33:14  jduska
 * - bug fix to correct the Temporary Directory
 *   issue when using the library within a web application
 * 
 * Revision 1.1  2003/06/26 23:54:10  jduska
 * - Command Line version of the Publication Library
 * 
 * Revision 1.3  2003/06/26 23:43:21  jduska
 * - Changes to support using the PDF Library on the command line
 * 
 * Revision 1.1  2003/06/09 21:27:29  jduska
 * - Updated the library, so works on Windows
 * 
 * Revision 1.1  2003/01/24 18:43:58  jduska
 * More refactoring of the SerialPublications to
 * support the local documents
 * 
 * Revision 1.3  2002/10/11 21:03:50  jduska
 *  keyword substitution change ***
 * 
 * </pre>
 */
public class RemotePublication extends Publication {
	private static final String PDF_FILETYPE = "PDF";
	private static Log log = LogFactory.getLog(RemotePublication.class
			.getName());
	private String _fileList;
	private String _fileType;
	protected String _pages[];
	protected RemotePublication(String id, String year, String month,
			String stateCode, String serverDirectory, String cabinet) {
		super(id, year, month, stateCode, serverDirectory, cabinet);
		_fileList = _cabinet + '~' + id + ".txt";
	}
	public boolean getDocument() {
		int attempt = 0;
		while (attempt < _maxAttempts) {
			try {
				attempt++;
				log.info("*** Downloading " + _localFilename + EXTENTION
						+ " attempt " + attempt + " out of " + _maxAttempts);
				if (getRemoteDocument()) {
					log.info("Success document published " + _localFilename
							+ EXTENTION);
					return true;
				}
			} catch (ConvertException e) {
				log.warn(e);
			}
		}
		return false;
	}
	protected boolean getRemoteDocument() throws ConvertException {
		boolean successful = false;
		try {
			createTempWorkingDir();
			getPages();
			if (_fileType != PDF_FILETYPE) {
				createListFile();
				createPDFFile();
			} else {
				renamePDFFile();
			}
			successful = true;
		} catch (FTPServerTimeoutException e) {
			String msg = "Convert process timed out retriving TIFF page"
					+ e.getFileName();
			log.warn(msg);
			throw new ConvertException(msg);
		} catch (FTPServerException e) {
			String msg = "Convert process encounter an " + e.getMessage();
			log.warn(msg);
			throw new ConvertException(msg);
		} catch (CreateTempWorkingDirException e) {
			String msg = "Error: Covert could not create a Working Directory!";
			log.warn(msg);
			throw new ConvertException(msg);
		} catch (CreatePDFFileException e) {
			String msg = "Could not create the PDF file  " + e.getMessage();
			log.warn(msg);
			throw new ConvertException(msg);
		} catch (createListFileException e) {
			String msg = "Could not create the c42pdf List File"
					+ e.getMessage();
			throw new ConvertException(msg);
		} catch (NoFTPServiceException e) {
			String msg = "Could not create the FTP Service " + e.getMessage();
			log.warn(msg);
			throw new ConvertException(msg);
		}
		return successful;
	}
	protected void getPages() throws FTPServerTimeoutException,
			FTPServerException, NoFTPServiceException {
		// Setup the ftp server
		FtpServer ftp;
		try {
			ftp = (FtpServer) Class.forName(Registry.getFTPService())
					.newInstance();
			ftp.setServer(Registry.getUrl());
			ftp.setPassword(Registry.getFtpPassword());
			ftp.setUserName(Registry.getFtpUserName());
			ftp.setPassive(Registry.usePassive());
			ftp.setTimeout(Registry.getFtpTimeout());
			// get the pages and store them in the _workingDir
			_pages = ftp.getFiles(getWssrdDocumentName(), _workingDir);
			if ((_pages.length == 1) && _pages[0].endsWith(".PDF")) {
				_fileType = PDF_FILETYPE;
			}
		} catch (InstantiationException e) {
			log.warn(e.getMessage());
			throw new NoFTPServiceException();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	protected void createPDFFile() throws CreatePDFFileException {
		String cmdLine = null;
		try {
			File newPdfFile = new File(_workingDir + File.separator
					+ _localFilename + EXTENTION);
			if (Registry.isCommandLineMode()) {
				handleMultipleDocuments(newPdfFile);
			}
			cmdLine = createC42PDFCommandLine(newPdfFile.getName());
			if (log.isDebugEnabled()) {
				log.debug("Running " + cmdLine);
			}
			// run C42PDF to create the file & wait till it finishes.
			Process p = Runtime.getRuntime().exec(cmdLine);
			p.waitFor();
			// check to see if it worked
			if (!newPdfFile.exists()) {
				log.warn("PDF File was not created!");
				throw new CreatePDFFileException("PDF File was not created!");
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new CreatePDFFileException(e.toString());
		} catch (InterruptedException e) {
			e.printStackTrace();
			throw new CreatePDFFileException(e.toString());
		}
		return;
	}
	/**
	 * generates the command line need to execute the C42PDF tool in
	 * a shell. This requires that the
	 * <code>Registry.getConvertToolPath()</code> and
	 * <code>Registry.getConvertToolName()</code> are  valid.
	 *
	 * @param documentName the name of the document you wish to
	 *        create
	 *
	 * @return
	 */
	private String createC42PDFCommandLine(String documentName) {
		return (Registry.getConvertToolPath() + File.separator
				+ Registry.getConvertToolName() + (" -o " + _workingDir
				+ File.separator + documentName + " -l " + _workingDir
				+ File.separator + _fileList));
	}
	/**
	 * there a special cases within WSSRD where there may be more
	 * than one PDF that makes up the document. For example River
	 * Basin documents. Thus, it is possible for the same document
	 * already exist when using this libarary via  a command line
	 * mode. <code>handleMultipleDocuments</code> will add a index
	 * counter to end of each duplicate filename. Thus, if there are
	 * three files for this document the files would be name
	 * filename1, filename2 and filename3
	 *
	 * @param newPdfFile name of the document we are making sure does
	 *        already exist
	 */
	private void handleMultipleDocuments(File newPdfFile) {
		int documentCount = 0;
		String newFilename;
		if (log.isDebugEnabled()) {
			log.debug("Checking to see if " + newPdfFile.getName()
					+ "already exists");
		}
		while (newPdfFile.exists()) {
			documentCount++;
			newFilename = (new StringBuffer(_workingDir).append(File.separator)
					.append(_localFilename).append(documentCount)
					.append(EXTENTION)).toString();
			if (log.isDebugEnabled()) {
				log
						.debug("File already exists. Check to see if we need rename existing file");
			}
			if (documentCount == 1) {
				File backupPdfFile = new File(newFilename);
				newPdfFile.renameTo(backupPdfFile);
			}
			newPdfFile = new File(newFilename);
		}
		return;
	}
	protected void createListFile() throws createListFileException {
		PrintWriter tiffFileListFile = null;
		String s;
		// Must have pages to sort! 
		if (_pages.length == 0) {
			log.error("Internal Error: _tiffPage == null!");
			throw new createListFileException("No pages found in the document!");
		}
		// Sort TIFF files into the correct page order
		WssrdFileComparator fileComparator = new WssrdFileComparator();
		Arrays.sort(_pages, fileComparator);
		if (log.isDebugEnabled()) {
			log.debug("Creating list file for C42PDF");
		}
		try {
			File tiffFileList = new File(_workingDir + File.separator
					+ _fileList);
			if (tiffFileList.exists()) {
				tiffFileList.delete();
			}
			tiffFileListFile = new PrintWriter(new BufferedWriter(
					new FileWriter(_workingDir + File.separator + _fileList)));
			for (int i = 0; i < _pages.length; i++) {
				s = _workingDir + File.separator + _pages[i];
				if (log.isDebugEnabled()) {
					log.debug(s);
				}
				tiffFileListFile.println(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
			throw new createListFileException(e.toString());
		} finally {
			if (tiffFileListFile != null) {
				tiffFileListFile.close();
			}
			if (log.isDebugEnabled()) {
				log.debug("Finished Creating list file for C42PDF");
			}
			return;
		}
	}
	protected void renamePDFFile() throws CreatePDFFileException {
		String newFileName = _workingDir + File.separator + _localFilename
				+ EXTENTION;
		String oldWssrdFile = _workingDir + File.separator + _pages[0];
		try {
			FileUtils.renameFile(oldWssrdFile, newFileName);
		} catch (IOException e) {
			String msg = "The following error occurred " + e.getMessage();
			log.warn(msg);
			throw new CreatePDFFileException("Could not rename PDF File");
		}
	}
}