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


/// <summary>
/// Hermetyzuje większość funkcji ADO.NEt potrzebnych
/// przez klasy dostępu do danych
/// </summary>
public abstract class AbstractDA
{

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

   /// <summary>
   /// Definiuje podstawową instrukcję wybierającą, która służy do pobierania danych
   /// bez żadnych kryteriów.
   /// 
   /// Ta właściwośc jest implementowana przez konkretne podklasy.
   /// </summary>
   protected abstract string SelectStatement
   {
      get;
   }

   /// <summary>
   /// Tworzy obiekt klasy DbParameter niezależny od dostawcy
   /// </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 klasy 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 klasy 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 połączenie niezależne od dostawcy
         DbConnection conn = Factory.CreateConnection();
         conn.ConnectionString = ConnectionSetting.ConnectionString;

         // utwórz dapter danych niezależny od dostawcy
         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 nową tabelę z danymi
         dt = new DataTable();

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

      }
      catch (Exception ex)
      {
         // każdy błąd będzie obsługiwany przez niestandardową funkcję obsługi wyjątków
         DataAccessExceptionHandler.HandleException(ex.Message);
      }

      // Zwróć obiekt klasy DataTable 
      return dt;
   }

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

         // utwórz polecenie niezależne od dostawcy
         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)
         {
            // każdy błąd będzie obsługiwany przez niestandardową funkcję 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 połączenie niezależne od dostawcy
      using (DbConnection conn = Factory.CreateConnection())
      {
         conn.ConnectionString = ConnectionSetting.ConnectionString;

         // utwórz polecenie niezależne od dostawcy
         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)
         {
            // każdy błąd będzie obsługiwany przez niestandardową funkcję obsługi wyjątków
            DataAccessExceptionHandler.HandleException(ex.Message);
         }
         return scalar;
      }
   }

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

   /// <summary>
   /// Właściwośc prywatna, która zwraca odpowiedni obiekt klasy DbProviderFactory
   /// </summary>
   private DbProviderFactory Factory
   {
      get
      {
         string connString = ConnectionSetting.ConnectionString;
         string invariantName = ConnectionSetting.ProviderName;

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

         // pobierz odpowiednią fabrykę dostawców
         return DbProviderFactories.GetFactory(invariantName);
      }
   }
}
