package com.github.ruediste1.btrbck;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.inject.Singleton;

import com.github.ruediste1.btrbck.dom.RemoteRepository;
import com.github.ruediste1.btrbck.dom.SshTarget;

@Singleton
public class SshService {
    private ThreadLocal<Boolean> sudoRemoteBtrbck = new ThreadLocal<>();

    public void setSudoRemoteBtrbck(boolean value) {
        sudoRemoteBtrbck.set(value);
    }

    protected boolean sudoRemoteBtrbck() {
        return Boolean.TRUE.equals(sudoRemoteBtrbck.get());
    }

    private ThreadLocal<Boolean> sudoRemoteBtrfs = new ThreadLocal<>();

    public void setSudoRemoteBtrfs(boolean value) {
        sudoRemoteBtrfs.set(value);
    }

    protected boolean sudoRemoteBtrfs() {
        return Boolean.TRUE.equals(sudoRemoteBtrfs.get());
    }

    private ThreadLocal<Boolean> verboseRemote = new ThreadLocal<>();

    public void setVerboseRemote(boolean value) {
        verboseRemote.set(value);
    }

    protected boolean verboseRemote() {
        return Boolean.TRUE.equals(verboseRemote.get());
    }

    private final class SshConnectionimpl implements SshConnection {
        private final Process process;

        private SshConnectionimpl(Process process) {
            this.process = process;
        }

        @Override
        public OutputStream getOutputStream() {
            return process.getOutputStream();
        }

        @Override
        public InputStream getInputStream() {
            return process.getInputStream();
        }

        @Override
        public void close() throws Exception {
            process.waitFor();
        }
    }

    public interface SshConnection {
        InputStream getInputStream();

        OutputStream getOutputStream();

        void close() throws Exception;
    }

    private ProcessBuilder processBuilder(SshTarget target,
            List<String> commands) {
        // construct command
        LinkedList<String> list = new LinkedList<String>();
        list.add("ssh");

        // add port
        if (target.getPort() != null) {
            list.add("-p");
            list.add(target.getPort().toString());
        }

        // add host
        list.add(target.getHost());

        // add commands
        list.addAll(commands);

        return new ProcessBuilder().redirectError(Redirect.INHERIT).command(
                list);
    }

    public SshConnection sendSnapshots(RemoteRepository repo,
            String remoteStreamName) throws IOException {
        List<String> commands = new ArrayList<>();
        if (sudoRemoteBtrbck()) {
            commands.add("sudo");
        }
        commands.add("btrbck");
        commands.add("-r");
        commands.add(repo.location);
        if (sudoRemoteBtrfs()) {
            commands.add("-sudo");
        }
        if (verboseRemote()) {
            commands.add("-v");
        }

        commands.add("sendSnapshots");
        commands.add(remoteStreamName);
        final Process process = processBuilder(repo.sshTarget, commands)
                .start();
        return new SshConnectionimpl(process);
    }

    public SshConnection receiveSnapshots(RemoteRepository repo,
            String remoteStreamName, boolean createRemoteIfNecessary)
            throws IOException {
        List<String> commands = new ArrayList<>();
        if (sudoRemoteBtrbck()) {
            commands.add("sudo");
        }
        commands.add("btrbck");
        if (createRemoteIfNecessary) {
            commands.add("-c");
        }
        commands.add("-r");
        commands.add(repo.location);
        if (sudoRemoteBtrfs()) {
            commands.add("-sudo");
        }
        if (verboseRemote()) {
            commands.add("-v");
        }
        commands.add("receiveSnapshots");
        commands.add(remoteStreamName);
        Process process = processBuilder(repo.sshTarget, commands).start();
        return new SshConnectionimpl(process);
    }
}