package be.iminds.ilabt.jfed.experimenter_gui.slice.scp;

import be.iminds.ilabt.jfed.experiment.Experiment;
import be.iminds.ilabt.jfed.experiment.SshConnectionPool;
import be.iminds.ilabt.jfed.experiment.SshConnectionPoolFactory;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentCommand;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecNode;
import javafx.application.Platform;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.xfer.FileSystemFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * User: twalcari
 * Date: 8/24/2016
 * Time: 16:47
 */
public class SCPUploadTask extends SCPTask {
    private static final Logger LOG = LoggerFactory.getLogger(SCPUploadTask.class);

    private final String localPath;
    private final String remotePath;
    private final LongProperty size = new SimpleLongProperty();


    private final SshConnectionPoolFactory sshConnectionPoolFactory;

    public SCPUploadTask(Experiment experiment, FXRspecNode node, String localPath, String remotePath, SshConnectionPoolFactory sshConnectionPoolFactory) {
        super(experiment, node);
        this.localPath = localPath;
        this.remotePath = remotePath;
        this.sshConnectionPoolFactory = sshConnectionPoolFactory;

        updateTitle("Upload '" + localPath + "' to '" + remotePath + "' on " + node.getClientId());
    }

    @Override
    public Boolean call() throws Exception {
        SshConnectionPool sshConnectionPool = sshConnectionPoolFactory.getSshConnectionPool(experiment);
        SSHClient ssh =
                sshConnectionPool.getConnection(node.getUniqueId(), ExperimentCommand.BlockingMode.WAIT_FOR_SINGLE);

        if (ssh != null) {
            SCPFileTransfer fileTransfer = ssh.newSCPFileTransfer();
            fileTransfer.setTransferListener(new TransferListener() {
                @Override
                public TransferListener directory(String name) {
                    LOG.debug("Uploading dir {}", name);
                    updateMessage("Uploading directory '" + name + "'");
                    return this;
                }

                @Override
                public StreamCopier.Listener file(String name, long size) {
                    Platform.runLater(() -> SCPUploadTask.this.size.set(SCPUploadTask.this.size.get() + size));
                    LOG.debug("Uploading {} (size: {} bytes)", name, size);
                    updateMessage("Uploading '" + name + "' (" + size + " bytes)");
                    return transferred -> {
                        updateMessage(String.format("Transferred %d%% of " + name, ((transferred * 100) / size)));
                        updateProgress(transferred, size);
                    };
                }
            });

            try {
                fileTransfer.upload(new FileSystemFile(localPath), remotePath);


                return true;
            } catch (IOException ex) {
                updateMessage("Error while uploading: " + ex.getMessage());
                LOG.error("Error while uploading {}", localPath, ex);
                throw ex;
            } finally {
                ssh.close();
            }

        } else {
            updateMessage("Could not establish a connection to " + node.getClientId());
            throw new RuntimeException("Could not establish a connection to " + node.getClientId());
        }
    }

    @Override
    public String getLocalPath() {
        return localPath;
    }


    @Override
    public String getRemotePath() {
        return remotePath;
    }

    @Override
    public LongProperty sizeProperty() {
        return size;
    }
}
