package net.filebot.ui.transfer;

import static net.filebot.Logging.*;
import static net.filebot.Settings.*;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;

import net.filebot.platform.gnome.GVFS;

public class FileTransferable implements Transferable {

	public static final DataFlavor uriListFlavor = createUriListFlavor();

	private static DataFlavor createUriListFlavor() {
		try {
			return new DataFlavor("text/uri-list;class=java.nio.CharBuffer");
		} catch (ClassNotFoundException e) {
			// will never happen
			throw new RuntimeException(e);
		}
	}

	public static boolean isFileListFlavor(DataFlavor flavor) {
		return flavor.isFlavorJavaFileListType() || flavor.equals(uriListFlavor);
	}

	public static boolean hasFileListFlavor(Transferable tr) {
		return tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor) || tr.isDataFlavorSupported(FileTransferable.uriListFlavor);
	}

	private final File[] files;

	public FileTransferable(File... files) {
		this.files = files;
	}

	public FileTransferable(Collection<File> files) {
		this.files = files.toArray(new File[0]);
	}

	@Override
	public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
		if (flavor.isFlavorJavaFileListType())
			return Arrays.asList(files);
		else if (flavor.equals(uriListFlavor))
			return CharBuffer.wrap(getUriList());
		else
			throw new UnsupportedFlavorException(flavor);
	}

	/**
	 * @return line separated list of file URIs
	 */
	private String getUriList() {
		StringBuilder sb = new StringBuilder(80 * files.length);

		for (File file : files) {
			// use URI encoded path
			sb.append("file://").append(file.toURI().getRawPath());
			sb.append("\r\n");
		}

		return sb.toString();
	}

	@Override
	public DataFlavor[] getTransferDataFlavors() {
		return new DataFlavor[] { DataFlavor.javaFileListFlavor, uriListFlavor };
	}

	@Override
	public boolean isDataFlavorSupported(DataFlavor flavor) {
		return isFileListFlavor(flavor);
	}

	public static List<File> getFilesFromTransferable(Transferable tr) throws IOException, UnsupportedFlavorException {
		// On Linux, if a file is dragged from a smb share to into a java application (e.g. Ubuntu Files to FileBot)
		// the application/x-java-file-list transfer data will be an empty list
		// because the native uri-list flavor drop data will be a list of smb URLs (e.g. smb://10.0.0.1/data/file.txt)
		// and not local GVFS paths (e.g. /run/user/1000/gvfs/smb-share:server=10.0.01.1,share=data/file.txt)
		if (useGVFS()) {
			if (tr.isDataFlavorSupported(FileTransferable.uriListFlavor)) {
				// file URI list flavor (Linux)

				Readable transferData = (Readable) tr.getTransferData(FileTransferable.uriListFlavor);

				try (Scanner scanner = new Scanner(transferData)) {
					List<File> files = new ArrayList<File>();

					while (scanner.hasNextLine()) {
						String line = scanner.nextLine();

						if (line.startsWith("#")) {
							// the line is a comment (as per RFC 2483)
							continue;
						}

						try {
							File file = GVFS.getDefaultVFS().getPathForURI(new URI(line));

							if (file == null || !file.exists()) {
								throw new FileNotFoundException(file.getPath());
							}

							files.add(file);
						} catch (Throwable e) {
							debug.warning(format("GVFS: %s => %s", line, e));
						}
					}

					return files;
				}
			}
		}

		// Windows / Mac and default handling
		if (tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
			// file list flavor
			Object transferable = tr.getTransferData(DataFlavor.javaFileListFlavor);

			if (transferable instanceof List<?>) {
				return (List<File>) transferable;
			}

			// on some platforms transferable data will not be available until the drop has been accepted
			return null;
		}

		// cannot get files from transferable
		throw new UnsupportedFlavorException(DataFlavor.javaFileListFlavor);
	}

}
