using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Data.Common;
using System.Data;

namespace Sdaleo
{
    /// <summary>
    /// Serves as our main Class for interacting with the DB
    /// </summary>
    public class DB
    {
        private IConnectDb _ConnectionStrObj = null;
        private DbConnection _DbConnection = null;

        /// <summary>
        /// Static DB Object Creator
        /// </summary>
        /// <param name="ConnectionStrObj">pass a valid Connection String Object</param>
        /// <returns>a new DB Object</returns>
        public static DB Create(IConnectDb ConnectionStrObj)
        {
            return new DB(ConnectionStrObj);
        }

        #region Construction 

        /// <summary>
        /// Construct out DB Class with a valid Connection String Obj
        /// </summary>
        /// <param name="IConnectString">pass a valid Connection String Object</param>
        public DB(IConnectDb ConnectionStrObj)
        {
            if(ConnectionStrObj == null || !ConnectionStrObj.IsValid)
               throw new Exception("Invalid Connection Object Passed in.");

            _ConnectionStrObj = ConnectionStrObj;
        }

        /// <summary>
        /// Private Constructor * Used for Transaction Processing *
        /// </summary>       
        /// <param name="dbConnection">A valid Connection Object that is in a Transaction State</param>
        private DB(DbConnection dbConnection)
        {
            if (dbConnection == null)
                throw new Exception("Invalid Connection Passed in.");            
        }

        #endregion

        #region NonQuery

         /// <summary>
        /// Use this to Run SQL with the ExecuteNonQuery() Command Object
        /// </summary>
        /// <param name="SqlCommandText">SQL Text/Commands to execute</param>
        /// <returns>the result of the ExecuteNonQuery() operation</returns>
        public DBRetVal ExecuteNonQuery(string SqlCommandText)
        {
            return ExecuteNonQuery(SqlCommandText, CommandType.Text);
        }

