package name.admitriev.jhelper.generation;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import name.admitriev.jhelper.exceptions.NotificationException;
import net.egork.chelper.util.OutputWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;

public class FileUtils {
	private FileUtils() {
	}

	public static OutputWriter getOutputWriter(VirtualFile virtualFile, Object requestor) {
		try {
			return new OutputWriter(virtualFile.getOutputStream(requestor));
		}
		catch (IOException e) {
			throw new NotificationException("Couldn't open virtual file to write", e);
		}
	}

	@Nullable
	private static VirtualFile findChild(VirtualFile file, @NotNull String child) {
		if (child.equals(".")) {
			return file;
		}
		if (child.equals("..")) {
			return file.getParent();
		}
		return file.findChild(child);
	}

	public static VirtualFile findOrCreateByRelativePath(VirtualFile root, String localPath) {
		return ApplicationManager.getApplication().runWriteAction(
				new Computable<VirtualFile>() {
					@Override
					public VirtualFile compute() {
						String path = localPath;
						path = StringUtil.trimStart(path, "/");
						if (path.isEmpty()) {
							return root;
						}
						int index = path.indexOf('/');
						if (index < 0) {
							index = path.length();
						}
						String name = path.substring(0, index);

						@Nullable VirtualFile child = findChild(root, name);
						if (child == null) {
							try {
								if (index == path.length()) {
									child = root.createChildData(this, name);
								}
								else {
									child = root.createChildDirectory(this, name);
								}
							}
							catch (IOException e) {
								throw new NotificationException(
										"Couldn't create directory: " + root.getPath() + '/' + name,
										e
								);
							}
						}
						assert child != null;

						if (index < path.length()) {
							return findOrCreateByRelativePath(child, path.substring(index + 1));
						}
						return child;
					}
				}
		);
	}

	/**
	 * Checks if given file is a C++ file.
	 * In other words checks if code may be generated for that file
	 */
	public static boolean isCppFile(PsiFile file) {
		return file.getName().endsWith(".cpp");
	}

	public static void writeToFile(PsiFile outputFile, String... strings) {
		Project project = outputFile.getProject();
		Document document = PsiDocumentManager.getInstance(project).getDocument(outputFile);
		if (document == null) {
			throw new NotificationException("Couldn't open output file as document");
		}

		WriteCommandAction.writeCommandAction(project).run(
				() -> {
					document.deleteString(0, document.getTextLength());
					for (String string : strings) {
						document.insertString(document.getTextLength(), string);
					}
					FileDocumentManager.getInstance().saveDocument(document);
					PsiDocumentManager.getInstance(project).commitDocument(document);
				}
		);
	}

	public static String relativePath(String parentPath, String childPath) {
		if (!parentPath.endsWith("/")) {
			parentPath += "/";
		}
		if (!isChild(parentPath, childPath)) {
			throw new IllegalArgumentException("childPath should be inside a parentPath");
		}
		// Minimum is needed for case when childPath = parentPath and there's no / at the end of childPath
		return childPath.substring(Math.min(parentPath.length(), childPath.length()));
	}

	public static boolean isChild(String parentPath, String childPath) {
		if (!parentPath.endsWith("/")) {
			parentPath += "/";
		}
		if (!childPath.endsWith("/")) {
			childPath += "/";
		}
		return childPath.startsWith(parentPath);
	}

	public static String getDirectory(String filePath) {
		int index = filePath.lastIndexOf('/');
		if (index < 0) {
			return ".";
		}
		return filePath.substring(0, index + 1);
	}

	public static String getFilename(String filePath) {
		int index = filePath.lastIndexOf('/');
		if (index < 0) {
			return filePath;
		}
		return filePath.substring(index + 1);
	}
}