context: //
  // System.Data.OleDb.OleDbDataReader
  //
  // Author:
  //   Rodrigo Moya (rodrigo@ximian.com)
  //   Tim Coleman (tim@timcoleman.com)
  //
  // Copyright (C) Rodrigo Moya, 2002
  // Copyright (C) Tim Coleman, 2002
  // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
  //
  // 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.Collections;
  using System.ComponentModel;
  using System.Data.Common;
  using System.Runtime.InteropServices;
  
  namespace System.Data.OleDb
  {
  #if NET_2_0
  	public sealed class OleDbDataReader : DbDataReader, IDisposable
  #else
  	public sealed class OleDbDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
  #endif
  	{
  		#region Fields
  		
  		private OleDbCommand command;
  		private bool open;
  		private ArrayList gdaResults;
  		private int currentResult;
  		private int currentRow;
  		private bool disposed;
  
  		#endregion
  
  		#region Constructors
  
  		internal OleDbDataReader (OleDbCommand command, ArrayList results) 
  		{
  			this.command = command;
  			open = true;
  			if (results != null)
  				gdaResults = results;
  			else
  				gdaResults = new ArrayList ();
  			currentResult = -1;
  			currentRow = -1;
  		}
  
  		#endregion
  
  		#region Properties
  
  		public
  #if NET_2_0
  		override
  #endif
  		int Depth {
  			get {
  				return 0; // no nested selects supported
  			}
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		int FieldCount {
  			get {
  				if (currentResult < 0 || currentResult >= gdaResults.Count)
  					return 0;
  
  				return libgda.gda_data_model_get_n_columns (
  					(IntPtr) gdaResults[currentResult]);
  			}
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		bool IsClosed {
  			get {
  				return !open;
  			}
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		object this[string name] {
  			get {
  				int pos;
  
  				if (currentResult == -1)
  					throw new InvalidOperationException ();
  
  				pos = libgda.gda_data_model_get_column_position (
  					(IntPtr) gdaResults[currentResult],
  					name);
  				if (pos == -1)
  					throw new IndexOutOfRangeException ();
  
  				return this[pos];
  			}
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		object this[int index] {
  			get {
  				return (object) GetValue (index);
  			}
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		int RecordsAffected {
  			get {
  				int total_rows;
  				
  				if (currentResult < 0 || currentResult >= gdaResults.Count)
  					return 0;
  
  				total_rows = libgda.gda_data_model_get_n_rows (
  					(IntPtr) gdaResults[currentResult]);
  				if (total_rows > 0) {
  					if (FieldCount > 0) {
  						// It's a SELECT statement
  						return -1;
  					}
  				}
  
  				return FieldCount > 0 ? -1 : total_rows;
  			}
  		}
  		
  		[MonoTODO]
  		public
  #if NET_2_0
  		override
  #endif
  		bool HasRows {
  			get {
  				throw new NotImplementedException ();
  			}
  		}
  
  #if NET_2_0
  		[MonoTODO]
  		public override int VisibleFieldCount {
  			get {
  				throw new NotImplementedException ();
  			}
  		}
  #endif
  
  		#endregion
  
  		#region Methods
  
  		public
  #if NET_2_0
  		override
  #endif
  		void Close ()
  		{
  			for (int i = 0; i < gdaResults.Count; i++) {
  				IntPtr obj = (IntPtr) gdaResults[i];
  				libgda.FreeObject (obj);
  			}
  
  			gdaResults.Clear ();
  			gdaResults = null;
  			
  			open = false;
  			currentResult = -1;
  			currentRow = -1;
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		bool GetBoolean (int ordinal)
  		{
  			IntPtr value;
  
  			if (currentResult == -1)
  				throw new InvalidCastException ();
  
  			value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
  								    ordinal, currentRow);
  			if (value == IntPtr.Zero)
  				throw new InvalidCastException ();
  			
  			if (libgda.gda_value_get_type (value) != GdaValueType.Boolean)
  				throw new InvalidCastException ();
  			return libgda.gda_value_get_boolean (value);
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		byte GetByte (int ordinal)
  		{
  			IntPtr value;
  
  			if (currentResult == -1)
  				throw new InvalidCastException ();
  
  			value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
  								    ordinal, currentRow);
  			if (value == IntPtr.Zero)
  				throw new InvalidCastException ();
  			
  			if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
  				throw new InvalidCastException ();
  			return libgda.gda_value_get_tinyint (value);
  		}
  
  		[MonoTODO]
  		public
  #if NET_2_0
  		override
  #endif
  		long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
  		{
  			throw new NotImplementedException ();
  		}
  		
  		[EditorBrowsableAttribute (EditorBrowsableState.Never)]
  		public
  #if NET_2_0
  		override
  #endif
  		char GetChar (int ordinal)
  		{
  			IntPtr value;
  
  			if (currentResult == -1)
  				throw new InvalidCastException ();
  
  			value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
  				ordinal, currentRow);
  			if (value == IntPtr.Zero)
  				throw new InvalidCastException ();
  			
  			if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
  				throw new InvalidCastException ();
  			return (char) libgda.gda_value_get_tinyint (value);
  		}
  
  		[MonoTODO]
  		public
  #if NET_2_0
  		override
  #endif
  		long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
  		{
  			throw new NotImplementedException ();
  		}
  
  		[MonoTODO]
  #if NET_2_0
  		[EditorBrowsable (EditorBrowsableState.Advanced)]
  #endif
  		public new OleDbDataReader GetData (int ordinal)
  		{
  			throw new NotImplementedException ();
  		}
  
  #if NET_2_0
  		protected override DbDataReader GetDbDataReader (int ordinal)
  		{
  			return this.GetData (ordinal);
  		}
  #endif
  
  		public
  #if NET_2_0
  		override
  #endif
  		string GetDataTypeName (int index)
  		{
  			IntPtr attrs;
  			GdaValueType type;
  
  			if (currentResult == -1)
  				return "unknown";
  
  			
  			attrs = libgda.gda_data_model_describe_column ((IntPtr) gdaResults[currentResult],
  				index);
  			if (attrs == IntPtr.Zero)
  				return "unknown";
  
  			type = libgda.gda_field_attributes_get_gdatype (attrs);
  			libgda.gda_field_attributes_free (attrs);
  			
  			return libgda.gda_type_to_string (type);
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		DateTime GetDateTime (int ordinal)
  		{
  			IntPtr value;
  
  			if (currentResult == -1)
  				throw new InvalidCastException ();
  
  			value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
  				ordinal, currentRow);
  			if (value == IntPtr.Zero)
  				throw new InvalidCastException ();
  			
  			if (libgda.gda_value_get_type (value) == GdaValueType.Date) {
  				GdaDate gdt;
  
  				gdt = (GdaDate) Marshal.PtrToStructure (libgda.gda_value_get_date (value),
  									typeof (GdaDate));
  				return new DateTime ((int) gdt.year, (int) gdt.month, (int) gdt.day);
  			} else if (libgda.gda_value_get_type (value) == GdaValueType.Time) {
  				GdaTime gdt;
  
  				gdt = (GdaTime) Marshal.PtrToStructure (libgda.gda_value_get_time (value),
  									typeof (GdaTime));
  				return new DateTime (0, 0, 0, (int) gdt.hour, (int) gdt.minute, (int) gdt.second, 0);
  			} else if (libgda.gda_value_get_type (value) == GdaValueType.Timestamp) {
  				GdaTimestamp gdt;
  				
  				gdt = (GdaTimestamp) Marshal.PtrToStructure (libgda.gda_value_get_timestamp (value),
  					typeof (GdaTimestamp));
  
  				return new DateTime ((int) gdt.year, (int) gdt.month, (int) gdt.day,
  					(int) gdt.hour, (int) gdt.minute, (int) gdt.second,
  					(int) gdt.fraction);
  			}
  
  			throw new InvalidCastException ();
  		}
  
  		[MonoTODO]
  		public
  #if NET_2_0
  		override
  #endif
  		decimal GetDecimal (int ordinal)
  		{
  			throw new NotImplementedException ();
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		double GetDouble (int ordinal)
  		{
  			IntPtr value;
  
  			if (currentResult == -1)
  				throw new InvalidCastException ();
  
  			value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
  								    ordinal, currentRow);
  			if (value == IntPtr.Zero)
  				throw new InvalidCastException ();
  			
  			if (libgda.gda_value_get_type (value) != GdaValueType.Double)
  				throw new InvalidCastException ();
  			return libgda.gda_value_get_double (value);
  		}
  
  		public
  #if NET_2_0
  		override
  #endif
  		Type GetFieldType (int index)
  		{
  			IntPtr value;
  			GdaValueType type;
  
  			if (currentResult == -1)
  				throw new IndexOutOfRangeException ();
  
  			value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
  				index, currentRow);
  			if (value == IntPtr.Zero)
  
gt: 				throw new IndexOutOfRangeException ();