        /// <summary>
        /// Use this to Run SQL with the ExecuteNonQuery() Command Object
        /// </summary>
        /// <param name="SqlCommandText">SQL Text/Commands to execute</param>
        /// <returns>the result of the ExecuteNonQuery() operation</returns>
        public DBRetVal ExecuteNonQuery(string SqlCommandText, CommandType type)
        {
            DBRetVal retVal = new DBRetVal();
            try
            {
                using (DbConnection conn = DBMS.CreateDbConnection(_ConnectionStrObj.DBType, _ConnectionStrObj.ConnectionString))
                {
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = SqlCommandText;
                        cmd.CommandType = type;
                        if(_ConnectionStrObj.SupportsTimeouts)
                           cmd.CommandTimeout = (int) _ConnectionStrObj.Timeouts.CommandTimeout;
                        int nRows = cmd.ExecuteNonQuery();
                        retVal.SetNonQueryRetVal(nRows);
                    }
                }
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e);                
            }
            return retVal;
        }

        /// <summary>
        /// Use this to Run SQL with the ExecuteNonQuery() Command Object
        /// </summary>
        /// <param name="SqlCommandText">SQL Text/Commands to execute</param>
        /// <param name="parameters">Allows the use of DBMS Independent parameters</param>
        /// <returns>the result of the ExecuteNonQuery() operation</returns>
        public DBRetVal ExecuteNonQuery(string SqlCommandText, DBMSIndParameter[] parameters)
        {
            return ExecuteNonQuery(SqlCommandText, CommandType.Text);
        }

        /// <summary>
        /// Use this to Run SQL with the ExecuteNonQuery() Command Object
        /// </summary>
        /// <param name="SqlCommandText">SQL Text/Commands to execute</param>
        /// <param name="parameters">Allows the use of DBMS Independent parameters</param>
        /// <returns>the result of the ExecuteNonQuery() operation</returns>
        public DBRetVal ExecuteNonQuery(string SqlCommandText, DBMSIndParameter[] parameters, CommandType type)
        {
            DBRetVal retVal = new DBRetVal();
            try
            {
                using (DbConnection conn = DBMS.CreateDbConnection(_ConnectionStrObj.DBType, _ConnectionStrObj.ConnectionString))
                {
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = SqlCommandText;
                        cmd.CommandType = type;
                        if (_ConnectionStrObj.SupportsTimeouts)
                            cmd.CommandTimeout = (int)_ConnectionStrObj.Timeouts.CommandTimeout;                        

                        // Add DBMS Specific Parameters
                        foreach (DbParameter parameter in DBMS.CreateDbParameter(_ConnectionStrObj.DBType, parameters))
                                 cmd.Parameters.Add(parameter);

                        int nRows = cmd.ExecuteNonQuery();
                        retVal.SetNonQueryRetVal(nRows);
                    }
                }
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e); 
            }
            return retVal;
        }

        #endregion

        #region ExecuteScalar

        /// <summary>
        /// Use this to Run SQL with the ExecuteScalar() Command Object
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <returns>the result of the ExecuteScalar() operation</returns>
        public DBRetVal ExecuteScalar(string SqlText)
        {
            return ExecuteScalar(SqlText, CommandType.Text);
        }

        /// <summary>
        /// Use this to Run SQL with the ExecuteScalar() Command Object
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <returns>the result of the ExecuteScalar() operation</returns>
        public DBRetVal ExecuteScalar(string SqlText, CommandType type)
        {
            DBRetVal retVal = new DBRetVal();
            try
            {
                using (DbConnection conn = DBMS.CreateDbConnection(_ConnectionStrObj.DBType, _ConnectionStrObj.ConnectionString))
                {                    
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = SqlText;
                        cmd.CommandType = type;
                        if (_ConnectionStrObj.SupportsTimeouts)
                            cmd.CommandTimeout = (int)_ConnectionStrObj.Timeouts.CommandTimeout;                        
                        object oResult = cmd.ExecuteScalar();
                        retVal.SetScalarRetVal(oResult);
                    }
                }
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e);                
            }
            return retVal;
        }

        /// <summary>
        /// Use this to Run SQL with the ExecuteScalar() Command Object
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <param name="parameters">Allows the use of DBMS Independent parameters</param>
        /// <returns>the result of the ExecuteScalar() operation</returns>
        public DBRetVal ExecuteScalar(string SqlText, DBMSIndParameter[] parameters)
        {
            return ExecuteScalar(SqlText, parameters, CommandType.Text);
        }

        /// <summary>
        /// Use this to Run SQL with the ExecuteScalar() Command Object
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <param name="parameters">Allows the use of DBMS Independent parameters</param>
        /// <returns>the result of the ExecuteScalar() operation</returns>
        public DBRetVal ExecuteScalar(string SqlText, DBMSIndParameter[] parameters, CommandType type)
        {
            DBRetVal retVal = new DBRetVal();            
            try
            {
                using (DbConnection conn = DBMS.CreateDbConnection(_ConnectionStrObj.DBType, _ConnectionStrObj.ConnectionString))
                {
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = SqlText;
                        cmd.CommandType = type;
                        if (_ConnectionStrObj.SupportsTimeouts)
                            cmd.CommandTimeout = (int)_ConnectionStrObj.Timeouts.CommandTimeout;                        

                        // Add DBMS Specific Parameters
                        foreach (DbParameter parameter in DBMS.CreateDbParameter(_ConnectionStrObj.DBType, parameters))
                            cmd.Parameters.Add(parameter);

                        object oResult = cmd.ExecuteScalar();
                        retVal.SetScalarRetVal(oResult);
                    }
                }
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e);
            }
            return retVal;
        }

        #endregion

        #region DataTable

          /// <summary>
        /// Use this to Run SQL with the DataAdapter Fill()
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <returns>the result of the DataAdapter Fill() operation</returns>
        public DBRetVal FillDataTable(string SqlText)
        {
            return FillDataTable(SqlText, CommandType.Text);
        }

        /// <summary>
        /// Use this to Run SQL with the DataAdapter Fill()
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <returns>the result of the DataAdapter Fill() operation</returns>
        public DBRetVal FillDataTable(string SqlText, CommandType type)
        {
            DBRetVal retVal = new DBRetVal();            
            try
            {
                using (DbConnection conn = DBMS.CreateDbConnection(_ConnectionStrObj.DBType, _ConnectionStrObj.ConnectionString))
                {
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = SqlText;
                        cmd.CommandType = type;
                        if (_ConnectionStrObj.SupportsTimeouts)
                            cmd.CommandTimeout = (int)_ConnectionStrObj.Timeouts.CommandTimeout;

                        using (DbDataAdapter dataAdapter = DBMS.CreateDbDataAdapter(_ConnectionStrObj.DBType, cmd))
                        {
                            DataTable dt = new DataTable();
                            dataAdapter.Fill(dt);
                            retVal.SetDataTableRetVal(dt);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e);
            }
            return retVal;
        }

         /// <summary>
        /// Use this to Run SQL with the DataAdapter Fill()
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <param name="parameters">Allows the use of DBMS Independent parameters</param>
        /// <returns>the result of the DataAdapter Fill() operation</returns>
        public DBRetVal FillDataTable(string SqlText, DBMSIndParameter[] parameters)
        {
            return FillDataTable(SqlText, parameters, CommandType.Text);
        }

        /// <summary>
        /// Use this to Run SQL with the DataAdapter Fill()
        /// </summary>
        /// <param name="SqlText">SQL Text to execute</param>
        /// <param name="parameters">Allows the use of DBMS Independent parameters</param>
        /// <returns>the result of the DataAdapter Fill() operation</returns>
        public DBRetVal FillDataTable(string SqlText, DBMSIndParameter[] parameters, CommandType type)
        {
            DBRetVal retVal = new DBRetVal();
            try
            {
                using (DbConnection conn = DBMS.CreateDbConnection(_ConnectionStrObj.DBType, _ConnectionStrObj.ConnectionString))
                {
                    conn.Open();
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = SqlText;
                        cmd.CommandType = type;
                        if (_ConnectionStrObj.SupportsTimeouts)
                            cmd.CommandTimeout = (int)_ConnectionStrObj.Timeouts.CommandTimeout;

                        // Add DBMS Specific Parameters
                        foreach (DbParameter parameter in DBMS.CreateDbParameter(_ConnectionStrObj.DBType, parameters))
                            cmd.Parameters.Add(parameter);

                        using (DbDataAdapter dataAdapter = DBMS.CreateDbDataAdapter(_ConnectionStrObj.DBType, cmd))
                        {
                            DataTable dt = new DataTable(); 
                            dataAdapter.Fill(dt);
                            retVal.SetDataTableRetVal(dt);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e);
            }
            return retVal;
        }

        #endregion

        #region Transaction

        /// <summary>
        /// Use this to run multiple DB Steps and group them into a Single Transaction
        /// </summary>
        /// <param name="transactionSteps">an array of Transaction Steps to execute</param>
        /// <returns>the result of Transaction</returns>
        public DBRetVal StartTransaction(TransactionStep[] transactionSteps)
        {
            DBRetVal retVal = new DBRetVal();
            try
            {
                DB db = new DB(_ConnectionStrObj);

                // Enter Transaction

                foreach (TransactionStep step in transactionSteps)
                {
                    // Iterate thru each Transaction Step-By-Step
                    retVal = step(retVal, db);

                    // Stop The Transaction
                    if (retVal.ErrorOccured)  // Have to Leave the Transaction as well
                        break;
                }

                // Leave Transaction
            }
            catch (Exception e)
            {
                retVal = DBMS.CreateErrorDBRetVal(_ConnectionStrObj.DBType, e);
            }
            return retVal;
        }

        #endregion
    }
}
