package J20250805.demo01;

import java.io.*;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author WanJl
 * @version 1.0
 * @title FileCopyUtil
 * @description
 * **文件夹拷贝工具**
 * 需求：
 * - 设计`FileCopyUtil`类，包含`copyDirectory(String srcDir, String destDir)`方法
 * - 使用多线程并发拷贝目录下的文件（子目录需递归处理）
 * - 利用`BufferedInputStream`和`BufferedOutputStream`提高 IO 效率
 * - 统计总拷贝文件数和耗时，通过`System.out`输出结果
 * @create 2025/8/5
 */
public class FileCopyUtil {
    //线程安全的计数器，用来统计拷贝的文件总数
    private final AtomicInteger copyFileCount=new AtomicInteger();
    private ExecutorService executorService;
    /**
     * 拷贝目录的方法--在这里进行多线程操作
     * @param srcDir 源目录路径
     * @param destDir 目标目录路径
     */
    public void copyDirectory(String srcDir, String destDir) throws IOException, InterruptedException {
        File srcDirectory=new File(srcDir);
        File destDirectory=new File(destDir);

        //验证目录是否有效，
        if (!srcDirectory.exists()){
            throw new FileNotFoundException("源目录不存在"+srcDir);
        }
        //记录开始时间
        long startTime = System.currentTimeMillis();

        //初始化线程池
        int threadCount=Runtime.getRuntime().availableProcessors()*2;
        executorService= Executors.newFixedThreadPool(3);
        try{
            //处理目录复制
            copyDir(srcDirectory,destDirectory);
            //关闭线程池并且等待所有的任务都完成
            executorService.shutdown();
            if (!executorService.awaitTermination(1, TimeUnit.SECONDS)){
                executorService.shutdownNow();
            }
        }finally {
            if (!executorService.isTerminated()){
                executorService.shutdownNow();
            }
        }
        //计算耗时并且输出统计结果
        long endTime = System.currentTimeMillis();
        long time=endTime-startTime;
        System.out.println("目录拷贝完成");
        System.out.println("总拷贝文件数:"+copyFileCount.get());
        System.out.println("总耗时"+time+"毫秒");

    }

    /**
     * 多线程中每个线程要执行的拷贝任务
     * @param srcDir
     * @param destDir
     */
    private void copyDir(File srcDir, File destDir) throws IOException,InterruptedException {

        //创建目标目录对象
        if (!destDir.exists()&&!destDir.mkdirs()){
            throw new IOException("无法创建目标目录"+destDir.getAbsolutePath());
        }
        //获取源目录中的所有的文件和子目录
        File[] files = srcDir.listFiles();
        System.out.println(Arrays.toString(files));
        if (files==null){
            throw new IOException("无法访问目录内容"+srcDir.getAbsolutePath());
        }
        //任务集合
        List<Future<?>> futures=new ArrayList<>();
        //处理目录中每个文件和文件夹
        for (File file:files){
            //在目标目录位置创建文件或子目录
            File destFile = new File(destDir, file.getName());
            if (file.isDirectory()){
                //递归处理子目录
                copyDir(file,destFile);
            }else {
                //提交文件拷贝任务到线程池
                Future<?> future=executorService.submit(()-> {
                    try {
                        copyFile(file,destFile);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
                //将任务放入集合
                futures.add(future);
            }
        }
        //等所有的文件拷贝任务都完成后
        for (Future<?> future:futures){
            try {
                future.get();
            }catch (ExecutionException e) {
                //处理任务过程中如果出现问题则也抛出异常
                Throwable cause = e.getCause();
                if (cause instanceof IOException){
                    throw (IOException) cause;
                }else {
                    throw new RemoteException("文件拷贝失败",cause);
                }
            }
        }

    }

    /**
     * 复制单个文件，使用缓冲流提高效率
     * @param srcFile
     * @param destFile
     */
    private void copyFile(File srcFile, File destFile) throws IOException {
        try(BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile))) {
            //字节缓冲区
            byte[] buffer=new byte[8192];
            int byteRead;
            //读取数据并且写入数据
            while ((byteRead=bis.read(buffer))!=-1){
                bos.write(buffer,0,byteRead);
            }
            //确保所有的数据都写入到磁盘
            bos.flush();
            //递增文件计数器
            copyFileCount.incrementAndGet();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        FileCopyUtil fileCopyUtil=new FileCopyUtil();
        fileCopyUtil.copyDirectory("D:\\myCode\\battle_city","D:\\MavenRepository111");

    }
}
