package com.jscape.inet.sftp;

import com.jscape.util.file.PosixFilePermissions;
import com.jscape.util.file.UnixFileType;
import com.jscape.util.service.Service;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;

/**
 * @author Alex Usun
 */
public interface FileService
        extends Service<Void> {

    boolean available();

    PathInfo infoAbout(String path, boolean followLink)
            throws OperationException;

    boolean exists(String path)
            throws OperationException;

    UnixFileType typeOf(String path, boolean followLink)
            throws OperationException;

    long sizeOf(String path, boolean followLink)
            throws OperationException;

    PosixFilePermissions permissionsOf(String path, boolean followLink)
            throws OperationException;

    void permissions(String path, PosixFilePermissions permissions)
            throws OperationException;

    long createTimeOf(String path, boolean followLink)
            throws OperationException;

    void createTime(String path, long timeMillis)
            throws OperationException;

    long accessTimeOf(String path, boolean followLink)
            throws OperationException;

    void accessTime(String path, long timeMillis)
            throws OperationException;

    long modificationTimeOf(String path, boolean followLink)
            throws OperationException;

    void modificationTime(String path, long timeMillis)
            throws OperationException;

    HashInfo hashOf(String path, long offset, long length, int blockSize, String... algorithms)
            throws OperationException;

    void createSymbolicLink(String linkPath, String targetPath)
            throws OperationException;

    void createDirectory(String path, PosixFilePermissions permissions)
            throws OperationException;

    void removeDirectory(String path)
            throws OperationException;

    void removeFile(String path)
            throws OperationException;

    void rename(String sourcePath, String destinationPath)
            throws OperationException;

    List<PathInfo> listDirectory(String path)
            throws OperationException;

    void readFile(String sourcePath,
                  long sourceOffset,
                  OutputStream destination,
                  boolean textModeRequired,
                  TransferListener listener)
            throws OperationException;

    void readFile(String sourcePath,
                  long sourceOffset,
                  OutputStream destination,
                  boolean textModeRequired,
                  TransferListener listener,
                  ExecutorService executorService)
            throws OperationException;

    void appendFile(InputStream source,
                   String destinationPath,
                   boolean textModeRequired,
                   TransferListener listener)
            throws OperationException;

    void appendFile(InputStream source,
                   String destinationPath,
                   boolean textModeRequired,
                   TransferListener listener,
                   ExecutorService executorService)
            throws OperationException;

    void writeFile(InputStream source,
                   String destinationPath,
                   long destinationOffset,
                   boolean textModeRequired,
                   TransferListener listener)
            throws OperationException;

    void writeFile(InputStream source,
                   String destinationPath,
                   long destinationOffset,
                   boolean textModeRequired,
                   TransferListener listener,
                   ExecutorService executorService)
            throws OperationException;

    void cancelTransferOperation();

    class OperationException
            extends ServiceException {

        private static final long serialVersionUID = -1646425077983797505L;

        public static OperationException wrap(Throwable e) {
            return e instanceof OperationException ? (OperationException) e : new OperationException(e);
        }

        public OperationException() {
        }

        public OperationException(String message) {
            super(message);
        }

        public OperationException(String message, Throwable cause) {
            super(message, cause);
        }

        public OperationException(Throwable cause) {
            super(cause.getMessage() != null ? cause.getMessage() : cause.toString(), cause);
        }
    }

    class UnsupportedVersionException
            extends OperationException {

        private static final long serialVersionUID = -5022009697527877234L;

        public final int clientVersion;
        public final int serverVersion;

        public UnsupportedVersionException(int clientVersion, int serverVersion) {
            super(String.format("Incompatible protocol versions. Client: '%s', server: '%s'.", clientVersion, serverVersion));

            this.clientVersion = clientVersion;
            this.serverVersion = serverVersion;
        }
    }

    class ServerException
            extends OperationException {

        private static final long serialVersionUID = -1843129624652018517L;
        public final int code;
        public final String message;
        public final String languageTag;

        public ServerException(int code, String message, String languageTag) {
            super(String.format("Server error: %s (%s) (%s).", message, languageTag, code));

            this.code = code;
            this.message = message;
            this.languageTag = languageTag;
        }
    }

    interface TransferListener {

        TransferListener NULL = new TransferListener() {
            @Override
            public void onTransferProgress(long transferredBytes) {
            }
        };

        void onTransferProgress(long transferredBytes);

    }


}
