//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   16 Mar 06  Brian Frank  Creation
//

**
** InStream is used to read binary and text stream based input.
**
class InStream
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  **
  ** Constructor for an InStream which wraps another stream.
  ** All reads from this stream will be routed to the specified
  ** inner stream.
  **
  ** If 'in' is null, then it is the subclass responsibility to
  ** handle reads by overriding the following methods: `read`,
  ** `readBuf`, and `unread`.
  **
  ** If 'in' is not null, then this stream will automatically
  ** set its own charset to 'in.charset'.
  **
  protected new make(InStream? in)

//////////////////////////////////////////////////////////////////////////
// Virtuals
//////////////////////////////////////////////////////////////////////////

  **
  ** Return the number of bytes available on input stream without
  ** blocking.  Return zero if no bytes available or it is unknown.
  ** Throw IOErr on error.
  **
  virtual Int avail()

  **
  ** Read the next unsigned byte from the input stream.
  ** Return null if at end of stream.  Throw IOErr on error.
  **
  virtual Int? read()

  **
  ** Attempt to read the next n bytes into the Buf at its current
  ** position.  The buffer will be grown as needed.  Return the number
  ** of bytes read and increment buf's size and position accordingly.
  ** Return null and leave buf's state untouched if end of stream.
  ** Note this method may not read the full number of n bytes, use
  ** `readBufFully` if you must block until all n bytes read.
  ** Throw IOErr on error.
  **
  virtual Int? readBuf(Buf buf, Int n)

  **
  ** Pushback a byte so that it is the next byte to be read.  There
  ** is a finite limit to the number of bytes which may be pushed
  ** back.  Return this.
  **
  virtual This unread(Int b)

  **
  ** Close the input stream.  This method is guaranteed to never
  ** throw an IOErr.  Return true if the stream was closed successfully
  ** or false if the stream was closed abnormally.  Default implementation
  ** does nothing and returns true.
  **
  virtual Bool close()

  **
  ** Attempt to skip 'n' number of bytes.  Return the number of bytes
  ** actually skipped which may be equal to or less than n.
  **
  virtual Int skip(Int n)

//////////////////////////////////////////////////////////////////////////
// Buf Support
//////////////////////////////////////////////////////////////////////////

  **
  ** Read the entire contents of the stream into a memory Buf.
  ** The resulting buffer is automatically positioned at the beginning.
  ** This InStream is guaranteed to be closed.
  **
  Buf readAllBuf()

  **
  ** Read the next n bytes from the stream into the Buf at its
  ** current position.  The buffer will be grown as needed.  If the
  ** buf parameter is null, then a memory buffer is automatically created
  ** with a capacity of n.  Block until exactly n bytes have been
  ** read or throw IOErr if end of stream is reached first.  Return
  ** the Buf passed in or the one created automatically if buf is null.
  ** The buffer is automatically positioned at zero.
  **
  Buf readBufFully(Buf? buf, Int n)

