/*
 * Copyright 1999-2010 University of Chicago
 *
 * 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 org.globus.util.http;

import java.io.InputStream;
import java.io.IOException;

public class HTTPChunkedInputStream extends InputStream {

    protected byte[] _buf;
    protected int _index;
    protected int _max;
    protected boolean _eof;
    protected InputStream _in;

    public HTTPChunkedInputStream(InputStream in) {
	_in = in;
	// initial buf size - will adjust automatically
	_buf = new byte[2048];
	_index = 0;
	_max = 0;
	_eof = false;
    }

    /* only called when the buffer is empty */
    private void readChunk()
	throws IOException {

	String line = readLine(_in).trim();
	int length  = Integer.parseInt(line, 16);

	if (length > 0) {

	    // make sure the chunk will fit into the buffer

	    if (length > _buf.length) {
		_buf = new byte[length];
	    }

	    int bytesLeft = length;
	    int reqBytes = 0;
	    int off = 0;
	    int read = 0;

	    /*  multiple reads might be necessary to load
		the entire chunk */
	    while (bytesLeft != 0) {
		reqBytes = bytesLeft;
		read = _in.read(_buf, off, reqBytes);
		if (read == -1) break;
		bytesLeft -= read;
		off += read;
	    }

	    _max = off;
	    _index = 0;

	} else {
	    // end of data indicated
	    _eof = true;
	}

	_in.read(); // skip CR
	_in.read(); // skip LF

    }

    /**
     * Read a line of text from the given Stream and return it
     * as a String.  Assumes lines end in CRLF.
     */
    private String readLine(InputStream in) throws IOException {
	StringBuffer buf = new StringBuffer();
	int c, length = 0;

	while(true) {
	    c = in.read();
	    if (c == -1 || c == '\n' || length > 512) {
		break;
	    } else if (c == '\r') {
		in.read();
		return buf.toString();
	    } else {
		buf.append((char)c);
		length++;
	    }

	}
	return buf.toString();
    }

    public int read(byte [] buffer, int off, int len)
	throws IOException {
	if (_eof) return -1;
	if (_max == _index) readChunk();

	if (_index + len <= _max) {
	    // that's easy
	    System.arraycopy(_buf, _index, buffer, off, len);
	    _index += len;
	    return len;
	} else {
	    int maximum = _max - _index;
	    System.arraycopy(_buf, _index, buffer, off, maximum);
	    _index += maximum;
	    int read = read(buffer, off+maximum, len-maximum);
	    if (read == -1) {
		return maximum;
	    } else {
		return maximum + read;
	    }
	}
    }

    public int read()
	throws IOException {
	if (_eof) return -1;
	if (_max == _index) readChunk();
	return _buf[_index++] & 0xff;
    }

    public int available()
        throws IOException {
        return _in.available();
    }

    public void close()
	throws IOException {
	_in.close();
    }

}
