/*
 *This library is free software; you can redistribute it and/or
 *modify it under the terms of the GNU Lesser General Public
 *License as published by the Free Software Foundation; either
 *version 2.1 of the License, or (at your option) any later version.
 *
 *This library is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *Lesser General Public License for more details.
 *
 *You should have received a copy of the GNU Lesser General Public
 *License along with this library; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* 
 * This software was written by Phillip Lord (p.lord@hgmp.mrc.ac.uk)
 * whilst at the University of Manchester as a Pfizer post-doctoral 
 * Research Fellow. 
 *
 * The initial code base is copyright by Pfizer, or the University
 * of Manchester. Modifications to the initial code base are copyright
 * of their respective authors, or their employers as appropriate. 
 * Authorship of the modifications may be determined from the ChangeLog
 * placed at the end of this file
 */

package uk.ac.man.bioinf.io; // Package name inserted by JPack
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;


/**
 * NarrowedInputStream.java
 *
 * This InputStream wraps another and puts a limits access to that
 * stream. It hides all of the input that occur before the first
 * occurrence of a given input, and ends after the occurrence of the
 * second. 
 *
 * It may well be useful in a number of circumstances, but it should
 * be particularly good for pulling out data from the middle of an
 * HTML page between two tags. 
 *
 * Created: Thu Feb  8 17:39:21 2001
 *
 * @author Phillip Lord
 * @version $Id: NarrowedInputStream.java,v 1.2 2001/04/11 17:04:43 lord Exp $
 */

public class NarrowedInputStream extends InputStream
{
  
  private PushbackInputStream push;
  private byte[] start, stop;
  private boolean beforeStart = true;
  private boolean afterEnd = false;
  private boolean markStart = false;
  private int markLimit;
  private byte[] comprStart;
  private byte[] comprEnd;
  
  
  public NarrowedInputStream( InputStream stream, byte[] start, byte[] stop )
  {
    // blargh
    super();
    this.push = new PushbackInputStream
      (( new NoisyStream( stream ) ), 
       Math.max
       ( ((start == null ) ? 0 : start.length),
         ((stop  == null ) ? 0 : stop.length ) ));
      
      this.start  = start;
    this.stop   = stop;
    if( start != null )
      this.comprStart = new byte[ start.length ];
    if( stop != null )
      this.comprEnd = new byte[ stop.length ];
  }
    
  class NoisyStream extends InputStream
  {
    InputStream inp;
    public NoisyStream( InputStream inp )
    {
      this.inp = inp;
    }
    
    public int read() throws IOException
    {
      int retn = inp.read();
      System.out.print( (char)retn );
      return retn;
    }
    
    public int read( byte[] b ) throws IOException
    {
      int retn = inp.read( b );
      System.out.println( "String is " + new String( b ) );
      return retn;
    }
  }
  
      

  private void readToStart() throws IOException
  {

    boolean cont = true;
    
    if( start == null ){
      cont = false;
      beforeStart = false;
    }
    
    while( cont ){
      // read some bytes
      int val = push.read( comprStart );
      //System.out.print( (char)comprStart[ 0 ] + " " );
      try{
        Thread.sleep( 5 );
      }
      catch( InterruptedException ie ){
      }
      
      // if cont -1 we have reached the end of the stream before the
      // start
      if( val == -1 ){
        afterEnd = true;
        cont = false;
      }else{
        if( compareArrays( start, comprStart ) ){
          // we have now reached the start of the stream, so we have
          // read up to the right point.
          cont = false;
          beforeStart = false;
        }
        else{
          // we have not read up to the right point therefore we want to
          // push back everything but the first byte. 
          push.unread( comprStart, 1, comprStart.length - 1 );
        }
      }
    }

    // if mark has already been called then it must have been on the
    // right at the beginning of the stream, which is now, rather than
    // the start of the internal stream. 
    if( markStart ) super.mark( markLimit );
  }

  /**
   * Compare two arrays. 
   * @param fixed the fixed array, ie either the start or stop array
   * @param notFixed the other array to compare.
   * @return true if they are the same. 
   */
  private boolean compareArrays( byte[] fixed, byte[] notFixed )
  {
    for( int i = 0; i < fixed.length; i++ ){
      if( fixed[ i ] != notFixed[ i ] ){
        return false;
      }
    }
    return true;
  }
  
  public int read() throws IOException 
  {
    // have we started yet? 
    if( beforeStart ) readToStart();
    
    // do we want to stop?
    if( stop == null ){
      return push.read();
    }
    
    // have we finished yet?
    if( afterEnd ) return -1;
    
    // read the enough bytes to check that we have not reached the
    // end.
    push.read( comprEnd, 0 , comprEnd.length );
    
    // we have reached the end. 
    if( compareArrays( stop, comprEnd ) ){
      afterEnd = true;
      return -1;
    }
    
    // push back all put the first
    push.unread( comprEnd, 1, comprEnd.length - 1 );
    return comprEnd[ 0 ];
  }
  
  public void mark( int readlimit )
  {
    if( beforeStart ){
      markStart = true;
      markLimit = readlimit;
    }
    else{
      super.mark( readlimit );
    }
  }
}// NarrowedInputStream



/*
 * ChangeLog
 * $Log: NarrowedInputStream.java,v $
 * Revision 1.2  2001/04/11 17:04:43  lord
 * Added License agreements to all code
 *
 * Revision 1.1  2001/02/19 17:45:31  lord
 * Initial checkin
 *
 */
