using System;
using System.Data;

namespace DotNetMock.Framework.Data
{
	/// <summary>
	/// This is a Mock object that implements the IDataParameter interface.
	/// </summary>
	public class MockDataParameter : MockObject, IDataParameter, IDbDataParameter
	{
		private ExpectationBool _nullable = new ExpectationBool("MockDataParameter.IsNullable");
		private ExpectationString _parameterName = new ExpectationString("MockDataParameter.ParameterName");
		private ExpectationValue _parameterValue = new ExpectationValue("MockDataParameter.Value");
		private ExpectationString _parameterSourceColumn = new ExpectationString("MockDataParameter.SourceColumn");
		private ExpectationValue _parameterType = new ExpectationValue("MockDataParameter.Type");

		#region Constructors
		/// <summary>
		/// Default, Empty Constructor
		/// </summary>
		public MockDataParameter() {}
		/// <summary>
		/// Initializes a new MockDataParameter with the supplied name and of the supplied type
		/// </summary>
		/// <param name="name">Name for new parameter</param>
		/// <param name="newType">Type for new parameter</param>
		public MockDataParameter(string name, DbType newType)
		{
			_parameterName.Actual = name;
			_parameterType.Actual = newType;
		}
		/// <summary>
		/// Initializes a new MockDataParameter with the supplied name and value
		/// </summary>
		/// <param name="name">Name for the new parameter</param>
		/// <param name="value">Value for the new parameter</param>
		public MockDataParameter(string name, Object value) 
		{
			_parameterName.Actual = name;
			_parameterValue.Actual = value;
			_parameterType.Actual = inferType(value);
		}
		/// <summary>
		/// Initializes a new MockDataParameter with supplied name, and of the supplied type, corresponding to the 
		/// supplied column
		/// </summary>
		/// <param name="name">Name for the new parameter</param>
		/// <param name="newType">Type for new parameter</param>
		/// <param name="sourceColumn">Corresponding column for the parameter</param>
		public MockDataParameter(string name, DbType newType, string sourceColumn)
		{
			_parameterName.Actual = name;
			_parameterType.Actual = newType;
			_parameterSourceColumn.Actual = sourceColumn;
		}
		#endregion

		#region Mock Methods
		/// <summary>
		/// Sets expected parameter name
		/// </summary>
		/// <param name="name">Parameter name to expect</param>
		public void SetExpectedName(string name)
		{
			_parameterName.Expected = name;
		}
		/// <summary>
		/// Sets expected parameter value
		/// </summary>
		/// <param name="value">Value to expect</param>
		public void SetExpectedValue(Object value)
		{
			_parameterValue.Expected = value;
		}
		/// <summary>
		/// Sets IsNullable or not
		/// </summary>
		/// <param name="nullable">True/False</param>
		public void SetExpectedNullable(bool nullable)
		{
			_nullable.Expected = nullable;
		}
		/// <summary>
		/// Sets expected parameter type
		/// </summary>
		/// <param name="expectedType">Type to expect</param>
		public void SetExpectedType(DbType expectedType)
		{
			_parameterType.Expected = expectedType;
		}
		/// <summary>
		/// Sets expected source column
		/// </summary>
		/// <param name="column">Expected source column</param>
		public void SetExpectedSourceColumn(string column)
		{
			_parameterSourceColumn.Expected = column;
		}
		#endregion

		#region Implementation of IDataParameter
		
