/*
* Copyright 2010 Srikanth Reddy Lingala  
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, 
* software distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/

package net.lingala.zip4j.io;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.Deflater;

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipModel;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.Zip4jConstants;

public class DeflaterOutputStream extends CipherOutputStream {
	
	private byte[] buff;
	protected Deflater deflater;
	private boolean firstBytesRead;
	
	public DeflaterOutputStream(OutputStream outputStream, ZipModel zipModel) {
		super(outputStream, zipModel);
		deflater = new Deflater();
		buff = new byte[InternalZipConstants.BUFF_SIZE];
		firstBytesRead = false;
	}
	
	public void putNextEntry(File file, ZipParameters zipParameters)
			throws ZipException {
		super.putNextEntry(file, zipParameters);
		if (zipParameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
			deflater.reset();
			if ((zipParameters.getCompressionLevel() < 0 || zipParameters
					.getCompressionLevel() > 9)
					&& zipParameters.getCompressionLevel() != -1) {
				throw new ZipException(
						"invalid compression level for deflater. compression level should be in the range of 0-9");
			}
			deflater.setLevel(zipParameters.getCompressionLevel());
		}
	}

	public void write(byte[] b) throws IOException {
		write(b, 0, b.length);
	}
	
	private void deflate () throws IOException {
		int len = deflater.deflate(buff, 0, buff.length);
		if (len > 0) {
			if (deflater.finished()) {
				if (len == 4) return;
				if (len < 4) {
					decrementCompressedFileSize(4 - len);
					return;
				}
				len -= 4;
			}
			if (!firstBytesRead) {
				super.write(buff, 2, len - 2);
				firstBytesRead = true;
			} else {
				super.write(buff, 0, len);
			}
		}
	}
	
	public void write(int bval) throws IOException {
	    byte[] b = new byte[1];
	    b[0] = (byte) bval;
	    write(b, 0, 1);
	}
	
	public void write(byte[] buf, int off, int len) throws IOException {
		if (zipParameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE) {
			super.write(buf, off, len);
		} else {
			deflater.setInput(buf, off, len);
			 while (!deflater.needsInput()) {
				 deflate();
             }
		}		
	}
	
	public void closeEntry() throws IOException, ZipException {
		if (zipParameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
			if (!deflater.finished()) {
				deflater.finish();
				while (!deflater.finished()) {
					deflate();
				}
			}
			firstBytesRead = false;
		}
		super.closeEntry();
	}
	
	public void finish() throws IOException, ZipException {
		super.finish();
	}
}
