/*******************************************************************************
 * Copyright (c) 2001, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.wst.xml.core.internal.validation.core;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;


/**
 * This class allows InputStreams to be created and returned to xerces without
 * actually opening file handles or network connections until it is absolutely
 * neccessary.
 */
public class LazyURLInputStream extends InputStream
{  
  private static int debugTotalOpenStreamCount = 0; 
  protected InputStream inner;
  protected String url;
  protected boolean error;
  boolean hasMarks;
  boolean pretendFileIsStillOpen;

  public LazyURLInputStream(String url)
  {
    this.url = url;
	inner = null;
	pretendFileIsStillOpen = false;
  }

  private void createInnerStreamIfRequired() throws IOException
  {
    if (inner == null && !error)
    {
      debugTotalOpenStreamCount++;
    
      try
      {
        inner = new URL(url).openStream();
		pretendFileIsStillOpen = false;
		hasMarks = false;
      }
      finally 
      {
        if (inner == null)
        {  
          error = true;
        }
      }
    }
  }

	protected void closeStream() throws IOException {
		if (inner != null && !pretendFileIsStillOpen) {
			inner.close();
			pretendFileIsStillOpen = true;
		}
  	}

  public int available() throws IOException
  {
	if (pretendFileIsStillOpen) return 0;
	createInnerStreamIfRequired();
	if (inner == null) throw new IOException("Stream not available"); //$NON-NLS-1$
	return inner.available();
  }

  public void close() throws IOException
  {
	if (pretendFileIsStillOpen) {
		// Stop behaving as if the stream were still open.
		pretendFileIsStillOpen = false;
	} else {
		if (inner != null) {
			debugTotalOpenStreamCount--;
			inner.close();
		}
	}
  }

  public void mark(int readlimit)
  {
	if (pretendFileIsStillOpen) return;
	hasMarks = true;
	try {
		createInnerStreamIfRequired();
		inner.mark(readlimit);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
  }

  public boolean markSupported()
  {
	if (pretendFileIsStillOpen) return false;
	try {
		createInnerStreamIfRequired();
		if (inner == null) return false;
		return inner.markSupported();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return false;
  }

  public int read() throws IOException
  {
	if (pretendFileIsStillOpen) return -1;
	createInnerStreamIfRequired();
	if (inner == null) throw new IOException("Stream not available"); //$NON-NLS-1$
	int bytesRead = inner.read();
	if (bytesRead == -1 && !hasMarks) closeStream();
	return bytesRead;
  }


	public int read(byte[] b) throws IOException {
		if (pretendFileIsStillOpen) return -1;
		createInnerStreamIfRequired();
		if (inner == null) throw new IOException("Stream not available"); //$NON-NLS-1$
		int bytesRead = inner.read(b);
		if (bytesRead == -1 && !hasMarks) closeStream();
		return bytesRead;
	}

  public int read(byte[] b, int off, int len) throws IOException
  {    
	if (pretendFileIsStillOpen) return -1;
	createInnerStreamIfRequired();
	if (inner == null) throw new IOException("Stream not available"); //$NON-NLS-1$
	int bytesRead = inner.read(b, off, len);
	if (bytesRead == -1 && !hasMarks) closeStream();
	return bytesRead;
  }

  public void reset() throws IOException
  {
	if (pretendFileIsStillOpen) return;
	createInnerStreamIfRequired();
	if (inner == null) throw new IOException("Stream not available"); //$NON-NLS-1$
	inner.reset();
  }

  public long skip(long n) throws IOException
  {   
	if (pretendFileIsStillOpen) return 0;
	createInnerStreamIfRequired();
	if (inner == null) throw new IOException("Stream not available"); //$NON-NLS-1$
	return inner.skip(n);
  }
}
