using System;
using System.Data;
using System.Configuration;
using System.Data.Common;
using System.Web.Configuration;

namespace TwoLayer.DataAccess
{
   /// <summary>
   /// Hermetyzuje większość funkcji środowiska ADO.NEt potrzebnych
   /// przez klasy dostępu do danych
   /// </summary>
   public abstract class AbstractDA
   {
      /// <summary>
      /// Definiuje podstawową instrukcję pobierania danych
      /// bez kryteriów.
      /// 
      /// Ta właściwość jest zaimplementowana przez konkretne podklasy.
      /// </summary>
      protected abstract string SelectStatement
      {
         get;
      }

      /// <summary>
      /// Zwraca tabelę z danymi zawierającą informacje o tabeli wydawcy dla określonego identyfikatora.
      /// Ten zbiór danych będzie zawierać 0 lub 1 wiersz danych.
      /// </summary>
      public abstract DataTable GetById(object oId);

      /// <summary>
      /// Zwraca wszystkie dane dla źródła danych
      /// </summary>
      public DataTable GetAll()
      {
         string sql = SelectStatement;
         return GetDataTable(sql, null);
      }

      /// <summary>
      /// Utwórz niezależy od dostawcy obiekt DbParameter
      /// </summary>
      protected DbParameter MakeParameter(string name, object value, DbType paramType)
      {
         DbParameter param = Factory.CreateParameter();
         param.ParameterName = name;
         param.Value = value;
         param.DbType = paramType;
         return param;
      }

      /// <summary>
      /// Zwraca obiekt DataTable wypełniony danymi określonymi przez sql i parametry
      /// </summary>
      protected DataTable GetDataTable(string sql, DbParameter[] parameters)
      {
         return GetDataTable(sql, CommandType.Text, parameters);
      }

      /// <summary>
      /// Zwraca obiekt DataTable wypełniony danymi określonymi przez nazwę i parametry
      /// </summary>
      protected DataTable GetDataTable(string name, CommandType cmdType, DbParameter[] parameters)
      {
         DataTable dt = null;
         try
         {
            // utwórz niezależne od dostawcy połączenie
            DbConnection conn = Factory.CreateConnection();
            conn.ConnectionString = ConnectionSetting.ConnectionString;

            // utwórz niezależny od dostawcy adapter danych
            DbDataAdapter adapter = Factory.CreateDataAdapter();

            DbCommand cmd = Factory.CreateCommand();
            cmd.Connection = conn;
            cmd.CommandText = name;
            cmd.CommandType = cmdType;
            adapter.SelectCommand = cmd; ;

            // dodaj parametry
            if (parameters != null)
            {
               foreach (DbParameter p in parameters)
                  adapter.SelectCommand.Parameters.Add(p);
            }

            // utwórz tabelę danych
            dt = new DataTable();

            // wypełnij nową tabelę danymi pochodzącymi z adaptera
            adapter.Fill(dt);

         }
         catch (Exception ex)
         {
            // wszystkie błędy będą obsługiwane przez niestandardową procedurę obsługi wyjątków
            DataAccessExceptionHandler.HandleException(ex.Message);
         }

         // Zwróć obiekt DataTable 
         return dt;
      }

      /// <summary>
      /// Uruchamia zapytanie SQL, które nie zwraca danych
      /// </summary>
      protected void RunNonQuery(string sql, DbParameter[] parameters)
      {
         // utwórz niezależne od dostawcy połączenie
         using (DbConnection conn = Factory.CreateConnection())
         {
            conn.ConnectionString = ConnectionSetting.ConnectionString;

            // utwórz niezależną od dostawcy instrukcję
            DbCommand cmd = Factory.CreateCommand();
            cmd.Connection = conn;
            cmd.CommandText = sql;

            // dodaj parametry
            if (parameters != null)
            {
               foreach (DbParameter p in parameters)
                  cmd.Parameters.Add(p);
            }

            try
            {
               conn.Open();
               cmd.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
               // wszystkie błędy będą obsługiwane przez niestandardową procedurę obsługi wyjątków
               DataAccessExceptionHandler.HandleException(ex.Message);
            }
         }
      }

      /// <summary>
      /// Uruchamia skalar zwracający zapytanie SQL
      /// </summary>
      protected object RunScalar(string sql, DbParameter[] parameters)
      {
         object scalar = null;

         // utwórz niezależne od dostawcy połączenie
         using (DbConnection conn = Factory.CreateConnection())
         {
            conn.ConnectionString = ConnectionSetting.ConnectionString;

            // utwórz niezależną od dostawcy instrukcję
            DbCommand cmd = Factory.CreateCommand();
            cmd.Connection = conn;
            cmd.CommandText = sql;

            // dodaj parametry
            if (parameters != null)
            {
               foreach (DbParameter p in parameters)
                  cmd.Parameters.Add(p);
            }

            try
            {
               conn.Open();
               scalar = cmd.ExecuteScalar();
            }
            catch (Exception ex)
            {
               // wszystkie błędy będą obsługiwane przez niestandardową procedurę obsługi wyjątków
               DataAccessExceptionHandler.HandleException(ex.Message);
            }
            return scalar;
         }
      }

      /// <summary>
      /// Właściwość prywatna, która zwraca ustawienia łańcucha połączenia z pliku web.config
      /// </summary>
      private ConnectionStringSettings ConnectionSetting
      {
         get
         {
            return WebConfigurationManager.ConnectionStrings["BookCatalog"];

         }
      }

      /// <summary>
      /// Właściwość prywatna uruchamiająca odpowiednią DbProviderFactory
      /// </summary>
      private DbProviderFactory Factory
      {
         get
         {
            string connString = ConnectionSetting.ConnectionString;
            string invariantName = ConnectionSetting.ProviderName;

            // sprawdź, czy nazwa tego dostawcy jest obsługiwana
            DataTable providers = DbProviderFactories.GetFactoryClasses();
            DataRow[] foundArray = providers.Select("InvariantName='" + invariantName + "'");
            if (foundArray.Length == 0)
            {
               // wszystkie błędy będą obsługiwane przez niestandardową procedurę obsługi wyjątków
               string msg = "[AbstractDA] Data Provider " + invariantName + " not found";
               DataAccessExceptionHandler.HandleException(msg);
            }

            // pobierz odbowiednią fabrykę dostawcy
            return DbProviderFactories.GetFactory(invariantName);
         }
      }
   }
}