package org.kne.codec.kif;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.imageio.stream.ImageInputStream;

import org.kne.debug.TimeDebugger;
import org.kne.opencl64.OpenCLDevice;
import org.kne.opencl64.concurrent.OpenCLExecutors;


public class KIFCodec {
	private static ThreadPoolExecutor cpupool ;
	private static ThreadPoolExecutor gpupool ;
	static {
		cpupool= (ThreadPoolExecutor) Executors.newFixedThreadPool(
		    Runtime.getRuntime().availableProcessors(),
		    new ThreadFactory() {
		        private final AtomicInteger threadNumber = new AtomicInteger(1);
		        @Override
		        public Thread newThread(Runnable r) {
		            Thread t = new Thread(r, "KIFCodec-CPU-Worker-" + threadNumber.getAndIncrement());
		            t.setDaemon(true);  // 设置为守护线程
		            return t;
		        }
		    }
		);
		try {
			List<OpenCLDevice>devs=OpenCLDevice.getAllGPU();
			for(OpenCLDevice dev:devs) {
			System.out.println(dev);
			}
			gpupool=(ThreadPoolExecutor) OpenCLExecutors.newFixedThreadPool(devs,4);
		}catch(Throwable e) {
			e.printStackTrace();
		}
	}
	public static KIFCompressedImageBlock encode(IntImageBlock input,KIFCompressMode mode) {
		KIFEncodePipeline kip=new KIFEncodePipeline(input, mode);
		kip.encodeStage1();
		return kip.encodeStage2();
	}
	
	public static Future<KIFCompressedImageBlock> encodeAsync(IntImageBlock input,KIFCompressMode mode) {
		return encodeAsync(()->{return input;},mode);
	}
	
	public static Future<KIFCompressedImageBlock> encodeAsync(Supplier<IntImageBlock> input,KIFCompressMode mode) {
		/*CompletableFuture<KIFEncodePipeline> f1=CompletableFuture.supplyAsync(()->{
			KIFEncodePipeline kip=new KIFEncodePipeline(input, mode);
			kip.encodeStage1();
			return kip;
		}, gpupool);
		CompletableFuture<KIFCompressedImageBlock>f2=f1.thenApplyAsync((pipeline)->{
			return pipeline.encodeStage2();
		},cpupool);*/
		return getBestPool().submit(()->{
			KIFEncodePipeline kip=new KIFEncodePipeline(input.get(), mode);
			kip.encodeStage1();
			return kip.encodeStage2();
		});
	}
	
	public static Future<KIFCompressedImageBlock> encodeAsyncMultimode(IntImageBlock block, List<KIFCompressMode> list) {
		return encodeAsyncMultimode(()->{return block;},list);
	}
	public static Future<KIFCompressedImageBlock> encodeAsyncMultimode(Supplier< IntImageBlock> block, List<KIFCompressMode> list) {
	    // 为每个 mode 提交一个异步编码任务
	    @SuppressWarnings("unchecked")
	    ArrayList<Future<KIFCompressedImageBlock>> futures = new ArrayList<Future<KIFCompressedImageBlock>>(list.size());
	    
	    for (KIFCompressMode mode:list) {
	        // 这里假设 encodeAsync 是提交到线程池的方法
	        futures.add( encodeAsync(block, mode));
	    }
	    
	    // 返回 MultiModeFuture，它会等待所有任务完成，选择最小的
	    return new MultiModeFuture(futures);
	}
	
	
	public static IntImageBlock decode( KIFCompressedImageBlock input) throws IOException {
        KIFDecodePipeline kdec=new KIFDecodePipeline(input);
        return kdec.decodeStage2();
	}
	

	public static Future<IntImageBlock> decodeAsync(Supplier< KIFCompressedImageBlock> input) {
		
		return decodeAndConsumeAsync(input,null);
	}
	
	
	
	
	public static Future<IntImageBlock> decodeAndConsumeAsync(Supplier< KIFCompressedImageBlock> input,Consumer<IntImageBlock> con) {
		/*CompletableFuture<KIFDecodePipeline> f1=CompletableFuture.supplyAsync(()->{
			KIFDecodePipeline kip=new KIFDecodePipeline(input);
			try {
				kip.decodeStage1();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
			return kip;
		}, cpupool);
		CompletableFuture<IntImageBlock>f2=f1.thenApplyAsync((pipeline)->{
			try {
				return pipeline.decodeStage2();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		},gpupool);*/
		return getBestPool().submit(()->{
			KIFDecodePipeline kip=new KIFDecodePipeline(input.get());
			try {
				kip.decodeStage1();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
			IntImageBlock out=kip.decodeStage2();
			if(con!=null)
			con.accept(out);
			return out;
		});
	}
	
	public static void gpuBackPressure() {
		while(isGPUOverloaded()) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void cpuBackPressure() {
		while(isCPUOverloaded()) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * 判断是否过载（调用方可据此决定是否降级处理）
	 */
	public static boolean isGPUOverloaded() {
	    return gpupool.getQueue().size() > gpupool.getPoolSize() ;
	}
	/**
	 * 判断是否过载（调用方可据此决定是否降级处理）
	 */
	public static boolean isCPUOverloaded() {
	    return cpupool.getQueue().size() > cpupool.getPoolSize() ;
	}

	public static void backPressure() {
		while(isCPUOverloaded()&&isGPUOverloaded()) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	
	private static AbstractExecutorService getBestPool() {
		if(gpupool==null) {
			return cpupool;
		}
		if(isGPUOverloaded()) {
			return cpupool;
		}else {
		return gpupool;
		}
	}
	
}
