
//
// 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.Collections;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace IBM.Data.DB2
{

	internal sealed class DB2OpenConnection : IDisposable
	{
		private IntPtr dbHandle = IntPtr.Zero;

		private DB2ConnectionSettings settings;
		private bool disposed = false;
		internal DateTime poolDisposalTime; // time to live used for disposal of connections in the connection pool
		public bool transactionOpen;
		public bool autoCommit = true;
		private string		databaseProductName;
		private string		databaseVersion;
		private int			majorVersion;
		private int			minorVersion;

		public IntPtr DBHandle
		{
			get { return dbHandle; }
		}
		public string DatabaseProductName
		{
			get { return databaseProductName; }
		}
		public string DatabaseVersion
		{
			get { return databaseVersion; }
		}
		public int MajorVersion
		{
			get { return majorVersion; }
		}
		public int MinorVersion
		{
			get { return minorVersion; }
		}

		public DB2OpenConnection(DB2ConnectionSettings settings, DB2Connection connection)
		{
			this.settings = settings;
			try
			{
				short sqlRet = DB2CLIWrapper.SQLAllocHandle(DB2Constants.SQL_HANDLE_DBC, DB2Environment.Instance.penvHandle, out dbHandle);
				DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_DBC, DB2Environment.Instance.penvHandle, "Unable to allocate database handle in DB2Connection.", connection);

				if(settings.Server.Length > 0)
				{
					StringBuilder outConnectStr = new StringBuilder(60);  // TODO: ????
					short numOutCharsReturned;

					sqlRet = DB2CLIWrapper.SQLDriverConnect(dbHandle, IntPtr.Zero, 
						settings.ConnectionString,	(short)settings.ConnectionString.Length,
						outConnectStr,				(short)outConnectStr.Length, out numOutCharsReturned, 
						DB2Constants.SQL_DRIVER_NOPROMPT /*SQL_DRIVER_COMPLETE*/);
				}
				else
				{
					sqlRet = DB2CLIWrapper.SQLConnect(dbHandle, 
						settings.DatabaseAlias,	(short)settings.DatabaseAlias.Length, 
						settings.UserName,		(short)settings.UserName.Length,
						settings.PassWord,		(short)settings.PassWord.Length);
					DB2ClientUtils.DB2CheckReturn(sqlRet, DB2Constants.SQL_HANDLE_DBC, dbHandle, "Unable to connect to the database.", connection);
				}

				if((settings.Pool == null) || (settings.Pool.databaseProductName == null))
				{
					StringBuilder sb = new StringBuilder(256);
					short stringLength;
					sqlRet = DB2CLIWrapper.SQLGetInfo(dbHandle, /*SQL_DBMS_NAME*/17, sb, (short)(sb.Capacity / 2), out stringLength);
					new DB2ErrorCollection(DB2Constants.SQL_HANDLE_DBC, dbHandle).ToString();
					if(sqlRet == 0)
						databaseProductName = sb.ToString(0, Math.Min(sb.Capacity, stringLength / 2));
					sqlRet = DB2CLIWrapper.SQLGetInfo(dbHandle, /*SQL_DBMS_VER*/18, sb, (short)(sb.Capacity / 2), out stringLength);
					if(sqlRet == 0)
					{
						databaseVersion = sb.ToString(0, Math.Min(sb.Capacity, stringLength / 2));
						try
						{
							string[] splitVersion = databaseVersion.Split('.');
							majorVersion = int.Parse(splitVersion[0]);
							minorVersion = int.Parse(splitVersion[1]);
						}
						catch{}
					}
					if(settings.Pool != null)
					{
						settings.Pool.databaseProductName = databaseProductName;
						settings.Pool.databaseVersion	  = databaseVersion;
						settings.Pool.majorVersion		  = majorVersion;
						settings.Pool.minorVersion		  = minorVersion;
					}
				}
				else if(settings.Pool != null)
				{
					if(settings.Pool != null)
					{
						databaseProductName = settings.Pool.databaseProductName;
						databaseVersion		= settings.Pool.databaseVersion;
						majorVersion		= settings.Pool.majorVersion;
						minorVersion		= settings.Pool.minorVersion;
					}
				}
			}
			catch
			{
				if(dbHandle != IntPtr.Zero)
				{
					DB2CLIWrapper.SQLFreeHandle(DB2Constants.SQL_HANDLE_DBC, dbHandle);
					dbHandle = IntPtr.Zero;
				}
				throw;
			}
		}

		public void RollbackDeadTransaction()
		{
			DB2CLIWrapper.SQLEndTran(DB2Constants.SQL_HANDLE_DBC, DBHandle, DB2Constants.SQL_ROLLBACK);
			transactionOpen = false;
		}

		public void Close()
		{
			if(transactionOpen)
				RollbackDeadTransaction();

			if(settings.Pool != null)
			{
				settings.Pool.AddToFreeConnections(this);
			}
			else
			{
				Dispose();
			}
		}

		private void FreeHandles()
		{
			if(dbHandle != IntPtr.Zero)
			{
				short sqlRet = DB2CLIWrapper.SQLDisconnect(dbHandle);
				// Note that SQLDisconnect() automatically drops any statements and
				// descriptors open on the connection.
				sqlRet = DB2CLIWrapper.SQLFreeHandle(DB2Constants.SQL_HANDLE_DBC, dbHandle);

				dbHandle = IntPtr.Zero;
			}
		}

		#region IDisposable Members

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			if(!disposed) 
			{
				if(disposing)
				{
					// dispose managed resources
				}
				FreeHandles();
			}
			disposed = true;
		}


		~DB2OpenConnection()
		{
			if(settings.Pool != null)
			{
				settings.Pool.OpenConnectionFinalized();
			}
			Dispose(false);
		}
		#endregion
	}


}