//
// System.Xml.XmlParserInput
//
// Author:
//	Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
//
//	(C)2003 Atsushi Enomoto
//

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Globalization;

namespace System.Xml
{
	internal class XmlParserInput
	{
		#region ctor
		public XmlParserInput (TextReader reader, string baseURI)
			: this (reader, baseURI, 1, 1)
		{
		}

		public XmlParserInput (TextReader reader, string baseURI, int line, int column)
		{
			this.reader = reader;

			StreamReader sr = reader as StreamReader;
			if (sr != null)
				can_seek = sr.BaseStream.CanSeek;

			this.line = line;
			this.column = column;
			this.baseURI = baseURI;
		}
		#endregion

		#region Public Methods
		// Read the next character and compare it against the
		// specified character.
		public void Close ()
		{
			this.reader.Close ();
		}

		public void Expect (int expected)
		{
			int ch = ReadChar ();

			if (ch != expected) {
				throw ReaderError (
					String.Format (CultureInfo.InvariantCulture, 
						"expected '{0}' ({1:X}) but found '{2}' ({3:X})",
						(char)expected,
						expected,
						(char)ch,
						ch));
			}
		}

		public void Expect (string expected)
		{
			int len = expected.Length;
			for(int i=0; i< len; i++)
				Expect (expected[i]);
		}

		public void InsertParameterEntityBuffer (string value)
		{
			this.peBuffer.Insert (peBufferIndex, ' ');
			this.peBuffer.Insert (peBufferIndex + 1, value);
			this.peBuffer.Insert (peBufferIndex + value.Length + 1, ' ');
			peStored = true;
		}

		public int PeekChar ()
		{
			if (peStored)
				return peBuffer [peBufferIndex];

			if (has_peek)
				return peek_char;

			peek_char = reader.Read ();
			if (peek_char >= 0xD800 && peek_char <= 0xDBFF) {
				int i = reader.Read ();
				if (i >= 0xDC00 && i <= 0xDFFF)
					peek_char += i;
			}
			has_peek = true;
			return peek_char;
		}

		public int ReadChar ()
		{
			int ch;

			if (peStored) {
				ch = peBuffer [peBufferIndex];
				peBufferIndex++;
				if (peBufferIndex == peBuffer.Length) {
					peStored = false;
					peBuffer.Length = 0;
					peBufferIndex = 0;
				}
				// I decided not to add character to currentTag with respect to PERef value
				return ch;
			}

			if (has_peek) {
				ch = peek_char;
				has_peek = false;
			} else {
				ch = reader.Read ();
				if (ch >= 0xD800 && ch <= 0xDBFF) {
					int i = reader.Read ();
					if (i > 0xDC00 && i <= 0xDFFF)
						ch += i;
				}
			}

			if (ch == '\n') {
				line++;
				column = 1;
			} else {
				column++;
			}
			return ch;
		}

		#endregion

		#region Public Properties
		public string BaseURI {
			get { return baseURI; }
		}

		public bool HasPEBuffer {
			get { return peStored; }
		}
		
		public int LineNumber {
			get { return line; }
		}

		public int LinePosition {
			get { return column; }
		}

		public bool InitialState {
			get { return initialState; }
			set { initialState = value; }
		}

		#endregion

		#region Privates
		TextReader reader;
		bool can_seek;
		bool has_peek;
		int peek_char;
		int line;
		int column;
		StringBuilder peBuffer = new StringBuilder ();
		string baseURI;
		bool peStored = false;
		bool initialState = true;
		int peBufferIndex;

		private int ParseCharReference (string name)
		{
			int ret = -1;
			if (name.Length > 0 && name [0] == '#') {
				if (name [1] == 'x')
					ret = int.Parse (name.Substring (2, name.Length - 2), NumberStyles.None & NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
				else
					ret = int.Parse (name.Substring (1, name.Length - 1), CultureInfo.InvariantCulture);
			}
			return ret;
		}

		private int ParseKnownEntityReference (string name)
		{
			switch (name) {
			case "quot": return '"';
			case "lt": return '<';
			case "gt": return '>';
			case "amp": return '&';
			case "apos": return '\'';
			}
			return -1;
		}

		private XmlException ReaderError (string message)
		{
			return new XmlException (message, null, line, column);
		}
		#endregion
	}
}
