package org.kne.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import org.kne.debug.TimeDebugger;

public class Compress {
	static int n=0;
	public static ThreadPoolExecutor exf=(ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2, new ThreadFactory() {
		
		@Override
		public Thread newThread(Runnable r) {
			Thread t=new Thread(r,"Compress #"+(n++));
			t.setDaemon(true);
			return t;
		}
	});
		
				

	public static  Data compress(Data b) {
		
		Deflater def=new Deflater(Deflater.BEST_COMPRESSION, true);
		def.setInput(b.b,b.off,b.len);
		def.finish();
		byte[]bo=new byte[(int) (b.len*1.1)+64];
		int vp=def.deflate(bo,4,bo.length-4);
		
		def.end();
		int v=b.len;
		bo[0]=(byte) ((v >>> 24) & 0xFF);
        bo[1]=(byte) ((v >>> 16) & 0xFF);
        bo[2]=(byte) ((v >>>  8) & 0xFF);
        bo[3]=(byte) ((v >>>  0) & 0xFF);
		return new Data(bo,0,vp+4);
	}
public static Data uncompress(Data b) throws IOException, DataFormatException {
	if(b.len<4){
		throw new EOFException("!!");
	}
	int ch1 =b.b[b.off+0]&0xff;
    int ch2 = b.b[b.off+1]&0xff;
    int ch3 =b. b[b.off+2]&0xff;
    int ch4 =b.b[b.off+3]&0xff;
   
    int zix= ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));

			Inflater inf=new Inflater(true);
		inf.setInput(b.b, b.off+4, b.len-4);
		byte[]d=new byte[zix];
		int l=inf.inflate(d);
		inf.end();
		
		return new Data(d,0,l);
	}
private static class compresstask extends Task{
	volatile Data input,output;
	
	public compresstask(Data input) {
		super();
		this.input = input;
	}

	public Data getOutput() {
		return output;
	}

	@Override
	protected void runTask() {
		output=compress(input);
	}
	
}
public static void mulcomp(DataOutputStream dos,byte[]in, TimeDebugger dbg) throws IOException {
	ArrayList<Task> l=new ArrayList();
	int count =4;
	int sze=in.length/count;
	int y=in.length%count;

	for (int i = 0; i < count; i++) {
		Task t=new compresstask(new Data(in,i*sze,sze));
		exf.execute(t);
		l.add(t);
	}
	Data tmp=null;
	if(y>0) {
	tmp=compress(new Data(in,count*sze,y));
	}
	dbg.putTime("compress");
	dos.writeInt(l.size()+(tmp==null?0:1));
	for (int i = 0; i < l.size(); i++) {
		compresstask n=(compresstask) l.get(i);
		n.waitfortask();
		Data g=n.getOutput();
		dos.writeInt(g.len);
		dos.write(g.b, g.off, g.len);

	}
	
	
	if(tmp!=null) {
	dos.writeInt(tmp.len);
	dos.write(tmp.b, tmp.off, tmp.len);
	}
}
private static class uncompresstask extends Task{
	volatile Data input,output;
	
	public uncompresstask(Data input) {
		super();
		this.input = input;
	}

	public Data getOutput() {
		return output;
	}

	@Override
	protected void runTask() {
		try {
			output=uncompress(input);
		} catch (IOException e) {
			// TODO �Զ����ɵ� catch ��
			e.printStackTrace();
		} catch (DataFormatException e) {
			// TODO �Զ����ɵ� catch ��
			e.printStackTrace();
		}
	}
	
}
public static byte[] muluncomp(DataInputStream dis,TimeDebugger dbg) throws IOException, DataFormatException {
	ArrayList<Task> l=new ArrayList();
	ByteArrayOutputStream bod=new ByteArrayOutputStream(1024);
	int bc=dis.readInt();
	for(int x=0;x<bc;x++) {
		int s=dis.readInt();
		byte []input=new byte[s];
		int offset = 0;
	    int numRead = 0;
	    while (offset < input.length
	    && (numRead = dis.read(input, offset, input.length - offset)) >= 0) {
	        offset += numRead;
	    }
	    Task tsk=new uncompresstask(new Data(input,0,input.length));
	    exf.execute(tsk);
	    l.add(tsk);
	    
	    //Data rez=uncompress(new Data(input,0,input.length));
	   
	}    dbg.putTime("read");
	for (int i = 0; i < l.size(); i++) {
		uncompresstask uct=(uncompresstask) l.get(i);
		uct.waitfortask();
		Data rez=uct.getOutput();
		 bod.write(rez.b,rez.off,rez.len);
	}

	
	//rez.b.length=rez.off;
	return bod.toByteArray();
}
	public static void main(String[] args) throws IOException, DataFormatException {
		String s="jjjjjjjjjjjjjjjjjjhjhjhm";
		byte[] ib=s.getBytes();
		Data i=new Data(ib,0,ib.length);
		Data o=compress(i);
		Data d=uncompress(o);
		System.out.println(o.len+" "+d.len);
		//for (int j = d.off; j < d.off+d.len; j++) {
			System.out.println(new String(d.b,d.off,d.len));
		//}
	}

}