//////////////////////////////////////////////////////////////////////////
// Binary Data
//////////////////////////////////////////////////////////////////////////

  **
  ** Byte order mode for binary reads.
  ** Default is `Endian.big` (network byte order).
  **
  Endian endian

  **
  ** Peek at the next byte to be read without actually consuming
  ** it.  Peek has the same semantics as a read/unread.  Return
  ** null if at end of stream.
  **
  Int? peek()

  **
  ** Read the next byte as an unsigned 8-bit number.  This method may
  ** be paired with `OutStream.write`.  Throw IOErr on error or if the
  ** end of stream is reached before one byte can be read.  This method
  ** differs from `read` in that it will throw IOErr on end of stream
  ** rather than return null.
  **
  Int readU1()

  **
  ** Read the next byte as a signed 8-bit number.  This method may be
  ** paired with `OutStream.write`.  Throw IOErr on error or if the end
  ** of stream is reached before one byte can be read.
  **
  Int readS1()

  **
  ** Read the next two bytes as an unsigned 16-bit number using configured
  ** `endian`.  This method may be paired with `OutStream.writeI2`.
  ** Throw IOErr on error or if the end of stream is reached before
  ** two bytes can be read.
  **
  Int readU2()

  **
  ** Read the next two bytes as a signed 16-bit number using configured
  ** `endian`.  This method may be paired with `OutStream.writeI2`.
  ** Throw IOErr on error or if the end of stream is reached before
  ** two bytes can be read.
  **
  Int readS2()

  **
  ** Read the next four bytes as an unsigned 32-bit number using configured
  ** `endian`.  This method may be paired with `OutStream.writeI4`.
  ** Throw IOErr on error or if the end of stream is reached before
  ** four bytes can be read.
  **
  Int readU4()

  **
  ** Read the next four bytes as a signed 32-bit number using configured
  ** `endian`.  This method may be paired with `OutStream.writeI4`.
  ** Throw IOErr on error or if the end of stream is reached before
  ** four bytes can be read.
  **
  Int readS4()

  **
  ** Read the next eight bytes as a signed 64-bit number using configured
  ** `endian`.  This method may be paired with `OutStream.writeI8`.
  ** Throw IOErr on error or if the end of stream is reached before
  ** eight bytes can be read.  Note there is no readU8 (because Java
  ** doesn't support unsigned longs).
  **
  Int readS8()

  **
  ** Read the next four bytes as a 32-bit floating point number using
  ** configured `endian` according to `Float.bits32`.  This method
  ** may be paired with `OutStream.writeF4`.  Throw IOErr on error or if
  ** the end of stream is reached before four bytes can be read.
  **
  Float readF4()

  **
  ** Read the next eight bytes as a 64-bit floating point number using
  ** configured `endian` according to `Float.bits`.  This method may be
  ** paired with `OutStream.writeF8`.  Throw IOErr on error or if the
  ** end of stream is reached before four bytes can be read.
  **
  Float readF8()

  **
  ** Read the next byte and return true if nonzero.  This method may
  ** be paired with `OutStream.writeBool`.  Throw IOErr on error or if
  ** the end of stream is reached before one byte can be read.
  **
  Bool readBool()

  **
  ** Read a decimal string according to `readUtf`.
  **
  Decimal readDecimal()

  **
  ** Read a Str in modified UTF-8 format according to the java.io.DataInput
  ** specification. This method may be paired with `OutStream.writeUtf`.
  ** Throw IOErr on error, invalid UTF encoding, or if the end of stream
  ** is reached before the string is fully read.
  **
  Str readUtf()

  **
  ** Read between 0 and 64 bits from the input stream.  Bits which
  ** are partial bytes are consumed from the input stream one byte
  ** at a time.  Throw IOErr on error or if end of stream is reached
  ** before given number of bits can be read.
  **
  Int readBits(Int num)

  **
  ** Get number of bits left in current byte which have not been
  ** read by `readBits` yet.  For example if three bits are read, then
  ** return 5 to indicate number of bits left to sync up up to byte
  ** boundaries.  Not part of public API!
  **
  @NoDoc Int numPendingBits()

//////////////////////////////////////////////////////////////////////////
// Text Data
//////////////////////////////////////////////////////////////////////////

  **
  ** The current charset used to decode bytes into Unicode
  ** characters.  The default charset should always be UTF-8.
  **
  Charset charset

  **
  ** Read a single Unicode character from the stream using the
  ** current charset encoding.  Return null if at end of stream.
  ** Throw IOErr if there is a problem reading the stream, or
  ** an invalid character encoding is encountered.
  **
  Int? readChar()

  **
  ** Pushback a char so that it is the next char to be read.  This
  ** method pushes back one or more bytes depending on the current
  ** character encoding.  Return this.
  **
  This unreadChar(Int b)

  **
  ** Peek at the next char to be read without actually consuming
  ** it.  Peek has the same semantics as a readChar/unreadChar.
  ** Return null if at end of stream.
  **
  Int? peekChar()

  **
  ** Read the next n chars from the stream as a Str using the
  ** current `charset`.  Block until exactly n chars have been
  ** read or throw IOErr if end of stream is reached first.
  **
  Str readChars(Int n)

  **
  ** Read the next line from the input stream as a Str based on the
  ** configured charset.  A line is terminated by \n, \r\n, \r, or
  ** EOF.  The Str returned never contains the trailing newline.
  **
  ** The max parameter specifies the maximum number of Unicode
  ** characters (not bytes) to read before truncating the line and
  ** returning.  If max is null, then no boundary is enforced except
  ** of course the end of the stream.
  **
  ** Return null if the end of stream has been reached.  Throw IOErr
  ** if there is a problem reading the stream or an invalid character
  ** encoding is encountered.
  **
  Str? readLine(Int? max := null)

  **
  ** Read a Str token from the input stream which is terminated
  ** when the specified function 'c' returns true.  The terminating
  ** char is unread and will be the next char read once this
  ** method returns.  Characters are read based on the currently
  ** configured charset.
  **
  ** If 'c' is null then the default implementation tokenizes up
  ** until the next character which returns true for `Int.isSpace`.
  **
  ** The max parameter specifies the maximum number of Unicode
  ** characters (not bytes) to read before truncating the line and
  ** returning.  If max is null, then no boundary is enforced except
  ** of course the end of the stream.
  **
  ** Return null if the end of stream has been reached.  Throw IOErr
  ** if there is a problem reading the stream or an invalid character
  ** encoding is encountered.
  **
  Str? readStrToken(Int? max := null, |Int ch->Bool|? c := null)

  **
  ** Read a string terminated by the "\0" character.  The "\0"
  ** character is read from the stream, but not included in the
  ** string result.
  **
  ** The max parameter specifies the maximum number of Unicode
  ** characters (not bytes) to read before truncating the string and
  ** returning.  If max is null, then no boundary is enforced except
  ** of course the end of the stream.
  **
  Str readNullTerminatedStr(Int? max := null)

  **
  ** Read the entire stream into a list of Str lines based on the
  ** configured charset encoding.  Each Str in the list maps to a
  ** line terminated by \n, \r\n, \r, or EOF using the same semantics
  ** as `readLine` with default max line length of null.  The Str lines
  ** themselves do not contain a trailing newline.  Empty lines
  ** are returned as the empty Str "".  Return an empty list if
  ** currently at end of stream (not null).  Throw IOErr if there
  ** is a problem reading the stream or an invalid character encoding
  ** is encountered.  This InStream is guaranteed to be closed upon
  ** return.
  **
  Str[] readAllLines()

  **
  ** Read the entire stream into Str lines based on the current
  ** encoding.  Call the specified function for each line read.
  ** Each line is terminated by \n, \r\n, \r, or EOF using the same
  ** semantics as `readLine` with the null default max line length.
  ** The Str lines themselves do not contain a trailing newline.
  ** Empty lines are returned as the empty Str "".  This InStream is
  ** guaranteed to be closed upon return.
  **
  Void eachLine(|Str line| f)

  **
  ** Read the entire stream into a Str based on the configured
  ** charset encoding.  If the normalizeNewlines flag is true,
  ** then all occurances of \r\n or \r newlines are normalized
  ** into \n.  Return "" if the stream is empty.  Throw IOErr if
  ** there is a problem reading the stream or an invalid character
  ** encoding is encountered.  This InStream is guaranteed to
  ** be closed.
  **
  Str readAllStr(Bool normalizeNewlines := true)

  **
  ** Read a serialized object from the stream according to
  ** the Fantom [serialization format]`docLang::Serialization`.
  ** Throw IOErr or ParseErr on error.  This method may consume
  ** bytes/chars past the end of the serialized object (we may
  ** want to add a "full stop" token at some point to support
  ** compound object streams).
  **
  ** The options may be used to specify additional decoding
  ** logic:
  **   - "makeArgs": Obj[] arguments to pass to the root
  **     object's make constructor via 'Type.make'
  **
  Obj? readObj([Str:Obj]? options := null)

//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////

  **
  ** Read the entire stream into a 'Str:Str' of name/value pairs using the
  ** Fantom props file format.  This format is similar to but different than
  ** the Java properties file format:
  **   - Input must be UTF-8 encoded (current charset is ignored)
  **   - Name/value pairs formatted as logical line: '<name>=<value>'
  **   - Any Unicode character allowed in name or value
  **   - Leading and trailing whitespace trimmed from both name and value
  **   - Duplicate name keys within one file is an error condition
  **   - Lines starting with '#' are comments
  **   - Comment to end of line is '//' if start of line or preceeded
  **     by whitespace
  **   - Block comment is '/* */' (may be nested)
  **   - Use trailing '\' to continue logical line to another actual line,
  **     any leading whitespace (space or tab char) is trimmed from beginning
  **     of continued line
  **   - Fantom Str literal escape sequences supported: '\n \r \t or \uxxxx'
  **   - The '$' character is treated as a normal character and should not be
  **     escaped, but convention is to indicate a variable in a format string
  **   - Convention is that name is lower camel case with dot separators
  **
  ** Throw IOErr if there is a problem reading the stream or an invalid
  ** props format is encountered.  This InStream is guaranteed to be closed.
  **
  ** Also see `Env.props`.
  **
  Str:Str readProps()

  **
  ** Read props but map values to a list of strings which include duplicates.
  **
  @NoDoc Str:Str[] readPropsListVals()

  **
  ** Pipe bytes from this input stream to the specified output stream.
  ** If n is specified, then block until exactly n bytes have been
  ** read or throw IOErr if end of stream is reached first.  If n is
  ** null then the entire contents of this input stream are piped.  If
  ** close is true, then this input stream is guaranteed to be closed
  ** upon return (the OutStream is never closed).  Return the number
  ** of bytes piped to the output stream.
  **
  Int pipe(OutStream out, Int? n := null, Bool close := true)

}

**************************************************************************
** SysInStream
**************************************************************************

internal class SysInStream : InStream
{
  override Int? read()
  override Int? readBuf(Buf buf, Int n)
  override This unread(Int n)
  override Bool close()
}