/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.ftp;

import com.crunii.micro.common.exception.BusinessException;
import com.crunii.micro.common.utils.CycleAtomicInteger;
import com.crunii.micro.common.utils.FileUtil;
import com.crunii.micro.common.utils.Slf4jUtil;
import com.crunii.micro.common.utils.StopUtil;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 田平 create 2021/9/22 16:15
 */
@Slf4j
public class FtpRemoteFile implements RemoteFile {

	private GenericObjectPool<FTPClient> pool;

	public static void main(String[] args) throws Exception {
		String basePath = "";
		FTPClientFactory clientFactory = new FTPClientFactory();
		clientFactory.setHost("");
		clientFactory.setPort(30002);
		clientFactory.setUsername("");
		clientFactory.setPassword("");
		GenericObjectPool<FTPClient> pool = new GenericObjectPool<>(clientFactory, new GenericObjectPoolConfig());

		FtpRemoteFile remoteFile = new FtpRemoteFile(pool);
		int size = 1000;
		CountDownLatch latch = new CountDownLatch(size);
		AtomicInteger fileName = new AtomicInteger(1);
		CycleAtomicInteger cycleAtomicInteger = new CycleAtomicInteger(1, 9);
		ExecutorService es = Executors.newFixedThreadPool(10);
		for (int i = 0; i < size; i++) {
			es.execute(() -> {
				String remoteName = "file_" + fileName.getAndIncrement() + ".txt";
				String remotePath = basePath + cycleAtomicInteger.nextId() + "/";
				remoteFile.uploadFile(remotePath, remoteName, "D:\\rr.txt");
				latch.countDown();
			});
		}
		latch.await();
		StopUtil.stopES(es);
		pool.close();
	}

	public FtpRemoteFile(GenericObjectPool<FTPClient> pool) {
		this.pool = pool;
	}

	@Override
	public void uploadFile(String remotePath, String remoteName, String localFilePath) {
		uploadFile(remotePath, remoteName, FileUtil.getFileInputStream(localFilePath));
	}

	@Override
	public void uploadFile(String remotePath, String remoteName, InputStream in) {
		FTPClient ftpClient = null;
		String realPath = FileUtil.getRealPath(remotePath);
		try {
			ftpClient = pool.borrowObject();
			mkdirWithP(ftpClient, realPath);
			ftpClient.storeFile(realPath + remoteName, in);
		} catch (Exception e) {
			throw new RuntimeException("uploadFile error!", e);
		} finally {
			IOUtils.closeQuietly(in);
			pool.returnObject(ftpClient);
		}
	}

	public static void mkdirWithP(FTPClient ftpClient, String remotePath) {
		if (StringUtils.hasText(remotePath)) {
			String realPath = FileUtil.getRealPath(remotePath);
			Iterable<String> split = Splitter.on('/').trimResults().omitEmptyStrings().split(realPath);
			StringBuilder sb = new StringBuilder("/");
			try {
				for (String curr : split) {
					String currPath = sb.append(curr).append("/").toString();
					ftpClient.changeWorkingDirectory(currPath);
					int returnCode = ftpClient.getReplyCode();
					if (returnCode == 550) {
						//目录不存在,创建目录
						try {
							ftpClient.makeDirectory(currPath);
						} catch (Exception e) {
							log.warn("mkdir {} error!", currPath, e);
						}
					}
				}
				//切换到目标目录
				ftpClient.changeWorkingDirectory(realPath);
			} catch (Exception e) {
				throw new RuntimeException(Slf4jUtil.formatMsg("mkdirWithP ftp mkdir -p {} error!", realPath), e);
			}

		}
	}

	private void isFileExists(FTPClient ftpClient, String fullPath) throws IOException {
		FTPFile[] ftpFiles = ftpClient.listFiles(fullPath);
		if (ftpFiles.length == 0) {
			throw new BusinessException("remote file not exists！path:{}", fullPath);
		}
	}

