package com.webobjects.foundation;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.jar.*;

public class NSPathUtilities {
	public static final Class _CLASS = _NSUtilities._classWithFullySpecifiedName("com.webobjects.foundation.NSPathUtilities");

	public static String _fileURLPrefix = File.pathSeparatorChar == ';' ? "file:///" : "file://";

	private NSPathUtilities() {
		throw new IllegalStateException("Can't instantiate an instance of class " + getClass().getName());
	}

	public static String homeDirectory() {
		return System.getProperty("user.home");
	}

	public static String _fileSeparatorStandardizedPath(String path) {
		if (path == null) {
			return "";
		}

		return File.separatorChar != '/' ? path.replace(File.separatorChar, '/') : path;
	}

	public static String _normalizedPath(String path) {
		if (path == null) {
			return "";
		}

		return File.separatorChar != '/' ? path.replace('/', File.separatorChar) : path;
	}

	public static String _standardizedPath(String path) {
		if (path != null) {
			String aPath = path;
			int pathLength = aPath.length();
			if (pathLength > 0) {
				aPath = _fileSeparatorStandardizedPath(aPath);

				int index = aPath.indexOf("//");
				while (index >= 0) {
					if (index == pathLength - 1) {
						aPath = aPath.substring(0, index);
						break;
					}
					aPath = aPath.substring(0, index).concat(aPath.substring(index + 1));

					index = aPath.indexOf("//");
				}
				return aPath;
			}
		}
		return "";
	}

	public static String pathExtension(String path) {
		if (path != null) {
			int pathLength = path.length();
			if (pathLength > 0) {
				String standardizedPath = _fileSeparatorStandardizedPath(path);
				int extensionIndex = standardizedPath.lastIndexOf('.');

				if (extensionIndex >= 0) {
					int separatorIndex = standardizedPath.lastIndexOf('/');
					int lastIndex = pathLength - 1;
					int substringIndex = pathLength;
					if (separatorIndex == lastIndex) {
						separatorIndex = standardizedPath.lastIndexOf('/', lastIndex - 1);

						substringIndex = lastIndex;
					}

					if (((separatorIndex < 0) || (extensionIndex > separatorIndex)) && (extensionIndex < pathLength - 1)) {
						return standardizedPath.substring(extensionIndex + 1, substringIndex);
					}
				}
			}
		}

		return "";
	}

	public static String lastPathComponent(String path) {
		String pathComponent = path;

		if (path == null) {
			return "";
		}

		int pathLength = path.length();
		if (pathLength == 0) {
			return "";
		}

		String standardizedPath = _fileSeparatorStandardizedPath(path);
		int separatorIndex = standardizedPath.lastIndexOf('/');
		int lastIndex = pathLength - 1;

		if (separatorIndex == lastIndex) {
			separatorIndex = standardizedPath.lastIndexOf('/', lastIndex - 1);
			if ((separatorIndex >= 0) && (separatorIndex < lastIndex)) {
				pathComponent = path.substring(separatorIndex + 1, lastIndex);
			} else {
				pathComponent = path.substring(0, lastIndex);
			}
		} else if ((separatorIndex >= 0) && (separatorIndex < lastIndex)) {
			pathComponent = path.substring(separatorIndex + 1);
		}

		if ((path.startsWith(pathComponent)) && (pathComponent.length() == 2) && (Character.isLetter(pathComponent.charAt(0))) && (pathComponent.endsWith(":")) && (File.separatorChar == '\\') && (File.pathSeparatorChar == ';')) {
			pathComponent = "";
		}

		return pathComponent;
	}

	public static String stringByDeletingLastPathComponent(String path) {
		if (path != null) {
			int pathLength = path.length();
			if (pathLength > 0) {
				String standardizedPath = _fileSeparatorStandardizedPath(path);
				int separatorIndex = standardizedPath.lastIndexOf('/');
				int firstSeparatorIndex = 0;
				int lastIndex = pathLength - 1;

				if (Character.isLetter(standardizedPath.charAt(0))) {
					if ((standardizedPath.indexOf(":/") == 1) || (standardizedPath.indexOf(":\\") == 1)) {

						firstSeparatorIndex = 2;

						if (separatorIndex == -1) {
							separatorIndex = standardizedPath.lastIndexOf('\\');
						}
					} else if (standardizedPath.indexOf(":") == 1) {
						separatorIndex = firstSeparatorIndex = 1;
					}
				}

				if ((separatorIndex > -1) && (separatorIndex == firstSeparatorIndex))
					return path.substring(0, firstSeparatorIndex + 1);
				if (separatorIndex == lastIndex) {
					separatorIndex = standardizedPath.lastIndexOf('/', lastIndex - 1);
				}

				if ((separatorIndex > -1) && (separatorIndex == firstSeparatorIndex))
					return path.substring(0, firstSeparatorIndex + 1);
				if (separatorIndex > 0) {
					return path.substring(0, separatorIndex);
				}
			}
		}
		return "";
	}

