package net.i2p.util;

import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Provide a cache of reusable GZIP unzipper streams.
 * This provides stream output only, and therefore can handle unlimited size.
 */
public class ReusableGZIPInputStream extends ResettableGZIPInputStream {
    // Apache Harmony 5.0M13 Deflater doesn't work after reset()
    // Neither does Android
    private static final boolean ENABLE_CACHING = !(SystemVersion.isApache() ||
                                                    SystemVersion.isAndroid());
    private static final LinkedBlockingQueue<ReusableGZIPInputStream> _available;
    static {
        if (ENABLE_CACHING)
            _available = new LinkedBlockingQueue<ReusableGZIPInputStream>(8);
        else
            _available = null;
    }

    /**
     * Pull a cached instance
     */
    public static ReusableGZIPInputStream acquire() {
        ReusableGZIPInputStream rv = null;
        // Apache Harmony 5.0M13 Deflater doesn't work after reset()
        if (ENABLE_CACHING)
            rv = _available.poll();
        if (rv == null) {
            rv = new ReusableGZIPInputStream();
        } 
        return rv;
    }

    /**
     * Release an instance back into the cache (this will reset the 
     * state)
     */
    public static void release(ReusableGZIPInputStream released) {
        boolean cached;
        if (ENABLE_CACHING) {
            cached = _available.offer(released);
        } else {
            cached = false;
        }
        if (!cached) {
            try { released.destroy(); } catch (IOException ioe) {}
        }
    }
    
    private ReusableGZIPInputStream() { super(); }
    
    /**
     *  Clear the cache.
     *  @since 0.9.21
     */
    public static void clearCache() {
        if (_available != null)
            _available.clear();
    }

/*******
    public static void main(String args[]) {
        for (int i = 0; i < 2; i++)
            test();
        for (int i = 0; i < 64*1024; i++) {
            if (!test(i)) break;
        }
    }
    private static void test() {
        byte b[] = "hi, how are you today?".getBytes();
        try { 
            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(64);
            ResettableGZIPOutputStream o = new ResettableGZIPOutputStream(baos);
            o.write(b);
            o.finish();
            o.flush();
            byte compressed[] = baos.toByteArray();
            
            ReusableGZIPInputStream in = ReusableGZIPInputStream.acquire();
            in.initialize(new java.io.ByteArrayInputStream(compressed));
            byte rv[] = new byte[128];
            int read = in.read(rv);
            if (!net.i2p.data.DataHelper.eq(rv, 0, b, 0, b.length))
                throw new RuntimeException("foo, read=" + read);
            else
                System.out.println("match, w00t");
            ReusableGZIPInputStream.release(in);
        } catch (Exception e) { e.printStackTrace(); }
    }
    
    private static boolean test(int size) {
        byte b[] = new byte[size];
        RandomSource.getInstance().nextBytes(b);
        try { 
            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(size);
            ResettableGZIPOutputStream o = new ResettableGZIPOutputStream(baos);
            o.write(b);
            o.finish();
            o.flush();
            byte compressed[] = baos.toByteArray();
            
            ReusableGZIPInputStream in = ReusableGZIPInputStream.acquire();
            in.initialize(new java.io.ByteArrayInputStream(compressed));
            java.io.ByteArrayOutputStream baos2 = new java.io.ByteArrayOutputStream(size);
            byte rbuf[] = new byte[128];
            try {
                while (true) {
                    int read = in.read(rbuf);
                    if (read == -1)
                        break;
                    baos2.write(rbuf, 0, read);
                }
            } catch (java.io.IOException ioe) {
                ioe.printStackTrace();
                //long crcVal = in.getCurrentCRCVal();
                //try { in.verifyFooter(); } catch (IOException ioee) {
                //    ioee.printStackTrace();
                //}
                throw ioe;
            } catch (RuntimeException re) {
                re.printStackTrace();
                throw re;
            }
            ReusableGZIPInputStream.release(in);
            byte rv[] = baos2.toByteArray();
            if (rv.length != b.length)
                throw new RuntimeException("read length: " + rv.length + " expected: " + b.length);
            
            if (!net.i2p.data.DataHelper.eq(rv, 0, b, 0, b.length)) {
                throw new RuntimeException("foo, read=" + rv.length);
            } else {
                System.out.println("match, w00t @ " + size);
                return true;
            }
        } catch (Exception e) { 
            System.out.println("Error dealing with size=" + size + ": " + e.getMessage());
            e.printStackTrace(); 
            return false;
        }
    }
******/
}

