package javax.microedition.io;

import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public final class Connector {
	public static final int READ = 1;
	public static final int WRITE = 2;
	public static final int READ_WRITE = READ | WRITE;

	private Connector() {
	}

	public static Connection open(String name, int mode) throws IOException {
		if (name == null || !name.startsWith("file:///")) {
			throw new IOException("unsupported connection: " + name);
		}
		if (mode != READ && mode != WRITE && mode != READ_WRITE) {
			throw new IOException("unsupported connector mode: " + mode);
		}
		return new FileStreamConnection(toPath(name), mode);
	}

	private static Path toPath(String name) {
		String raw = name.substring("file:///".length());
		if (raw.length() > 2 && raw.charAt(1) == ':' && raw.charAt(2) == '/') {
			return Paths.get(raw);
		}
		return Paths.get(raw.startsWith("/") ? raw.substring(1) : raw);
	}

	private static final class FileStreamConnection implements StreamConnection {
		private final Path path;
		private final int mode;
		private InputStream input;
		private OutputStream output;
		private boolean closed;

		private FileStreamConnection(Path path, int mode) {
			this.path = path;
			this.mode = mode;
		}

		public InputStream openInputStream() throws IOException {
			ensureOpen();
			if ((mode & READ) == 0) {
				throw new IOException("connection not opened for reading");
			}
			if (input != null) {
				throw new IOException("input stream already open");
			}
			input = new FilterInputStream(Files.newInputStream(path, StandardOpenOption.READ)) {
				public void close() throws IOException {
					super.close();
					input = null;
				}
			};
			return input;
		}

		public OutputStream openOutputStream() throws IOException {
			ensureOpen();
			if ((mode & WRITE) == 0) {
				throw new IOException("connection not opened for writing");
			}
			if (output != null) {
				throw new IOException("output stream already open");
			}
			Path parent = path.getParent();
			if (parent != null) {
				Files.createDirectories(parent);
			}
			output = new FilterOutputStream(Files.newOutputStream(path,
				StandardOpenOption.CREATE,
				StandardOpenOption.TRUNCATE_EXISTING,
				StandardOpenOption.WRITE)) {
				public void close() throws IOException {
					super.close();
					output = null;
				}
			};
			return output;
		}

		public void close() throws IOException {
			if (closed) {
				return;
			}
			closed = true;
			IOException failure = null;
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					failure = e;
				}
			}
			if (output != null) {
				try {
					output.close();
				} catch (IOException e) {
					if (failure == null) {
						failure = e;
					}
				}
			}
			if (failure != null) {
				throw failure;
			}
		}

		private void ensureOpen() throws IOException {
			if (closed) {
				throw new IOException("connection closed");
			}
		}
	}
}
