package org.kne.codec.kvf;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;

import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.kne.codec.kif.ExceptionTool;

public class KVFOutputStream extends ZipOutputStream {
	private ReentrantLock lock=new ReentrantLock();
	private List<Future<byte[]>>tasks=Collections.synchronizedList(new ArrayList<>());
	public KVFOutputStream(OutputStream out, Charset charset) {
		super(out, charset);
		setLevel(Deflater.NO_COMPRESSION);
	}

	public KVFOutputStream(OutputStream out) {
		super(out);
		setLevel(Deflater.NO_COMPRESSION);
	}


    /**
     * 写入一个条目（自动处理锁）
     */
    public void putEntry(String name, byte[] data) throws IOException {
        lock.lock();
        try {
            putNextEntry(new ZipEntry(name));
            write(data);
            closeEntry();
        } finally {
            lock.unlock();
        }
    }
    
  
    
    /**
     * 写入 meta.json（自动使用 UTF-8）
     */
    public void writeMeta(String json) throws IOException {
        putEntry("meta.json", json.getBytes(StandardCharsets.UTF_8));
    }
    
    
    /**
     * 写入一个帧（KIF 数据）
     */
    public void writeFrame(int channelIndex, int frameIndex, byte[] kifData,String format) throws IOException {
        putEntry(String.format("video/%d/frame_%04d.%s",channelIndex, frameIndex,format), kifData);
    }
    //private List
    public void writeFrame(int channelIndex, int frameIndex,BufferedImage frame,String format) throws IOException {
		removeDone();
    	Future<byte[]>encodeFuture= KVFCodec.encodeIFrame(frame, format, (result)->{
    		try {
				writeFrame(channelIndex, frameIndex, result,format);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
    	});
    	tasks.add(encodeFuture);
    }

	public void writeFrame(int channelIndex, int frameIndex, Picture picture, String format) throws IOException {
		removeDone();
        Future<byte[]>encodeFuture= KVFCodec.encodeIFrame(picture, format, (result)->{
    		try {
				writeFrame(channelIndex, frameIndex, result,format);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
    	});
        tasks.add(encodeFuture);
	}
	
	private void waitForDone()throws IOException  {
		while(true) {
			removeDone();
			if(tasks.isEmpty()) {
				return;
			}
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	private void removeDone()throws IOException {
		AtomicReference<Throwable>e=new AtomicReference<>();
		tasks.removeIf((v)->{
			boolean b=v.isDone();
			if(b!=false) {
				try{
				v.get();
				}catch(ExecutionException | InterruptedException thr) {
					if(e.get()==null) {
						e.set(thr);
					}
				}
			}
			return b;
		});
		Throwable th=e.get();
		ExceptionTool.throwIOException(th);
	}

	@Override
    public void finish() throws IOException {
    	waitForDone();
        lock.lock();
        try {
            super.finish();
        } finally {
            lock.unlock();
        }
    }
	
	
    @Override
    public void close() throws IOException {
    	waitForDone();
        lock.lock();
        try {
            super.close();
        } finally {
            lock.unlock();
        }
    }
}