	public static String stringByDeletingPathExtension(String path) {
		String standardizedPath = _fileSeparatorStandardizedPath(path);

		int length = standardizedPath.length();
		int pos = length - 1;

		while ((pos >= 0) && (standardizedPath.charAt(pos) == '/')) {
			pos--;
		}
		if (pos == -1) {
			if (length == 0) {
				return "";
			}

			return File.separator;
		}
		int lastSlash = pos;

		while ((pos >= 0) && (standardizedPath.charAt(pos) != '.'))
			pos--;
		String result;

		if (pos == -1) {
			result = path.substring(0, lastSlash + 1);
		} else {
			result = path.substring(0, pos);
		}
		return result;
	}

	/**
	 * BUG IN THIS METHOD, Jars paths end with "/" not the OS Default
	 * Need to figure out how to determine if jar path and where to make the switch
	 * 
	 * @param path
	 * @param component
	 * @return
	 */
	public static String stringByAppendingPathComponent(String path, String component) {
		if (path == null)
			return component == null ? "" : component;
		if (component == null) {
			return path;
		}
		int pathLength = path.length();
		int componentLength = component.length();
		if (pathLength == 0)
			return component;
		if (componentLength == 0) {
			return path;
		}

		boolean pathEndsWithFileSeparator = path.endsWith(File.separator) || (path.startsWith("jar:") && path.endsWith("/"));
		boolean componentStartsWithFileSeparator = component.startsWith(File.separator);
		if ((pathEndsWithFileSeparator) && (componentStartsWithFileSeparator)) {
			StringBuffer buffer = new StringBuffer(pathLength + componentLength - 1);
			buffer.append(path.substring(0, pathLength - 1));
			buffer.append(component);
			return new String(buffer);
		}
		if ((pathEndsWithFileSeparator) || (componentStartsWithFileSeparator)) {
			StringBuffer buffer = new StringBuffer(pathLength + componentLength);
			buffer.append(path);
			buffer.append(component);
			return new String(buffer);
		}

		StringBuffer buffer = new StringBuffer(pathLength + componentLength + 1);
		buffer.append(path);
		buffer.append(path.startsWith("jar:") ? "/" : File.separator);
		buffer.append(component);
		return new String(buffer);
	}

	public static String stringByAppendingPathExtension(String path, String extension) {
		if (path == null) {
			if (extension != null) {
				StringBuffer buffer = new StringBuffer(extension.length() + 1);
				buffer.append('.');
				buffer.append(extension);
				return new String(buffer);
			}
			return "";
		}
		if (extension == null) {
			return path;
		}

		int pathLength = path.length();
		if ((path.endsWith("/")) && (pathLength > 1)) {
			StringBuffer buffer = new StringBuffer(pathLength + extension.length());
			buffer.append(path.substring(0, pathLength - 1));
			buffer.append('.');
			buffer.append(extension);
			return new String(buffer);
		}

		StringBuffer buffer = new StringBuffer(pathLength + extension.length() + 1);
		buffer.append(path);
		buffer.append('.');
		buffer.append(extension);
		return new String(buffer);
	}

	public static String stringByNormalizingExistingPath(String path) {
		if (path != null) {
			File f = new File(path);

			if (f.exists()) {
				try {
					return f.getCanonicalPath();
				} catch (IOException e) {
					if (NSLog.debugLoggingAllowedForLevelAndGroups(2, 8192L)) {
						NSLog.debug.appendln("Exception while getting canonical path: " + path);
						NSLog.debug.appendln(e);
					}
				}
			}
		}
		return "";
	}

	@Deprecated
	public static String stringByStandardizingPath(String path) {
		return _stringByStandardizingPath(path);
	}

