package org.bndtools.utils.workspace;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;

import org.bndtools.utils.osgi.BundleUtils;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;

import aQute.lib.io.IO;

public class FileUtils {
	public static IDocument readFully(IFile file) throws CoreException, IOException {
		if (file.exists()) {
			InputStream stream = file.getContents();
			byte[] bytes = readFully(stream);

			String string = new String(bytes, file.getCharset());
			return new Document(string);
		}
		return null;
	}

	public static void recurseCreate(IContainer container, IProgressMonitor monitor) throws CoreException {
		SubMonitor progress = SubMonitor.convert(monitor, 2);
		if (container == null || container.exists())
			return;

		recurseCreate(container.getParent(), progress.split(1, SubMonitor.SUPPRESS_NONE));

		if (container instanceof IFolder)
			((IFolder) container).create(false, true, progress.split(1, SubMonitor.SUPPRESS_NONE));
		else
			throw new CoreException(new Status(IStatus.ERROR, BundleUtils.getBundleSymbolicName(FileUtils.class), 0,
				"Cannot create new projects or workspace roots automatically.", null));
	}

	public static byte[] readFully(InputStream stream) throws IOException {
		return IO.read(stream);
	}

	public static void writeFully(IDocument document, IFile file, boolean createIfAbsent) throws CoreException {
		writeFully(document.get(), file, createIfAbsent);
	}

	public static void writeFully(String text, IFile file, boolean createIfAbsent) throws CoreException {
		ByteArrayInputStream inputStream;
		try {
			String charset = file.getCharset(true);
			if (charset == null) {
				charset = Charset.defaultCharset()
					.name();
			}
			inputStream = new ByteArrayInputStream(text.getBytes(charset));
		} catch (UnsupportedEncodingException e) {
			return;
		}
		if (file.exists()) {
			file.setContents(inputStream, false, true, null);
		} else {
			if (createIfAbsent)
				file.create(inputStream, false, null);
			else
				throw new CoreException(new Status(IStatus.ERROR, BundleUtils.getBundleSymbolicName(FileUtils.class), 0,
					"File does not exist: " + file.getFullPath()
						.toString(),
					null));
		}
	}

	public static boolean isAncestor(File dir, File child) {
		if (child == null)
			return false;
		File c = child.getAbsoluteFile();
		if (c.equals(dir))
			return true;
		return isAncestor(dir, c.getParentFile());
	}

	public static IResource toProjectResource(IProject project, File file) {
		if (file == null) {
			return null;
		}

		String projectPath = project.getLocation()
			.toFile()
			.getAbsolutePath();
		String filePath = file.getAbsolutePath();
		if (!filePath.startsWith(projectPath)) {
			return null;
		}

		return project.getFolder(filePath.substring(projectPath.length()));
	}

	public static IResource toWorkspaceResource(File file) {
		IPath path = new Path(file.toString());

		final IWorkspace workspace = ResourcesPlugin.getWorkspace();
		final IWorkspaceRoot workspaceRoot = workspace.getRoot();
		IPath workspacePath = workspaceRoot.getLocation();

		if (workspacePath.isPrefixOf(path)) {
			final IPath relativePath = path.removeFirstSegments(workspacePath.segmentCount());
			IResource resource;
			if (file.isDirectory()) {
				resource = workspaceRoot.getFolder(relativePath);
			} else {
				resource = workspaceRoot.getFile(relativePath);
			}
			return resource;
		}
		return null;
	}

	public static IFile[] getWorkspaceFiles(File javaFile) {
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace()
			.getRoot();
		IFile[] candidates = root.findFilesForLocationURI(javaFile.toURI());
		Arrays.sort(candidates, (a, b) -> Integer.compare(a.getFullPath()
			.segmentCount(),
			b.getFullPath()
				.segmentCount()));
		return candidates;
	}

	public static File toFile(IWorkspaceRoot root, IPath path) {
		IFile wsfile;
		IFolder wsfolder;

		if ((wsfile = root.getFile(path)).exists()) {
			IPath location = wsfile.getLocation();
			if (location != null)
				return location.toFile();
		}

		if ((wsfolder = root.getFolder(path)).exists()) {
			IPath location = wsfolder.getLocation();
			if (location != null)
				return location.toFile();
		}

		return path.toFile();
	}

	public static void dumpResourceDelta(IResourceDelta delta, PrintStream out) {
		dumpResourceDelta(delta, out, "");
	}

	private static void dumpResourceDelta(IResourceDelta delta, PrintStream out, String indent) {
		out.println(
			String.format("%s%s: kind=%h, flags=%h", indent, delta.getFullPath(), delta.getKind(), delta.getFlags()));
		IResourceDelta[] children = delta.getAffectedChildren();
		for (IResourceDelta child : children) {
			dumpResourceDelta(child, out, indent + "   ");
		}
	}

	static String containerType(int type) {
		switch (type) {
			case IResource.FILE :
				return "File";
			case IResource.FOLDER :
				return "Folder";
			case IResource.ROOT :
				return "WorkspaceRoot";
			case IResource.PROJECT :
				return "Project";
			default :
				return "<unknown>";
		}
	}

	public static void mkdirs(IContainer container, IProgressMonitor monitor) throws CoreException {
		SubMonitor progress = SubMonitor.convert(monitor, 2);

		if (container.exists())
			return;

		IContainer parent = container.getParent();
		if (parent != null)
			mkdirs(parent, progress.split(1));

		if (container.getType() == IResource.FOLDER) {
			IFolder folder = (IFolder) container;
			folder.create(false, true, progress.split(1));
		} else {
			throw new CoreException(new Status(IStatus.ERROR, "bndtools.utils", 0,
				"Attempting to create container " + container + " of type "
					+ containerType(container.getType()) + ", can only create plain Folder containers.",
				null));
		}
	}

}