		// TODO: Implement complete directions: Input, Output, InputOutput, and ReturnValue
		/// <summary>
		/// Parameter Direction
		/// </summary>
		public System.Data.ParameterDirection Direction
		{
			get
			{
				return new System.Data.ParameterDirection();
			}
			set
			{
			}
		}
		/// <summary>
		/// Paramter Type
		/// </summary>
		public System.Data.DbType DbType
		{
			get
			{
				if (_parameterType.Actual == null) 
				{
					return DbType.String;
				} 
				else 
				{
					return (DbType)_parameterType.Actual;
				}
			}
			set
			{
				_parameterType.Actual = value;
			}
		}
		/// <summary>
		/// Parameter Value
		/// </summary>
		public object Value
		{
			get
			{
				if (_parameterValue.Actual == null) 
				{
					return "";
				}
				return _parameterValue.Actual;
			}
			set
			{
				_parameterValue.Actual = value;
				if (_parameterType.Actual == null) 
				{
					_parameterType.Actual = inferType(value);
				}
			}
		}
		/// <summary>
		/// Is Nullable
		/// </summary>
		public bool IsNullable
		{
			set
			{
				_nullable.Actual = value;
			}
			get
			{
				return _nullable.Actual;
			}
		}
		// TODO: Implement different DataRowVersion information: Current, Proposed, Default, and Original
		/// <summary>
		/// Parameter Source Version to use
		/// </summary>
		public System.Data.DataRowVersion SourceVersion
		{
			get
			{
				return DataRowVersion.Current;
			}
			set
			{

			}
		}
		/// <summary>
		/// Paramter Name
		/// </summary>
		public string ParameterName
		{
			get
			{
				return _parameterName.Actual;
			}
			set
			{
				_parameterName.Actual = value;
			}
		}
		/// <summary>
		/// Source Column for the parameter
		/// </summary>
		public string SourceColumn
		{
			get
			{
				if ( _parameterSourceColumn.Actual == null) 
				{
					return "";
				}
				return _parameterSourceColumn.Actual;
			}
			set
			{
				if (value == null) 
				{
					_parameterSourceColumn.Actual = "";
				}
				_parameterSourceColumn.Actual = value;
			}
		}
		#endregion

		#region IDbDataParameter Members

		/// <summary>
		/// <see cref="IDbDataParameter.Precision"/>
		/// </summary>
		public byte Precision
		{
			get
			{
				// TODO:  Add MockDataParameter.Precision getter implementation
				return 0;
			}
			set
			{
				// TODO:  Add MockDataParameter.Precision setter implementation
			}
		}

		/// <summary>
		/// <see cref="IDbDataParameter.Scale"/>
		/// </summary>
		public byte Scale
		{
			get
			{
				// TODO:  Add MockDataParameter.Scale getter implementation
				return 0;
			}
			set
			{
				// TODO:  Add MockDataParameter.Scale setter implementation
			}
		}

		/// <summary>
		/// <see cref="IDbDataParameter.Size"/>
		/// </summary>
		public int Size
		{
			get
			{
				// TODO:  Add MockDataParameter.Size getter implementation
				return 0;
			}
			set
			{
				// TODO:  Add MockDataParameter.Size setter implementation
			}
		}

		#endregion

		#region Private Helper Methods
		/// <summary>
		/// Infers corresponding DbType from the given value
		/// </summary>
		/// <param name="value">Value to use</param>
		/// <returns>Inferred DbType from given value</returns>
		private DbType inferType(Object value)
		{
			switch (Type.GetTypeCode(value.GetType()))
			{
				case TypeCode.Object:
					return DbType.Object;

				case TypeCode.Empty:
				case TypeCode.DBNull:
				case TypeCode.Char:
				case TypeCode.SByte:
				case TypeCode.UInt16:
				case TypeCode.UInt32:
				case TypeCode.UInt64:
					throw new ApplicationException("Unsupported data type");

				case TypeCode.Boolean:
					return DbType.Boolean;

				case TypeCode.Byte:
					return DbType.Byte;

				case TypeCode.Int16:
					return DbType.Int16;

				case TypeCode.Int32:
					return DbType.Int32;

				case TypeCode.Int64:
					return DbType.Int64;

				case TypeCode.Single:
					return DbType.Single;

				case TypeCode.Double:
					return DbType.Double;

				case TypeCode.Decimal:
					return DbType.Decimal;

				case TypeCode.DateTime:
					return DbType.DateTime;

				case TypeCode.String:
					return DbType.String;

				default:
					throw new ApplicationException("Value is of unknown data type");
			}
		}

		#endregion
	}
}
