package org.kne.opencl64.concurrent;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;

import org.kne.opencl64.OpenCLCommandQueue;
import org.kne.opencl64.OpenCLContext;
import org.kne.opencl64.OpenCLDevice;

public class OpenCLExecutors {
    
    public static ExecutorService newFixedThreadPool(List<OpenCLDevice> devices, int threadsPerDevice) {
        if (devices == null || devices.isEmpty()) {
            throw new IllegalArgumentException("设备列表不能为空");
        }
        if (threadsPerDevice <= 0) {
            throw new IllegalArgumentException("每个设备的线程数必须大于 0");
        }
        
        int totalThreads = devices.size() * threadsPerDevice;
        return (ThreadPoolExecutor) Executors.newFixedThreadPool(
            totalThreads,
            new ThreadFactory() {
                private final AtomicInteger id = new AtomicInteger(0);
                private OpenCLContext[] queue = new OpenCLContext[devices.size()];
                @Override
                public Thread newThread(Runnable r) {
                    int idv = id.getAndIncrement();
                    int devId = idv / threadsPerDevice;
                    int queueId = idv % threadsPerDevice;
                    OpenCLDevice device = devices.get(devId);
                    if (queue[devId] == null) {
                        queue[devId] = new OpenCLContext(device.getPlatform(), device);
                    }
                    OpenCLContext cont = queue[devId];
                    OpenCLContextThread t = new OpenCLContextThread(device, cont, r);
                    t.setPriority(Thread.NORM_PRIORITY - 1);
                    t.setDaemon(true);
                    String type = device.getDeviceType().toString();
                    t.setName(String.format("%s%d-Queue%d-Worker-%d", type, devId, queueId, idv));
                    return t;
                }
            }
        );
    }

    /**
     * 创建混合线程池：
     * - 每张显卡创建 threadsPerDevice 个绑定队列的 GPU 线程
     * - 如果 GPU 线程总数不足 minThreads，补足普通 CPU 线程
     * 
     * @param devices          OpenCL 设备列表（可以为空）
     * @param threadsPerDevice 每张显卡的 GPU 线程数
     * @param minThreads       线程池最小线程数
     * @return 混合线程池
     */
    public static ExecutorService newHybridThreadPool(List<OpenCLDevice> devices, int threadsPerDevice, int minThreads) {
        if (threadsPerDevice < 0) {
            throw new IllegalArgumentException("每个设备的线程数不能为负数");
        }
        if (minThreads <= 0) {
            throw new IllegalArgumentException("最小线程数必须大于 0");
        }

        // 先计算 GPU 线程数
        int gpuThreads = 0;
        if (devices != null && !devices.isEmpty()) {
            gpuThreads = devices.size() * threadsPerDevice;
        }

        // 补足 CPU 线程
        int cpuThreads = Math.max(0, minThreads - gpuThreads);
        int totalThreads = gpuThreads + cpuThreads;
        boolean hasGPU = devices != null && !devices.isEmpty() && threadsPerDevice > 0;

        // 🔥 关键：将 gpuThreads 声明为 final
        final int finalGpuThreads = gpuThreads;

        return (ThreadPoolExecutor) Executors.newFixedThreadPool(
            totalThreads,
            new ThreadFactory() {
                private final AtomicInteger id = new AtomicInteger(0);
                private final OpenCLContext[] contexts = hasGPU ? new OpenCLContext[devices.size()] : null;
                private final AtomicInteger cpuThreadCounter = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    int idv = id.getAndIncrement();

                    // ✅ 使用 finalGpuThreads 替代 gpuThreads
                    if (hasGPU && idv < finalGpuThreads) {
                        int devId = idv / threadsPerDevice;
                        int queueId = idv % threadsPerDevice;
                        OpenCLDevice device = devices.get(devId);
                        if (contexts[devId] == null) {
                            contexts[devId] = new OpenCLContext(device.getPlatform(), device);
                        }
                        OpenCLContext cont = contexts[devId];
                        OpenCLContextThread t = new OpenCLContextThread(device, cont, r);
                        t.setPriority(Thread.NORM_PRIORITY - 1);
                        t.setDaemon(true);
                        String type = device.getDeviceType().toString();
                        t.setName(String.format("%s%d-Queue%d-Worker-%d", type, devId, queueId, idv));
                        return t;
                    }

                    // CPU 线程
                    Thread t = new Thread(r);
                    t.setPriority(Thread.NORM_PRIORITY - 1);
                    t.setDaemon(true);
                    int cpuId = cpuThreadCounter.getAndIncrement();
                    t.setName(hasGPU ? String.format("CPU-Worker-%d", cpuId) : String.format("CPU-Worker-%d", idv));
                    return t;
                }
            }
        );
    }
}