// Copyright (C) 2004 MySQL AB
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as published by
// the Free Software Foundation
//
// There are special exceptions to the terms and conditions of the GPL 
// as it is applied to this software. View the full text of the 
// exception in file EXCEPTIONS in the directory of this software 
// distribution.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
using System;
using System.Data;
using MySql.Data.MySqlClient;
namespace MySql.Data.Types
{
	/// 
	/// Summary description for MySqlDateTime.
	/// 
	public class MySqlDateTime : MySqlValue, IConvertible
	{
		private DateTime	mValue;
		private bool		isZero;
		internal MySqlDateTime() 
		{
			dbType = DbType.DateTime;
			mySqlDbType = MySqlDbType.Datetime;
		}
		internal MySqlDateTime(MySqlDbType type) : this()
		{
			if (type == MySqlDbType.Date)
				dbType = DbType.Date;
			else
				dbType = DbType.DateTime;
			mySqlDbType = type;
		}
		internal MySqlDateTime(DateTime val, MySqlDbType type) : this(type)
		{
			mValue = val;
		}
		/// Indicates if this value is zero
		public bool IsZero 
		{ 
			get { return isZero; }
		}
		internal override void SerializeBinary(PacketWriter writer)
		{
			Packet p = new Packet(false);
			if (mySqlDbType == MySqlDbType.Timestamp)
				writer.WriteByte( 11 );
			else
				writer.WriteByte( 7 );
			writer.WriteInteger( mValue.Year, 2 );
			writer.WriteByte( (byte)mValue.Month );
			writer.WriteByte( (byte)mValue.Day );
			if (mySqlDbType == MySqlDbType.Date) 
			{
				writer.WriteByte( 0 );
				writer.WriteByte( 0 );
				writer.WriteByte( 0 );
			}
			else 
			{
				writer.WriteByte( (byte)mValue.Hour );
				writer.WriteByte( (byte)mValue.Minute  );
				writer.WriteByte( (byte)mValue.Second );
			}
			
			if (mySqlDbType == MySqlDbType.Timestamp)
				writer.WriteInteger( mValue.Millisecond, 4 );
		}
		/// Returns this value as byte[]
		public override byte[] ToBytes()
		{
			Packet p = new Packet(false);
			byte len = 11;
			if (mySqlDbType == MySqlDbType.Date)
				len = 7;
			p.WriteByte( len );
			p.WriteInteger( mValue.Year, 2 );
			p.WriteByte( (byte)mValue.Month );
			p.WriteByte( (byte)mValue.Day );
			if (mySqlDbType == MySqlDbType.Date) 
			{
				p.WriteByte( 0 );
				p.WriteByte( 0 );
				p.WriteByte( 0 );
			}
			else 
			{
				p.WriteByte( (byte)mValue.Hour );
				p.WriteByte( (byte)mValue.Minute  );
				p.WriteByte( (byte)mValue.Second );
			}
			
			if (mySqlDbType == MySqlDbType.Timestamp)
				p.WriteInteger( mValue.Millisecond, 4 );
			byte[] buff = new byte[len+1]; 
			Array.Copy( p.GetBuffer(), 0, buff, 0, len+1 );
			return buff;
		}
		internal override DbType DbType 
		{
			get 
			{ 
				switch (mySqlDbType) 
				{
					case MySqlDbType.Date:			
					case MySqlDbType.Newdate:
						return DbType.Date;
				}
				return DbType.DateTime;
			}
		}
		internal override string GetMySqlTypeName()
		{
			switch (mySqlDbType) 
			{
				case MySqlDbType.Date: return "DATE";
				case MySqlDbType.Newdate: return "NEWDATE";
				case MySqlDbType.Timestamp: return "TIMESTAMP";
			}
			return "DATETIME";
		}
		internal override object ValueAsObject
		{
			get { return mValue; }
			set { mValue = Convert.ToDateTime(value); }
		}
		/// Returns this value as a DateTime
		public DateTime Value
		{
			get { return mValue; }
			set { mValue = value; } 
		}
		/// Parses a string into this value
		/// 
		public override void Parse(string s)
		{
			mValue = DateTime.Parse(s);
		}
		internal void ParseMySql( string s, bool is41 ) 
		{
			isZero = false;
			switch (mySqlDbType) 
			{
				case MySqlDbType.Date:
					if (s == "0000-00-00") isZero = true;
					else mValue = DateTime.ParseExact( s, "yyyy-MM-dd", System.Globalization.DateTimeFormatInfo.InvariantInfo );
					break;
				case MySqlDbType.Datetime:
					if (s == "0000-00-00 00:00:00") isZero = true;
					else mValue = DateTime.ParseExact( s, "yyyy-MM-dd HH:mm:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo );
					break;
				case MySqlDbType.Timestamp:
					// MySql 4.1.0 and later use DateTime format for timestamp
					if (is41)
					{
						if (s == "0000-00-00 00:00:00") isZero = true;
						else mValue = DateTime.ParseExact( s, "yyyy-MM-dd HH:mm:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo );
						return;
					}
					string pattern;
					switch (s.Length) 
					{
						case 2:  pattern = "yy"; break;
						case 4:  pattern = "yyMM"; break;
						case 6:  pattern = "yyMMdd"; break;
						case 8:  pattern = "yyyyMMdd"; break;
						case 10: pattern = "yyMMddHHmm"; break;
						case 12: pattern = "yyMMddHHmmss"; break;
						case 14: 
						default: pattern = "yyyyMMddHHmmss"; break;
					}
					if (s.Replace("0","").Length == 0) isZero = true;
					else
						mValue = DateTime.ParseExact( s, pattern, System.Globalization.DateTimeFormatInfo.InvariantInfo );
					break;
			}
		}
		internal override Type SystemType
		{
			get { return typeof(DateTime); }
		}
		internal override long SetBinaryData(Packet packet)
		{
			long bufLength = 0;
			bufLength = packet.ReadByte();
			if (bufLength == 0) 
				IsNull = true;
			else if (bufLength == 4)
				mValue = new DateTime( packet.ReadInteger(2), packet.ReadByte(), 
					packet.ReadByte() );
			else if (bufLength == 7)
				mValue = new DateTime( packet.ReadInteger(2), packet.ReadByte(), 
					packet.ReadByte(), packet.ReadByte(), packet.ReadByte(),
					packet.ReadByte() );
			else if (bufLength == 11)
				mValue = new DateTime( packet.ReadInteger(2), packet.ReadByte(), 
					packet.ReadByte(), packet.ReadByte(), packet.ReadByte(),
					packet.ReadByte(), packet.ReadInteger(4) / 1000000 );
			return bufLength;
		}
		/// Returns a MySQL specific string representation of this value
		public override string ToString()
		{
			if (mySqlDbType == MySqlDbType.Date)
				return IsZero ? "0000-00-00" : Value.ToString("yyyy-MM-dd");
			return IsZero ? "0000-00-00 00:00:00" : Value.ToString( "yyyy-MM-dd HH:mm:ss" );
		}
		internal string ToMySqlString(bool is41) 
		{
			switch (mySqlDbType) 
			{
				case MySqlDbType.Date:
					return mValue.ToString("yyyy-MM-dd");
				case MySqlDbType.Datetime:
					return mValue.ToString("yyyy-MM-dd HH:mm:ss");
				case MySqlDbType.Timestamp:
					if (is41)
						return mValue.ToString("yyyy-MM-dd HH:mm:ss");
					else
						return mValue.ToString("yyyyMMddHHmmss");
			}
			return mValue.ToString();
		}
		#region IConvertible Members
		ulong IConvertible.ToUInt64(IFormatProvider provider)
		{
			return Convert.ToUInt64( mValue );
		}
		sbyte IConvertible.ToSByte(IFormatProvider provider)
		{
			return Convert.ToSByte( mValue );
		}
		double IConvertible.ToDouble(IFormatProvider provider)
		{
			return Convert.ToDouble( mValue );
		}
		DateTime IConvertible.ToDateTime(IFormatProvider provider)
		{
			return mValue;
		}
		float IConvertible.ToSingle(IFormatProvider provider)
		{
			return Convert.ToSingle( mValue );
		}
		bool IConvertible.ToBoolean(IFormatProvider provider)
		{
			return Convert.ToBoolean( mValue );
		}
		int IConvertible.ToInt32(IFormatProvider provider)
		{
			return Convert.ToInt32( mValue );
		}
		ushort IConvertible.ToUInt16(IFormatProvider provider)
		{
			return Convert.ToUInt16( mValue );
		}
		short IConvertible.ToInt16(IFormatProvider provider)
		{
			return Convert.ToInt16( mValue );
		}
		string IConvertible.ToString(IFormatProvider provider)
		{
			return ToString();
		}
		byte IConvertible.ToByte(IFormatProvider provider)
		{
			return Convert.ToByte( mValue );
		}
		char IConvertible.ToChar(IFormatProvider provider)
		{
			return Convert.ToChar( mValue );
		}
		long IConvertible.ToInt64(IFormatProvider provider)
		{
			return Convert.ToInt64( mValue );
		}
		TypeCode IConvertible.GetTypeCode()
		{
			return TypeCode.Object;
		}
		decimal IConvertible.ToDecimal(IFormatProvider provider)
		{
			return Convert.ToDecimal( mValue );
		}
		object IConvertible.ToType(Type conversionType, IFormatProvider provider)
		{
			return null;
		}
		uint IConvertible.ToUInt32(IFormatProvider provider)
		{
			return Convert.ToUInt32( mValue );
		}
		#endregion
	}
}