	@Override
	public byte[] getFileBytes(String remotePath, String remoteName) {
		FTPClient ftpClient = null;
		String realPath = FileUtil.getRealPath(remotePath);
		String fullPath = realPath + remoteName;
		byte[] bytes;
		try {
			ftpClient = pool.borrowObject();
			isFileExists(ftpClient, fullPath);
			InputStream inputStream = ftpClient.retrieveFileStream(fullPath);
			//自动关闭流
			bytes = FileCopyUtils.copyToByteArray(inputStream);
		} catch (Exception e) {
			throw new RuntimeException("getFileBytes error!", e);
		} finally {
			try {
				//ftpClient.retrieveFileStream 后必须调用完整指令
				ftpClient.completePendingCommand();
			} catch (IOException e) {
				log.warn("completePendingCommand {} error!", fullPath, e);
			}
			pool.returnObject(ftpClient);
		}
		return bytes;
	}

	@Override
	public int copyRange(String remotePath, String remoteName, int start, int end, OutputStream output) {
		FTPClient ftpClient = null;
		String realPath = FileUtil.getRealPath(remotePath);
		String fullPath = realPath + remoteName;
		InputStream input = null;
		try {
			ftpClient = pool.borrowObject();
			isFileExists(ftpClient, fullPath);
			input = ftpClient.retrieveFileStream(fullPath);
			Assert.notNull(input, "No InputStream specified");
			Assert.notNull(output, "No OutputStream specified");
			return (int) StreamUtils.copyRange(input, output, start, end);
		} catch (Exception e) {
			throw new RuntimeException("copyRange error!", e);
		} finally {
			IOUtils.closeQuietly(input);
			IOUtils.closeQuietly(output);
			try {
				//ftpClient.retrieveFileStream 后必须调用完整指令
				ftpClient.completePendingCommand();
			} catch (IOException e) {
				log.warn("completePendingCommand {} error!", fullPath, e);
			}
			pool.returnObject(ftpClient);
		}

	}

	@Override
	public int copyToOutputStream(String remotePath, String remoteName, OutputStream output) {
		FTPClient ftpClient = null;
		String realPath = FileUtil.getRealPath(remotePath);
		String fullPath = realPath + remoteName;
		InputStream input;
		try {
			ftpClient = pool.borrowObject();
			isFileExists(ftpClient, fullPath);
			input = ftpClient.retrieveFileStream(fullPath);

			Assert.notNull(input, "No InputStream specified");
			Assert.notNull(output, "No OutputStream specified");
			//自动关闭流
			return FileCopyUtils.copy(input, output);
		} catch (Exception e) {
			throw new RuntimeException("copyToOutputStream error!", e);
		} finally {
			try {
				//ftpClient.retrieveFileStream 后必须调用完整指令
				ftpClient.completePendingCommand();
			} catch (IOException e) {
				log.warn("completePendingCommand {} error!", fullPath, e);
			}
			pool.returnObject(ftpClient);
		}
	}

	@Override
	public void downloadFile(String remotePath, String remoteName, String localFilePath, String localFileName) {
		FTPClient ftpClient = null;
		InputStream input;
		OutputStream output;
		String realPath = FileUtil.getRealPath(remotePath);
		String realLocalFilePath = FileUtil.getRealPath(localFilePath);
		String fullPath = realPath + remoteName;
		try {
			ftpClient = pool.borrowObject();
			isFileExists(ftpClient, fullPath);
			input = ftpClient.retrieveFileStream(fullPath);
			File file = new File(realLocalFilePath + localFileName);
			output = new FileOutputStream(file);
			//自动关闭流
			FileCopyUtils.copy(input, output);
		} catch (Exception e) {
			throw new RuntimeException("downloadFile error!", e);
		} finally {
			try {
				//ftpClient.retrieveFileStream 后必须调用完整指令
				ftpClient.completePendingCommand();
			} catch (IOException e) {
				log.warn("completePendingCommand {} error!", fullPath, e);
			}
			pool.returnObject(ftpClient);
		}
	}

	public GenericObjectPool<FTPClient> getPool() {
		return pool;
	}

	public void setPool(GenericObjectPool<FTPClient> pool) {
		this.pool = pool;
	}
}