	public static String _stringByStandardizingPath(String path) {
		if (path == null) {
			return "";
		}

		boolean destandardizePath = path.indexOf(File.separatorChar) >= 0;
		String aPath = _standardizedPath(path);
		int pathLength = aPath.length();

		if (aPath.startsWith("~")) {
			String homeDirectory = homeDirectory();
			aPath = homeDirectory + aPath.substring(1);
		}

		if ((aPath.endsWith("/")) && (pathLength > 1)) {
			aPath = aPath.substring(0, --pathLength);
		}

		int searchStartIndex = 0;
		int index1 = aPath.indexOf("..", searchStartIndex);
		while (index1 >= 0) {
			if (index1 == 0)
				throw new IllegalArgumentException("<NSPathUtilities> Unable to resolve path starting with ..");
			if (aPath.charAt(index1 - 1) == '/') {
				int index2 = aPath.lastIndexOf('/', index1 - 2);
				if (index1 + 2 >= pathLength) {
					if (index2 < 0) {
						aPath = "";
					} else if (index2 == 0) {
						aPath = "/";
					} else {
						aPath = aPath.substring(0, index2);
					}
				} else if (aPath.charAt(index1 + 2) == '/') {
					if (index2 < 0) {
						aPath = aPath.substring(index1 + 3);
					} else if (index2 == 0) {
						aPath = aPath.substring(index1 + 2);
					} else {
						aPath = aPath.substring(0, index2 + 1) + aPath.substring(index1 + 3);
					}
				} else {
					searchStartIndex = index1 + 2;
				}
			} else {
				searchStartIndex = index1 + 2;
			}

			index1 = aPath.indexOf("..", searchStartIndex);
			pathLength = aPath.length();
		}

		if (destandardizePath) {
			aPath = aPath.replace('/', File.separatorChar);
		}
		return aPath;
	}

	@Deprecated
	public static boolean pathIsEqualToString(String path1, String path2) {
		if (path1 == path2) {
			return true;
		}
		if ((path1 == null) || (path2 == null)) {
			return false;
		}

		return new File(path1).equals(new File(path2));
	}

	@Deprecated
	public static boolean pathIsAbsolute(String path) {
		return path != null ? new File(path).isAbsolute() : false;
	}

	@Deprecated
	public static boolean fileExistsAtPath(String path) {
		return path != null ? new File(path).exists() : false;
	}

	public static boolean fileExistsAtPathURL(URL pathURL) {
		if (pathURL == null)
			return false;
		boolean result = false;
		if (_isJarProtocol(pathURL)) {
			try {
				result = ((JarURLConnection) pathURL.openConnection()).getJarEntry() != null;

			} catch (Exception exception) {
			}
		} else if (_isFileProtocol(pathURL)) {
			try {
				result = new File(pathURL.getPath()).exists();
			} catch (Exception exception) {
			}
		}

		return result;
	}

	public static boolean _isFileProtocol(URL url) {
		return (url != null) && ("file".equals(url.getProtocol()));
	}

	public static boolean _isJarProtocol(URL url) {
		return (url != null) && ("jar".equals(url.getProtocol()));
	}

	public static long _contentLengthForPathURL(URL url) {
		long contentLength = -1L;
		if (url != null) {
			if (_isJarProtocol(url)) {
				try {
					JarEntry entry = ((JarURLConnection) url.openConnection()).getJarEntry();
					if ((entry != null) && (!entry.isDirectory())) {
						contentLength = entry.getSize();
					}
				} catch (Exception exception) {
				}
			} else if (_isFileProtocol(url)) {
				try {
					File file = new File(url.getFile());
					if ((file.exists()) && (!file.isDirectory())) {
						contentLength = file.length();
					}
				} catch (Exception exception) {
				}
			}
		}
		return contentLength;
	}

	public static long _lastModifiedForPathURL(URL url) {
		long lastModified = 0L;
		if ((url != null) && (_isFileProtocol(url))) {
			File file = new File(url.getPath());
			lastModified = file.lastModified();
		}

		return lastModified;
	}

	@Deprecated
	public static URL URLWithPath(String path) {
		return _URLWithPath(path);
	}

	public static URL _URLWithPath(String path) {
		URL url = null;
		if (path == null) {
			return null;
		}
		try {
			url = new URL(_fileURLPrefix.concat(stringByNormalizingPath(path)));
		} catch (MalformedURLException e) {
		}

		return url;
	}

	public static String stringByNormalizingPath(String path) {
		if (path != null) {
			File f = new File(path);
			try {
				return f.getCanonicalPath();
			} catch (IOException e) {
				if (NSLog.debugLoggingAllowedForLevelAndGroups(2, 8192L)) {
					NSLog.debug.appendln("Exception while getting canonical path: " + path);
					NSLog.debug.appendln(e);
				}
			}
		}
		return "";
	}

	public static URL _URLWithPathURL(String path) {
		URL url = null;
		if (path == null)
			return null;
		try {
			url = new URL(path);
		} catch (MalformedURLException e) {
		}

		if (url == null) {
			url = _URLWithPath(path);
		}
		return url;
	}

	public static URL _URLWithFile(File aFile) {
		URL url = null;
		if (aFile != null) {
			try {
				url = new URL(aFile.getCanonicalPath());
			} catch (Exception e) {
			}
		}

		return url;
	}

