// 
// OciStatementHandle.cs 
//  
// Part of managed C#/.NET library System.Data.OracleClient.dll
//
// Part of the Mono class libraries at
// mcs/class/System.Data.OracleClient/System.Data.OracleClient.Oci
//
// Assembly: System.Data.OracleClient.dll
// Namespace: System.Data.OracleClient.Oci
// 
// Author: 
//     Tim Coleman <tim@timcoleman.com>
//     Daniel Morgan <danielmorgan@verizon.net>
//         
// Copyright (C) Tim Coleman, 2003
// Copyright (C) Daniel Morgan, 2005
// 

using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace System.Data.OracleClient.Oci {
	internal sealed class OciStatementHandle : OciHandle, IDisposable
	{
		#region Fields

		int columnCount;
		bool disposed = false;
		OciErrorHandle errorHandle;
		bool moreResults;
		OciServiceHandle serviceHandle;
		ArrayList values;
		ArrayList parm;
		OracleCommand command;
	
		#endregion // Fields

		#region Constructors

		public OciStatementHandle (OciHandle parent, IntPtr handle)
			: base (OciHandleType.Statement, parent, handle)
		{
			moreResults = false;
		}

		#endregion // Constructors

		#region Properties

		public int ColumnCount {
			get { return columnCount; }
		}

		public OciErrorHandle ErrorHandle {
			get { return errorHandle; }
			set { errorHandle = value; }
		}

		public OciServiceHandle Service {
			get { return serviceHandle; }
			set { serviceHandle = value; }
		}
		
		public ArrayList Values {
			get { return values; }
		}

		public OracleCommand Command {
			get { return command; }
			set { command = value; }
		}

		#endregion // Properties

		#region Methods
		
		protected override void Dispose (bool disposing)
		{
			if (!disposed) {
				disposed = true;
				
				if (disposing) {
					if (values != null) {
						foreach (OciDefineHandle h in values) 
							h.Dispose ();
						values = null;
					}
				}
				base.Dispose (disposing);
			}
		}

		public OciParameterDescriptor GetParameter (int position)
		{
			IntPtr handle = IntPtr.Zero;
			int status = 0;

			status = OciCalls.OCIParamGet (this,
						OciHandleType.Statement,
						ErrorHandle,
						out handle,
						position + 1);

			if (status != 0) {
				OciErrorInfo info = ErrorHandle.HandleError ();
				throw new OracleException (info.ErrorCode, info.ErrorMessage);
			}

			OciParameterDescriptor output = new OciParameterDescriptor (this, handle);
			output.ErrorHandle = ErrorHandle;
			if (parm == null)
				parm = new ArrayList();
			parm.Add(handle);
			return output;
		}

		public OciDefineHandle GetDefineHandle (int position, OracleConnection connection)
		{
			OciDefineHandle defineHandle = new OciDefineHandle (this, IntPtr.Zero);
			defineHandle.ErrorHandle = ErrorHandle;
			defineHandle.DefineByPosition (position, connection);

			return defineHandle;
		}

		void Define ()
		{
			Define (command.Connection);
		}

		void Define (OracleConnection connection) 
		{
			values = new ArrayList ();
			for (int i = 0; i < columnCount; i += 1)  
				values.Add (GetDefineHandle (i, connection));
		}

		public bool ExecuteQuery (bool schemaOnly)
		{
			return Execute (false, false, schemaOnly);
		}

		public bool ExecuteNonQuery (bool useAutoCommit)
		{
			return Execute (true, useAutoCommit, false);
		}

		public bool Execute (bool nonQuery, bool useAutoCommit, bool schemaOnly)
		{
			int status = 0;
			columnCount = 0;
			moreResults = false;
			int executeMode;

			if( useAutoCommit)
				executeMode = (int)OciExecuteMode.CommitOnSuccess;
			else {
				if (schemaOnly)
					executeMode = (int)OciExecuteMode.DescribeOnly;
				else
					executeMode = (int)OciExecuteMode.Default;
			}

			if (this.disposed) 
			{
				throw new InvalidOperationException ("StatementHandle is already disposed.");
			}

			status = OciCalls.OCIStmtExecute (Service,
				Handle,
				ErrorHandle,
				nonQuery,
				0,
				IntPtr.Zero,
				IntPtr.Zero,
				(OciExecuteMode)executeMode);

			switch (status) {
			case OciGlue.OCI_DEFAULT:
				if (!nonQuery) {
					GetColumnCount ();
					Define ();
					moreResults = true;
				}
				break;
			case OciGlue.OCI_NO_DATA:
				break;
			case OciGlue.OCI_INVALID_HANDLE:
				throw new OracleException (0, "Invalid handle.");
			default:
				OciErrorInfo info = ErrorHandle.HandleError ();
				throw new OracleException (info.ErrorCode, info.ErrorMessage);
			}
			return true;
		}
		
		internal void SetupRefCursorResult (OracleConnection connection) 
		{
			GetColumnCount ();
			Define (connection);
			moreResults = true;
		}

		void GetColumnCount ()
		{
			columnCount = GetAttributeInt32 (OciAttributeType.ParameterCount, ErrorHandle);
		}

		public OciStatementType GetStatementType ()
		{
			return (OciStatementType) GetAttributeUInt16 (OciAttributeType.StatementType, ErrorHandle);
		}

		public bool Fetch ()
		{
			int status = 0;

			if (this.disposed) 
			{
				throw new InvalidOperationException ("StatementHandle is already disposed.");
			}

			status = OciCalls.OCIStmtFetch (Handle,
				ErrorHandle.Handle,
				1,
				2,
				0);
		
			switch (status) {
			case OciGlue.OCI_NO_DATA:
				moreResults = false;
				foreach (IntPtr h in parm)
					OciCalls.OCIDescriptorFree(h, OciHandleType.Parameter);
				break;
			case OciGlue.OCI_DEFAULT:
				moreResults = true;
				break;
			case OciGlue.OCI_SUCCESS_WITH_INFO:
				//OciErrorInfo ei = ErrorHandle.HandleError ();
				//command.Connection.CreateInfoMessage (ei);
				moreResults = true;
				break;
			default:
				OciErrorInfo info = ErrorHandle.HandleError ();
				throw new OracleException (info.ErrorCode, info.ErrorMessage);
			}

			return moreResults;
		}

		public void Prepare (string commandText)
		{
			int status = 0;

			if (this.disposed) {
				throw new InvalidOperationException ("StatementHandle is already disposed.");
			}

			int rsize = 0;
			byte [] buffer;
			
			// Get size of buffer
			OciCalls.OCIUnicodeToCharSet (Parent, null, commandText, out rsize);
			
			// Fill buffer
			buffer = new byte[rsize];
			OciCalls.OCIUnicodeToCharSet (Parent, buffer, commandText, out rsize);

			// Execute statement
			status = OciCalls.OCIStmtPrepare (this,
				ErrorHandle,
				buffer,
				buffer.Length,
				OciStatementLanguage.NTV,
				OciStatementMode.Default);

			if (status != 0) {
				OciErrorInfo info = ErrorHandle.HandleError ();
				throw new OracleException (info.ErrorCode, info.ErrorMessage);
			}
		}

		#endregion // Methods
	}
}
