/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.io.uri;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Set;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.io.IOStreams;
import net.ranides.assira.text.IOStrings;

/**
 * This is abstraction over URL, File and Path objects.
 * URIHandle can point to any resource using any protocol (local file, HTTP, FTP).
 *
 * URIHandles are not created directly, but by {@link URIResolver} factory.
 *
 * Static methods in this interface use {@link URIResolver#DEFAULT}.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public interface URIHandle extends Closeable {

    /**
     * Uses default resolver to create new URIHandle pointing to specified location.
     *
     * @param location location
     * @return URIHandle
     * @throws IOException
     *  if location has invalid format
     *  or location has unsupported scheme
     *  or URIHandler throws exception
     */
    static URIHandle resolve(String location) throws IOException {
        return URIResolver.DEFAULT.resolve(location);
    }

    /**
     * Uses default resolver to create new URIHandle pointing to specified location.
     *
     * @param location location
     * @return URIHandle
     * @throws IOException
     *  if location has invalid format
     *  or location has unsupported scheme
     *  or URIHandler throws exception
     */
    static URIHandle resolve(File location) throws IOException {
        return URIResolver.DEFAULT.resolve(location);
    }

    /**
     * Uses default resolver to create new URIHandle pointing to specified location.
     *
     * @param location location
     * @return URIHandle
     * @throws IOException
     *  if location has invalid format
     *  or location has unsupported scheme
     *  or URIHandler throws exception
     */
    static URIHandle resolve(URI location) throws IOException {
        return URIResolver.DEFAULT.resolve(location);
    }

    /**
     * Returns URIResolver factory which creates this handle
     *
     * @return URIResolver
     */
    URIResolver resolver();

    /**
     * Returns URI used to access to resource
     *
     * @return URI
     */
    URI uri();

    /**
     * Returns scheme used to access resource
     *
     * @return String
     */
    String scheme();

    /**
     * Returns file which points to resource.
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @return File
     * @throws IOException on error
     */
    File file() throws IOException;

    /**
     * Returns path which points to resource.
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @return Path
     * @throws IOException on error
     */
    Path path() throws IOException;

    /**
     * Returns charset defined for resource.
     * Many resource types return UTF8 by default.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return Charset
     * @throws IOException on error
     */
    Charset charset() throws IOException;

    /**
     * Returns attributes of this resource.
     * Many resource types return at least EXISTS and READ for correct URIs.
     *
     * This method may make I/O calls.
     *
     * @return URIFlags
     * @throws IOException on error
     */
    Set<URIFlags> flags() throws IOException;

    /**
     * Returns true if this handle points to existing resource.
     *
     * This method may make I/O calls.
     *
     * @return boolean
     * @throws IOException on error
     */
    boolean exists() throws IOException;

    /**
     * Returns requested timestamp of resource.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @param ut ut
     * @return Instant
     * @throws IOException on error
     */
    Instant time(URITime ut) throws IOException;

    /**
     * Returns size of this resource, in bytes.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return long
     * @throws IOException on error
     */
    long size() throws IOException;

    /**
     * Returns handle to parent resource (most probably parent directory).
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @return handle
     * @throws IOException on error
     */
    URIHandle parent() throws IOException;

    /**
     * Assumes that resource is directory and returns CQuery with all resources inside it.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return CQuery
     * @throws IOException on error
     */
    CQuery<URIHandle> scan() throws IOException;

    /**
     * Assumes that resource is ordinary file and returns byte-stream opened for reading.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return stream
     * @throws IOException on error
     */
    InputStream istream() throws IOException;

    /**
     * Assumes that resource is ordinary file and returns byte-stream opened for reading.
     * Starts reading from specified offset.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @param offset offset
     * @return stream
     * @throws IOException on error
     */
    InputStream istream(long offset) throws IOException;

    /**
     * Assumes that resource is ordinary file and returns byte-stream opened for reading.
     * Starts reading from specified offset and terminates after "size" bytes.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @param offset offset
     * @param size size
     * @return stream
     * @throws IOException on error
     */
    InputStream istream(long offset, long size) throws IOException;

    /**
     * Assumes that resource is ordinary file and returns byte-stream opened for writing.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return stream
     * @throws IOException on error
     */
    OutputStream ostream() throws IOException;

    /**
     * Assumes that resource is text file and returns char-stream opened for reading.
     * Uses {@link #charset()} method for character encoding.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return reader
     * @throws IOException on error
     */
    Reader reader() throws IOException;

    /**
     * Assumes that resource is text file and returns char-stream opened for reading.
     * Uses provided charset for character encoding.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @param cs cs
     * @return reader
     * @throws IOException on error
     */
    Reader reader(Charset cs) throws IOException;

    /**
     * Assumes that resource is text file and returns char-stream opened for writing.
     * Uses {@link #charset()} method for character encoding.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return writer
     * @throws IOException on error
     */
    Writer writer() throws IOException;

    /**
     * Assumes that resource is text file and returns char-stream opened for writing.
     * Uses provided charset for character encoding.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @param cs cs
     * @return writer
     * @throws IOException on error
     */
    Writer writer(Charset cs) throws IOException;

    /**
     * Assumes that resource is text file and returns its content as String.
     * Uses {@link #charset()} method for character encoding.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @return string
     * @throws IOException on error
     */
    default String text() throws IOException {
        return IOStrings.read(reader());
    }

    /**
     * Assumes that resource is text file and returns its content as String.
     * Uses provided charset for character encoding.
     *
     * This method is optional: may throw IOException if not supported.
     * This method may make I/O calls.
     *
     * @param cs cs
     * @return string
     * @throws IOException on error
     */
    default String text(Charset cs) throws IOException {
        return IOStrings.read(reader(cs));
    }

    default byte[] content() throws IOException {
        return IOStreams.read(istream());
    }

    /**
     * Filesystem I/O operation: removes this resource.
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @throws IOException on error
     */
    void delete() throws IOException;

    /**
     * Filesystem I/O operation: creates resource specified by URI inside this handle.
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @throws IOException on error
     */
    void create() throws IOException;

    /**
     * Filesystem I/O operation: changes location of this resource.
     *
     * Please note, that "target" must use public format supported by URIHandle,
     * not internal path (for example local path on FTP server).
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @param target target
     * @throws IOException on error
     */
    void move(String target) throws IOException;

    /**
     * Filesystem I/O operation: changes location of this resource.
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @param target target
     * @throws IOException on error
     */
    void move(URIHandle target) throws IOException;

    /**
     * Filesystem I/O operation: copy this resource into new location.
     *
     * Please note, that "target" must use public format supported by URIHandle,
     * not internal path (for example local path on FTP server).
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @param target target
     * @throws IOException on error
     */
    void copy(String target) throws IOException;

    /**
     * Filesystem I/O operation: copy this resource into new location.
     *
     * This method is optional: may throw IOException if not supported.
     *
     * @param target target
     * @throws IOException on error
     */
    void copy(URIHandle target) throws IOException;
    
}
