package info.knacki.pass.io.ssh;

import android.os.AsyncTask;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.security.InvalidParameterException;
import java.util.logging.Level;
import java.util.logging.Logger;

import info.knacki.pass.io.FileUtils;

public class JSchWrapper extends AsyncTask<InputStream, Void, Void> implements SSHConnection {
    private static final Logger log = Logger.getLogger(JSchWrapper.class.getName());
    private final String fCommand;
    private final ConnectionParams fAuth;
    private OnConnectionReadyListener fReadyListener;

    private final PipedOutputStream stdout_out = new PipedOutputStream();
    private final PipedOutputStream stderr_out = new PipedOutputStream();
    private final PipedInputStream stdout = new PipedInputStream();
    private final PipedInputStream stderr = new PipedInputStream();

    private ChannelExec fChannel = null;

    public JSchWrapper(ConnectionParams config, String command) {
        fCommand = command;
        fAuth = config;
    }

    @Override
    public SSHConnection SetOnConnectionReadyListener(OnConnectionReadyListener listener) {
        fReadyListener = listener;
        return this;
    }

    @Override
    public byte[] GetOutputStd() throws IOException {
        return FileUtils.ReadAllStream(stdout);
    }

    @Override
    public byte[] GetOutputErr() throws IOException {
        return FileUtils.ReadAllStream(stderr);
    }

    @Override
    public SSHConnection connect() {
        if (fChannel != null)
            throw new InvalidParameterException("JSch Already connected");
        execute();
        return this;
    }

    @Override
    public SSHConnection connect(InputStream stdin) {
        if (fChannel != null)
            throw new InvalidParameterException("JSch Already connected");
        execute(stdin);
        return this;
    }

    @Override
    protected Void doInBackground(InputStream... in) {
        InitConnection(in == null || in.length == 0 ? null : in[0]);
        if (fChannel != null) {
            try {
                stderr.connect(stderr_out);
                stdout.connect(stdout_out);
            }
            catch (IOException e) {
                log.log(Level.SEVERE, "Cannot read from output: " +e.getMessage(), e);
            }
            if (fReadyListener != null)
                fReadyListener.OnConnectionReady(this, stdout, stderr);
        }
        return null;
    }

    private void InitConnection(InputStream in) {
        try {
            // Connection settings
            JSch connection = new JSch();
            String privateKey = fAuth.GetPrivateKey();
            Session session = connection.getSession(fAuth.GetUser(), fAuth.GetHostname());
            session.setConfig("StrictHostKeyChecking", "no"); // FIXME
            if (privateKey != null && !privateKey.isEmpty()) {
                connection.addIdentity(privateKey); // FIXME key passphrase
            } else {
                session.setPassword(fAuth.GetPassword());
            }
            session.connect();
            fChannel = (ChannelExec) session.openChannel("exec");

            // Stream settings
            if (in != null)
                fChannel.setInputStream(in);
            fChannel.setErrStream(stderr_out);
            fChannel.setOutputStream(stdout_out);

            // do connect
            fChannel.setCommand(fCommand);
            fChannel.connect();
        }
        catch (JSchException e) {
            log.log(Level.SEVERE, "Cannot Initiate ssh command: " +e.getMessage(), e);
            fChannel = null;
        }
    }

    @Override
    public void disconnect() {
        fChannel.disconnect();
        fChannel = null;
    }
}
