package com.ttv.acs.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.log4j.Logger;


/** A wrapper for the VFS FileSystemManager
 * 
 */
public class VirtualFileSystemManager {

	private final Logger log = Logger.getLogger(VirtualFileSystemManager.class);
	
	private FileSystemManager fileSystemManager = null;

	public VirtualFileSystemManager() {;}

	/** return a URI to the base URI if it is fully qualified, otherwise return a URI to the defaultPrefix + baseURI
	 * 
	 * @param defaultPrefix
	 * @param baseURI
	 * @return
	 */
	public URI 
	getURIWithDefaultPrefix(String defaultPrefix, String base)
	throws URISyntaxException {
		try {
			return new URI(base);
		} catch (URISyntaxException e) {
			return new URI(defaultPrefix + base);
		}
	}

	public FileSystemManager 
	getVFSManager() throws FileSystemException {
		if (fileSystemManager == null) {
			fileSystemManager = createManager("org.apache.commons.vfs.impl.StandardFileSystemManager");
		}
		return fileSystemManager;
	}
	
	public void 
	resetVFSManager() {
		fileSystemManager = null;
	}

	/**
	* Creates a file system manager instance.
	*/
	private static FileSystemManager 
	createManager(final String managerClassName)
	throws FileSystemException {
		
		try {
			// Create instance
			Class mgrClass = Class.forName(managerClassName);
			FileSystemManager mgr = (FileSystemManager) mgrClass.newInstance();

			try {
				// Initialize
				final Method initMethod = mgrClass.getMethod("init", new Class[0]);
				initMethod.invoke(mgr, new Object[0]);
			} catch (NoSuchMethodException ignore) {;}

			return mgr;
			
		} catch (InvocationTargetException e) {
			throw new FileSystemException("vfs/create-manager.error", managerClassName, e.getTargetException());
		} catch (Exception e) {
			throw new FileSystemException("vfs/create-manager.error", managerClassName, e);
		}
	}

	public InputStream 
	getInputStream(String uriString)
	throws FileSystemException {
		return resolveFile(uriString).getContent().getInputStream();
	}

	public OutputStream 
	getOutputStream(String uriString)
	throws FileSystemException {
		return resolveFile(uriString).getContent().getOutputStream();
	}

	public FileObject 
	resolveFile(String uriString)
	throws FileSystemException {
		return getVFSManager().resolveFile(uriString);
	}

	/** does a local directory exist?
	 * 
	 * @return true if the directory exists
	 */
	public boolean 
	localStorageDirectoryExists(String dirString)
	throws FileSystemException {
		FileObject dir = getVFSManager().resolveFile(dirString);
		return dir.exists(); //&& dir.isDirectory(); 
	}

	/** removes the directory and all the contents.
	 *  assumes that the dir has no subdirs.
	 * 
	 * @return true if the directory is deleted
	 */
	public void 
	killLocalDirectory(String dirString)
	throws FileSystemException {

		FileObject dir = getVFSManager().resolveFile(dirString);
		FileObject[] files = dir.getChildren();
		for (int i = 0; i < files.length; i++) {
			files[i].delete();
		}
		dir.delete();
	}

	/**copies the content fromURI to the toURI
	 * 
	 * @param fromURI
	 * @param toURI
	 * @throws FileSystemException
	 * @throws IOException
	 */
	public void 
	copyURI(URI fromURI, URI toURI)
	throws FileSystemException, IOException {

		InputStream instream = null;
		OutputStream outstream = null;

		log.info("copyURI from=" + fromURI.toString() + ";to=" + toURI);

		try {

			FileSystemManager fsManager = getVFSManager();
			FileObject fo_in = fsManager.resolveFile(fromURI.toString());

			outstream =
				fsManager
					.resolveFile(toURI.toString())
					.getContent()
					.getOutputStream();

			if (fo_in.getURL().getProtocol().equals("ftp")) {
				doFtp(fromURI, outstream);
			} else {
				instream = fo_in.getContent().getInputStream();

				int bytesRead = 0;
				byte[] buffer = new byte[4096];
				while ((bytesRead  = instream.read(buffer)) != -1) {
					if (bytesRead > 0) {
						outstream.write(buffer, 0, bytesRead);
					}
				}
			}

		} catch (FileSystemException e) {
			log.error(e);
			throw e;
		} catch (IOException e) {
			log.error(e);
			throw e;
		} finally {
			
			if (instream != null) {
				try {
					instream.close();
				} catch (IOException ioe) {
					log.error(ioe);
				}
			}
			
			if (outstream != null) {
				try {
					outstream.close();
				} catch (IOException ioe) {
					log.error(ioe);
				}
			}
		}
	}

	private void 
	doFtp(URI uri, OutputStream outstream) 
	throws IOException {

		//Extract the username & password from the userinfo portion of the URL
		String    userinfo  = uri.getUserInfo();
		String    username  = getUserName(userinfo);
		String    password  = getPassword(userinfo);
		FTPClient ftpClient = null;
		
		try {
			ftpClient = new FTPClient();

			ftpClient.setRemoteVerificationEnabled(false);
			ftpClient.connect(uri.getHost());
			
			if ( ! FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
				ftpClient.disconnect();
				log.error("FTP server refused connection.");
				throw new IOException("FTP server refused connection.");
			}
			
			if ( ! ftpClient.login(username, password)) {
				ftpClient.logout();
			}
			
			ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			ftpClient.enterLocalPassiveMode();
			String remotePath = uri.getPath();
			boolean isRetrievingSuccess = ftpClient.retrieveFile(remotePath, outstream);
			if(!isRetrievingSuccess){
				throw new IOException("Failed to retrieve file: "+ remotePath + " via FTP.");
			}
			
		} finally {

			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException ignore) {;}
			}
		}
	}

	static String 
	getUserName(String userinfo) {
		
		String username = null;
		String defaultusername = "anonymous";

		int colonindex = -1;

		if ((userinfo == null) || ((colonindex = userinfo.indexOf(':')) == -1)) {
			username = defaultusername;
		} else {
			username = userinfo.substring(0, colonindex);
		}

		return username;
	}

	static String
	getPassword(String userinfo) {
		
		String password = null;
		//String defaultpassword = "isa@n2bb.com";
		String defaultpassword = "ads123";
		int colonindex = -1;

		if ((userinfo == null)|| ((colonindex = userinfo.indexOf(':')) == -1)) {
			password = defaultpassword;
		} else {
			password = userinfo.substring(colonindex + 1, userinfo.length());
		}

		return password;
	}

	/** checks to see if the local directory exists, and creates it if it doesn't
	 * VFS file creation automagically creates the necessary directories, so this might be unnecessary.
	 * 
	 * @return true if the directory is good to go
	 */
	public boolean 
	localStorageDirectoryIsReady(String dirString) {

		try {
			FileObject dir = getVFSManager().resolveFile(dirString);

			log.debug("entering localStorageDirectoryIsReady for dir = " + dirString);

			if (dir.exists()) {
				log.debug("directory already exists " + 
						  dir.toString() + 
						  " for " + 
						  dirString);
			} else {
				try {
					dir.createFolder();
				} catch (Exception e) {
					log.error("failed to create storage directory " + 
							  dir.toString() + 
							  " for " + 
							  dirString);
					return false;
				}
			}
			
			return dir.isWriteable();
		} catch (FileSystemException e) {
			log.error("failed in localStorageDirectoryIsReady ", e);
			return false;
		} catch (Exception e) {
			log.error("failed in localStorageDirectoryIsReady ", e);
			return false;
		}
	}
}