	public static File _FileWithURL(URL url) {
		File file = null;
		if (_isFileProtocol(url)) {
			file = new File(url.getPath());
		}
		return file;
	}

	public static NSArray<String> _directoryContentsAtPath(String path) {
		if (path == null) {
			return NSArray.emptyArray();
		}
		return new NSArray(new File(path).list());
	}

	public static boolean _isDirectory(String path) {
		return path != null ? new File(path).isDirectory() : false;
	}

	public static boolean _isDirectoryAtPathURL(URL url) {
		if (url == null)
			return false;
		boolean result = false;
		try {
			if (_isJarProtocol(url)) {
				String path = url.getPath();
				URLConnection aConnection = url.openConnection();
				if ((aConnection instanceof JarURLConnection)) {
					JarFile archive = ((JarURLConnection) aConnection).getJarFile();
					int index = path.indexOf("!/");
					if (index > 0) {
						String fileName = path.substring(index + 2);
						if (!fileName.endsWith("/"))
							fileName = fileName + "/";
						result = archive.getEntry(fileName) != null;
					}
				}
			} else if (_isFileProtocol(url)) {
				result = new File(url.getPath()).isDirectory();
			} else {
				result = url.getPath().endsWith("/");
			}
		} catch (Exception exception) {
		}

		return result;
	}

	public static void _removeFileAtPath(String path) {
		if (path == null) {
			return;
		}
		if ((fileExistsAtPath(path)) && (_isDirectory(path))) {
			NSArray content = _directoryContentsAtPath(path);
			Enumeration enumerator = null;
			if (content != null) {
				enumerator = content.objectEnumerator();
				while (enumerator.hasMoreElements()) {
					_removeFileAtPath(path + File.separator + (String) enumerator.nextElement());
				}
			}
		}
		File file = new File(path);
		file.delete();
	}

	public static void _movePath(String src, String dest) {
		if ((src == null) || (dest == null)) {
			return;
		}
		File fileDest = new File(dest);
		File fileSrc = new File(src);
		fileSrc.renameTo(fileDest);
	}

	public static boolean _overwriteFileWithFile(File originalFile, File newFile) {
		File parent = originalFile.getParentFile();
		if (parent != null) {
			File backup = null;
			try {
				if (originalFile.exists()) {
					backup = File.createTempFile("backup", "tmp", parent);
					backup.delete();
				}
			} catch (IOException ioe) {
				NSLog.err.appendln("Failed to create backup file in directory " + parent);
				return false;
			}
			String originalPath = originalFile.getAbsolutePath();
			if ((backup != null) && (!originalFile.renameTo(backup))) {
				NSLog.err.appendln("Failed to rename " + originalFile + " to " + backup);
				return false;
			}
			if (newFile.renameTo(new File(originalPath))) {
				if (backup != null)
					backup.delete();
				return true;
			}

			if ((backup != null) && (!originalFile.renameTo(new File(originalPath)))) {
				throw new IllegalStateException("Tried to move " + newFile + " on to " + originalFile + " but failed. Attempts at restoring the original conditions have failed. The original file is at " + backup);
			}
		}

		return false;
	}

	public static boolean _createDirectory(String path) {
		if (path == null) {
			return false;
		}
		return new File(path).mkdirs();
	}

	public static String _currentDirectoryPath() {
		return System.getProperty("user.dir");
	}

	@Deprecated
	public static boolean _fileAtPathIsWritable(String path) {
		return path != null ? new File(path).canWrite() : false;
	}

	public static boolean _fileAtPathURLIsWritable(URL url) {
		if (url == null)
			return false;
		boolean result = false;
		if (!_isJarProtocol(url)) {
			if (_isFileProtocol(url)) {
				try {
					result = new File(url.getPath()).canWrite();
				} catch (Exception exception) {
				}
			}
		}
		return result;
	}

	@SuppressWarnings("unused")
	public static void _copyPath(String source, String dest, Object sender) {
		if (source.equals(dest)) {
			return;
		}

		FileInputStream in = null;
		FileOutputStream out = null;
				
		try {
			in = new FileInputStream(source);
			out = new FileOutputStream(dest);
			byte[] buffer = new byte[in.available()];
			int chunk = in.read(buffer);
			while (chunk >= 0) {
				out.write(buffer, 0, chunk);
				chunk = in.read(buffer);
			}
		} catch (IOException e) {
			if (NSLog.debugLoggingAllowedForLevelAndGroups(2, 8192L)) {
				NSLog.debug.appendln("Exception while copying path " + source + " to path " + dest);
				NSLog.debug.appendln(e);
			}
		} finally {
			if(null != in) try { in.close(); }  catch (Exception e) {}
			if(null != out) try { out.close(); }  catch (Exception e) {}			
		}
	}
}
