package com.nirima.jenkins.plugins.docker.builder;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.PullImageCmd;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.PullResponseItem;
import com.github.dockerjava.core.command.PullImageResultCallback;
import com.nirima.jenkins.plugins.docker.DockerCloud;
import com.nirima.jenkins.plugins.docker.DockerSimpleTemplate;
import com.nirima.jenkins.plugins.docker.DockerTemplateBase;
import com.nirima.jenkins.plugins.docker.utils.JenkinsUtils;

import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Run;
import hudson.model.TaskListener;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.docker.commons.credentials.DockerRegistryEndpoint;
import org.jenkinsci.plugins.docker.commons.credentials.DockerRegistryToken;
import org.jenkinsci.plugins.tokenmacro.TokenMacro;
import org.kohsuke.stapler.DataBoundConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

/**
 * Build step that allows run container through existed DockerCloud
 *
 * @author magnayn
 */
public class DockerBuilderControlOptionRun extends DockerBuilderControlCloudOption {
    private static final Logger LOG = LoggerFactory.getLogger(DockerBuilderControlOptionRun.class);

    public final String image;
    public final DockerRegistryEndpoint registry;
    public final String dnsString;
    public final String network;
    public final String dockerCommand;
    public final String volumesString;
    public final String volumesFrom;
    public final String environmentsString;
    public final String lxcConfString;
    public final boolean privileged;
    public final boolean tty;
    public final String hostname;
    public final String bindPorts;
    public final Integer memoryLimit;
    public final Integer memorySwap;
    public final Integer cpuShares;
    public final boolean bindAllPorts;
    public final String macAddress;

    @DataBoundConstructor
    public DockerBuilderControlOptionRun(
            String cloudName,
            String image,
            DockerRegistryEndpoint registry,
            String lxcConfString,
            String dnsString,
            String network,
            String dockerCommand,
            String volumesString,
            String volumesFrom,
            String environmentsString,
            String hostname,
            Integer memoryLimit,
            Integer memorySwap,
            Integer cpuShares,
            String bindPorts,
            boolean bindAllPorts,
            boolean privileged,
            boolean tty,
            String macAddress) {
        super(cloudName);
        this.image = image;
        this.registry = registry;
        this.lxcConfString = lxcConfString;
        this.dnsString = dnsString;
        this.network = network;
        this.dockerCommand = dockerCommand;
        this.volumesString = volumesString;
        this.volumesFrom = volumesFrom;
        this.environmentsString = environmentsString;
        this.privileged = privileged;
        this.tty = tty;
        this.hostname = hostname;
        this.bindPorts = bindPorts;
        this.memoryLimit = memoryLimit;
        this.memorySwap = memorySwap;
        this.cpuShares = cpuShares;
        this.bindAllPorts = bindAllPorts;
        this.macAddress = macAddress;
    }

    @Override
    public void execute(Run<?, ?> build, Launcher launcher, TaskListener listener)
            throws DockerException {
        final PrintStream llog = listener.getLogger();

        DockerClient client = getCloud(build,launcher).getClient();

        String xImage = expand(build, image);
        String xCommand = expand(build, dockerCommand);
        String xHostname = expand(build, hostname);

        LOG.info("Pulling image {}", xImage);
        llog.println("Pulling image " + xImage);


        PullImageResultCallback resultCallback = new PullImageResultCallback() {
            public void onNext(PullResponseItem item) {
                if (item.getStatus() != null && item.getProgress() == null) {
                    llog.print(item.getId() + ":" + item.getStatus());
                    LOG.info("{} : {}", item.getId(), item.getStatus());
                }
                super.onNext(item);
            }
        };

        PullImageCmd cmd = client.pullImageCmd(xImage);
        if (registry == null) {
            DockerRegistryToken token = registry.getToken(null);
            AuthConfig auth = new AuthConfig()
                    .withRegistryAddress(registry.getUrl())
                    .withEmail(token.getEmail())
                    .withRegistrytoken(token.getToken());
            cmd.withAuthConfig(auth);
        }
        cmd.exec(resultCallback).awaitSuccess();

        DockerTemplateBase template = new DockerSimpleTemplate(xImage, registry,
                dnsString, network, xCommand,
                volumesString, volumesFrom, environmentsString, lxcConfString, xHostname,
                memoryLimit, memorySwap, cpuShares, bindPorts, bindAllPorts, privileged, tty, macAddress);

        LOG.info("Starting container for image {}", xImage);
        llog.println("Starting container for image " + xImage);
        String containerId = DockerCloud.runContainer(template, client, null);

        LOG.info("Started container {}", containerId);
        llog.println("Started container " + containerId);

        getLaunchAction(build).started(client, containerId);
    }

    private String expand(Run<?, ?> build, String text) {
        try {
            if (build instanceof AbstractBuild && !Strings.isNullOrEmpty(text)) {
                text = TokenMacro.expandAll((AbstractBuild) build, TaskListener.NULL, text);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return text;
    }

    @Extension
    public static final class DescriptorImpl extends DockerBuilderControlOptionDescriptor {
        @Override
        public String getDisplayName() {
            return "Run Container";
        }
    }
}

@@ -192,24 +192,152 @@
 ption.Docker
+ClientException;%0Aimport com.github.dockerjava.api.exception.DockerException;%0Aimport com.github.dockerjava.api.exception.NotFound
 Exception;%0Ai
@@ -5111,32 +5111,50 @@
 uth);%0A        %7D%0A
+        try %7B%0A    
         cmd.exec
@@ -5179,18 +5179,355 @@
 wait
-Success();
+Completion();%0A        %7D catch (InterruptedException e) %7B%0A            throw new DockerClientException(%22Interrupted while pulling image%22, e);%0A        %7D%0A        try %7B%0A            client.inspectImageCmd(xImage).exec();%0A        %7D catch (NotFoundException e) %7B%0A            throw new DockerClientException(%22Failed to pull image: %22 + image, e);%0A        %7D%0A
 %0A%0A  

check for image after pull as detection for pull status based on message is fragile on swarm