package io.renren.common.utils;

import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerCertificates;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.LogStream;
import com.spotify.docker.client.exceptions.DockerCertificateException;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.*;

import java.net.URI;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DockerClientUtils {
    private static DockerClient docker;
    private static String dockerUrl;
    private static String docker_ca;
    private static String basePath;

    public DockerClientUtils(String dockerUrl, String docker_ca,String basePath) {
        DockerClientUtils.dockerUrl=dockerUrl;
        DockerClientUtils.docker_ca=docker_ca;
        DockerClientUtils.basePath=basePath;
        createDockerClient();
    }

    /**
     * Description:创建dockerClient
     */
    public void createDockerClient(){
        try {
            System.out.println("初始化docker连接:"+dockerUrl);
            this.docker= DefaultDockerClient.builder().uri(URI.create(dockerUrl))
                    .dockerCertificates(new DockerCertificates(Paths.get(docker_ca)))
                    .build();
        }catch (DockerCertificateException e){
            e.printStackTrace();
        }
    }

    /**
     * Description:获取所有镜像列表
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    public static List<Image> getImages() throws DockerException, InterruptedException {
        List<Image> images=docker.listImages(DockerClient.ListImagesParam.allImages());
        return images;
    }

    /**
     * Description 创建容器，挂载存储卷
     * @param filePath 上传到服务器的文件所在文件夹名称
     * @param port 创建docker容器绑定到主机的端口号
     * @param memory docker内存大小限制
     * @param setCpus docker容器指定运行在哪些cpu上
     * @return 返回创建好的容器id
     * @throws DockerException
     * @throws InterruptedException
     */
    public static String createContainer(String filePath,String port,Long memory,String imageName,String setCpus) throws DockerException, InterruptedException {
        //映射端口号
        Map<String,List<PortBinding>> map=new HashMap<>();
        List<PortBinding> list=new ArrayList<>();
        //TCP
        /*PortBinding portBindingTcp=new PortBinding() {
            @Override
            public String hostIp() {
                return "0.0.0.0";
            }

            @Override
            public String hostPort() {
                return port;
            }
        };*/
        //TCP6
        PortBinding portBindingTcp6=new PortBinding() {
            @Override
            public String hostIp() {
                return "";
            }

            @Override
            public String hostPort() {
                return port;
            }
        };


        //list.add(portBindingTcp);
        list.add(portBindingTcp6);

        map.put("6006",list);
        //设置挂载文件 将训练文件挂载到容器中,并设置内存限制
        HostConfig hostConfig= HostConfig.builder()
                .appendBinds(basePath+"/"+filePath+":/opt/"+filePath).portBindings(map)
                .memory(memory)
                .cpusetCpus(setCpus)
                .build();
        ContainerCreation container=docker.createContainer(ContainerConfig.builder().image(imageName).hostConfig(hostConfig).exposedPorts("6006")

                .env("PYTHONPATH=$PYTHONPATH:/opt/"+filePath).tty(true).openStdin(true).attachStdin(true)
                .workingDir("/opt/"+filePath)//创建容器时修改工作路径到存放算法文件的路径下
                .attachStdout(true).attachStderr(true).build());
        docker.startContainer(container.id());


        return container.id();
    }

    /**
     * Description 在docker容器中安装whl格式的依赖包
     * @param containerId
     * @param filePath
     * @param whlPackageName
     */
    public static void install(String containerId,String filePath,String whlPackageName) throws DockerException, InterruptedException {
        String[] execInstall={"pip","install","/opt/"+filePath+"/"+whlPackageName};
        ExecCreation execCreation=docker.execCreate(containerId,execInstall,DockerClient.ExecCreateParam.attachStdout(),
                DockerClient.ExecCreateParam.attachStderr(), DockerClient.ExecCreateParam.attachStdin(),
                DockerClient.ExecCreateParam.tty(), DockerClient.ExecCreateParam.detach());

        LogStream output=docker.execStart(execCreation.id());
        String execOutput=output.readFully();
        System.out.println(execOutput);
    }

    /**
     * Description 在docker容器中执行运行python文件的命令
     * @param containerId 容器id
     * @param filePath python所在文件夹路径
     * @param fileName python文件名
     * @return python运行时的日志
     * @throws DockerException
     * @throws InterruptedException
     */
    public static String execPython(String containerId,String filePath,String fileName) throws DockerException, InterruptedException {

        //创建要执行的命令
        String[] execPython1={"nohup","python","-u","/opt/"+filePath+"/"+fileName,">output.log","2>&1","&"};
        ExecCreation execCreation=docker.execCreate(containerId,execPython1,DockerClient.ExecCreateParam.attachStdout(),
                DockerClient.ExecCreateParam.attachStderr(), DockerClient.ExecCreateParam.attachStdin(),
                DockerClient.ExecCreateParam.tty(), DockerClient.ExecCreateParam.detach());

        //读取所执行命令的输出
        LogStream output=docker.execStart(execCreation.id());


        //执行tensorboard命令
        String execTensorboard=docker.execCreate(containerId,new String[]{"tensorboard","--logdir=/opt/testTensorboard"}).id();
        try (LogStream stream2=docker.execStart(execTensorboard)){
            stream2.readFully();
        }

        //打印输出
        String execPythonOutput=output.readFully();
        System.out.println(execPythonOutput);
        return execPythonOutput;
    }

    /**
     * Description 在docker容器中执行运行python文件的命令，并附带参数
     * @param containerId 容器id
     * @param filePath python所在文件夹路径
     * @param fileName python文件名
     * @return python运行时的日志
     * @throws DockerException
     * @throws InterruptedException
     */
    public static String execPythonWithPara(String containerId,String filePath,String fileName,String[] params) throws DockerException, InterruptedException {

        //创建要执行的命令
        String[] execPython1={"nohup","python","-u","/opt/"+filePath+"/"+fileName,">output.log","2>&1","&"};
        int execPythonWithParaLength=execPython1.length+params.length;
        String[] execPythonWithPara=new String[execPythonWithParaLength];
        int i=0;
        for(i=0;i<4;i++){
            execPythonWithPara[i]=execPython1[i];

        }
        for(int j=0;j<3;j++){
            execPythonWithPara[execPythonWithParaLength-1-j]=execPython1[6-j];
        }

        for(int j=0;j<params.length;j++){
            execPythonWithPara[i+j]=params[j];
        }

        ExecCreation execCreation=docker.execCreate(containerId,execPythonWithPara,DockerClient.ExecCreateParam.attachStdout(),
                DockerClient.ExecCreateParam.attachStderr(), DockerClient.ExecCreateParam.attachStdin(),
                DockerClient.ExecCreateParam.tty(), DockerClient.ExecCreateParam.detach());

        //读取所执行命令的输出
        LogStream output=docker.execStart(execCreation.id());
        //打印输出
        String execPythonOutput=output.readFully();

        return execPythonOutput;
    }

    /**
     * Description 在docker容器中执行运行tensorboard文件的命令
     * @param containerId
     * @param filePath
     * @throws DockerException
     * @throws InterruptedException
     */
    public static void execTensorboard(String containerId,String filePath) throws DockerException, InterruptedException {
        String execTensorboard=docker.execCreate(containerId,new String[]{"tensorboard","--logdir=/opt/"+filePath}).id();
        try (LogStream stream2=docker.execStart(execTensorboard)){
            stream2.readFully();
        }
    }

    /**
     * Description 在容器中复制文件
     * @param containerId 容器id
     * @param formFile 来源文件
     * @param toFile 复制到的路径
     */
    public static void copyFile(String containerId,String formFile,String toFile) throws DockerException, InterruptedException {
        String execCp=docker.execCreate(containerId,new String[]{"cp","-r",formFile,toFile}).id();
        try (LogStream stream=docker.execStart(execCp)){
            stream.readFully();
        }
    }

    /**
     * Description 暂停容器
     * @param containerId
     * @throws DockerException
     * @throws InterruptedException
     */
    public static  void pauseContainer(String containerId) throws DockerException, InterruptedException {
        docker.pauseContainer(containerId);
    }

    /**
     * Description 将暂停的容器恢复到运行状态
     * @param containerId
     * @throws DockerException
     * @throws InterruptedException
     */
    public static void unpauseContainer(String containerId) throws DockerException, InterruptedException {
        docker.unpauseContainer(containerId);
    }

    /**
     * Description 根据容器id删除容器
     * @param containerId
     * @throws DockerException
     * @throws InterruptedException
     */
    public static void deleteContainer(String containerId) throws DockerException, InterruptedException {

        //首先要停止容器
        docker.stopContainer(containerId,0);
        //然后删除容器
        docker.removeContainer(containerId);
    }


}
