//  =================================================
//  The stream reader class reads characters from the
//  underlying stream.
//
//  The Read() member function reads a character from
//  the stream and returns it as an int.
//  If end of stream is encountered it returns -1.
//
//  The Peek() member function returns the next
//  available character but does not consume it.
//  If end of stream is encountered it returns -1.
//
//  The ReadLine() member function reads a line of
//  characters and returns it as a string.
//  If end of stream is encountered it returns null.
//
//  The ReadToEnd() member function reads characters
//  from the current position to the end of the
//  stream and returns them as a string.
//  =================================================

using System;
using System.Text;
using System.Unicode;

namespace System.IO
{
    public class StreamReader : Closable
    {
        public StreamReader(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("provided stream is null");
            }
            this.stream = stream;
            this.decoder = new Utf8Decoder(stream);
            this.buffered = false;
            this.buffer = cast<uint>(-1);
        }
        public void Close()
        {
            stream.Close();
        }
        public int Read()
        {
            uint x = Get(false);
            if (x == cast<uint>(-1))
            {
                return -1;
            }
            return cast<int>(x);
        }
        public int Peek()
        {
            uint x = Get(true);
            if (x == cast<uint>(-1))
            {
                return -1;
            }
            return cast<int>(x);
        }
        public string ReadLine()
        {
            int x = Read();
            if (x == -1)
            {
                return null;
            }
            StringBuilder b = new StringBuilder();
            int state = 0;
            while (x != -1)
            {
                char c = cast<char>(x);
                switch (state)
                {
                    case 0: 
                    {
                        if (c == '\r')
                        {
                            state = 1;
                        }
                        else if (c == '\n')
                        {
                            return b.ToString();
                        }
                        else
                        {
                            b.Append(c);
                        }
                        break;
                    }
                    case 1:
                    {
                        if (c == '\n')
                        {
                            return b.ToString();
                        }
                        else if (c == '\r')
                        {
                            PutBack('\r');
                            return b.ToString();
                        }
                        else
                        {
                            PutBack(c);
                            return b.ToString();
                        }
                        state = 0;
                        break;
                    }
                }
                x = Read();
            }
            return b.ToString();
        }
        public string ReadToEnd()
        {
            StringBuilder b = new StringBuilder();
            int x = Read();
            while (x != -1)
            {
                b.Append(cast<char>(x));
                x = Read();
            }
            return b.ToString();
        }
        public void PutBack(char x)
        {
            buffered = true;
            buffer = cast<uint>(x);
        }
        public Stream ContainedStream
        {
            get { return stream; }
        }
        private uint Get(bool peek)
        {
            if (buffered)
            {
                if (!peek)
                {
                    buffered = false;
                }
                return buffer;
            }
            else
            {
                uint x = decoder.Decode();
                if (peek)
                {
                    buffer = x;
                    buffered = true;
                }
                return x;
            }
        }
        private Stream stream;
        private Utf8Decoder decoder;
        private bool buffered;
        private uint buffer;
    }
}