using System;
using System.Data;
using System.Configuration;
using System.Reflection;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Generic;
using System.Web.Configuration;
using Sky.Darkness.Commons.Util;
using System.Text;

namespace Sky.Darkness.Commons.DataAccess
{
    /// <summary>
    /// GenericDAO 的摘要说明
    /// 泛型DAO,SQL实现
    /// </summary>
    /// <typeparam name="T">持久化Model类</typeparam>
    /// <typeparam name="ID">持久化Model类的主键类型</typeparam>
    public class GenericDaoSql<T,ID>
    {
        #region 泛型持久化类及所操作表的相关操作
        // 持久化表主键常量
        private const string PRIMARY_KEY_NAME = "PrimaryKeyName";
        // 默认持久化表主键名称
        private const string PrimaryKeyName = "id";

        /// <summary>
        /// 获取泛型持久化类的类型
        /// </summary>
        /// <returns></returns>
        public Type GetGenericType()
        {
            return typeof(GenericDaoSql<T, ID>).GetGenericArguments()[0];
        }

        /// <summary>
        /// 获取泛型持久化类的名称
        /// </summary>
        /// <returns></returns>
        public string GetGenericTypeName()
        {
            return GetGenericType().Name;
        }

        /// <summary>
        /// 获取持久化数据库表的表名
        /// </summary>
        /// <returns></returns>
        public virtual string GetTableName()
        {
            return GetGenericTypeName();
        }

        /// <summary>
        /// 获取持久化类的所有公共属性（Property）
        /// </summary>
        /// <returns></returns>
        public PropertyInfo[] GetGenericTypeProperties()
        {
            return GetGenericType().GetProperties();
        }
        
        /// <summary>
        /// 获取持久化数据库表的主键
        /// </summary>
        /// <returns></returns>
        public virtual string GetPrimaryKeyName()
        {
            string primaryKeyName = PrimaryKeyName;

            Type gt = GetGenericType();
            FieldInfo pkInfo = gt.GetField(PRIMARY_KEY_NAME);
            if (pkInfo != null)
            {
                primaryKeyName = (string)pkInfo.GetValue(Activator.CreateInstance(gt));
            }
            return primaryKeyName;
        }

        /// <summary>
        /// 获取持久化数据库表的主键方法GetPrimaryKeyName()的简写
        /// </summary>
        /// <returns></returns>
        public string GetPrimaryKey()
        {
            return GetPrimaryKeyName();
        }

        /// <summary>
        /// 获取持久化数据库表主键的类型
        /// </summary>
        /// <returns></returns>
        public Type GetPrimaryKeyType()
        {
            Type gt = GetGenericType();
            FieldInfo pkInfo = gt.GetField(PRIMARY_KEY_NAME);
            return pkInfo.GetType();
        }
        #endregion 泛型持久化类及所操作表的相关操作

        #region 查询方法集
        /// <summary>
        /// 根据sql语句查询出所有数据
        /// </summary>
        /// <remarks>System.Byte[]类型的属性暂不赋值，有时间再考虑怎么弄</remarks>
        /// <param name="strSql"></param>
        /// <returns></returns>
        public List<T> SelectAll(string strSql)
        {
            // Create object collection
            List<T> colObjects = new List<T>();

            // Create command
            SqlCommand cmd = SqlHelper.CreateSqlCommand(strSql);

            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    PropertyInfo[] properties = GetGenericTypeProperties();

                    object obj = Activator.CreateInstance(GetGenericType());
                    for (int i = 0; i < properties.Length; i++)
                    {
                        Type propertyType = properties[i].PropertyType;
                        // System.Byte[]类型的属性暂不赋值，有时间在考虑怎么弄
                        if (propertyType.Equals(Type.GetType("System.Byte[]")))
                        {
                            properties[i].SetValue(obj, null, null);
                        }

                        if (reader[properties[i].Name] == System.DBNull.Value)
                        {
                            continue;
                        }
                        else
                        {
                            properties[i].SetValue(obj, reader[properties[i].Name], null);
                        }

                    }

                    colObjects.Add((T)obj);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                cmd.Connection.Close();
                cmd.Dispose();
            }
            return colObjects;
        }

        /// <summary>
        /// Selects all objects from the database
        /// </summary>
        public List<T> SelectAll()
        {
            return SelectAll("SELECT * FROM " + GetTableName());
        }

        /// <summary>
        /// Select all objects from the database by id primary key
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public List<T> SelectAllById(ID id)
        {
            string strId = id.ToString();
            if (id.GetType().Equals(Type.GetType("System.String")))
            {
                strId = "'" + id + "'";
            }
            return SelectAll("SELECT * FROM " + GetTableName() + " WHERE " + GetPrimaryKey() + "=" + strId);
        }

        /// <summary>
        /// Select all data into DataSet
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public DataSet SelectAllDataSetById(ID id)
        {
            string strId = id.ToString();
            strId = MyString.GetSqlString(strId,id.GetType());
            string strSql = "SELECT * FROM " + GetTableName() + " WHERE " + GetPrimaryKey() + "=" + strId;
            return SqlHelper.FillDataSet(strSql);
        }

        /// <summary>
        /// Select all data into DataSet
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public DataSet SelectAllDataSet()
        {
            string strSql = "SELECT * FROM " + GetTableName();
            return SqlHelper.FillDataSet(strSql);
        }

        /// <summary>
        /// Select all data into DataSet
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public DataSet SelectAllDataSet(Hashtable wheres)
        {
            string strSql = "SELECT * FROM " + GetTableName() + BuildHashtableToWhereString(wheres);
            return SqlHelper.FillDataSet(strSql);
        }

        /// <summary>
        /// Select all data into DataSet
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public DataSet SelectAllDataSetByLike(Hashtable wheres,Hashtable orderBy)
        {
            string strSql = "SELECT * FROM " + GetTableName() 
                + " WHERE 1=2 " + BuildHashtableToOrLikeString(wheres)
                + " ORDER BY " + BuildHashtableToOrderByString(orderBy);
            return SqlHelper.FillDataSet(strSql);
        }

        /// <summary>
        /// Select all objects by where condition
        /// </summary>
        /// <param name="wheres"></param>
        /// <returns></returns>
        public List<T> SelectAllByProperties(Hashtable wheres)
        {
            return SelectAll("SELECT * FROM " + GetTableName() + BuildHashtableToWhereString(wheres));
        }

        /// <summary>
        /// Select prperty list by where condition
        /// </summary>
        /// <typeparam name="D"></typeparam>
        /// <param name="property"></param>
        /// <param name="wheres"></param>
        /// <returns></returns>
        public List<D> SelectAllByProperties<D>(String property,Hashtable wheres)
        {
            string strSql = "SELECT " + property + " FROM " + GetTableName() + BuildHashtableToWhereString(wheres);
           
            // Create object collection
            List<D> colObjects = new List<D>();
            
            // Create command
            SqlCommand cmd = SqlHelper.CreateSqlCommand(strSql);

            try
            {
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    colObjects.Add((D)reader[property]);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                cmd.Connection.Close();
                cmd.Dispose();
            }
            return colObjects;
        }

        /// <summary>
        /// 辅助方法，将Hashtable里面的数据变成where条件的形式
        /// </summary>
        /// <param name="wheres"></param>
        /// <returns></returns>
        protected string BuildHashtableToWhereString(Hashtable wheres)
        {
            return " WHERE 1=1 " + BuildHashtableToAndString(wheres);
        }

        /// <summary>
        /// 辅助方法，将Hashtable里面的数据变成where条件的形式,不含Where
        /// </summary>
        /// <param name="wheres"></param>
        /// <returns></returns>
        protected string BuildHashtableToAndString(Hashtable wheres)
        {
            StringBuilder strWhere = new StringBuilder();
            foreach (object key in wheres.Keys)
            {
                object value = wheres[key];
                strWhere.Append(" AND ")
                        .Append(key)
                        .Append("=")
                        .Append(MyString.GetSqlString(value, value.GetType()));
            }
            return strWhere.ToString();
        }

        /// <summary>
        /// 辅助方法，将Hashtable里面的数据变成where条件的形式,不含Where
        /// </summary>
        /// <param name="wheres"></param>
        /// <returns></returns>
        protected string BuildHashtableToOrLikeString(Hashtable wheres)
        {
            StringBuilder strWhere = new StringBuilder();
            foreach (object key in wheres.Keys)
            {
                object value = wheres[key];
                strWhere.Append(" OR ")
                        .Append(key)
                        .Append(" LIKE %")
                        .Append(MyString.GetSqlString(value, value.GetType()))
                        .Append("%");
            }
            return strWhere.ToString();
        }

        /// <summary>
        /// 辅助方法，将Hashtable里面的数据变成Order By条件的形式
        /// </summary>
        /// <param name="wheres"></param>
        /// <returns></returns>
        protected string BuildHashtableToOrderByString(Hashtable wheres)
        {
            StringBuilder strWhere = new StringBuilder();
            foreach (object key in wheres.Keys)
            {
                object value = wheres[key];
                strWhere.Append(" ")
                        .Append(key)
                        .Append(" ")
                        .Append(value)
                        .Append(",");
            }
            return strWhere.ToString(0,strWhere.Length-1);
        }

        /// <summary>
        /// Selects all objects from the database
        /// </summary>
        /// <param name="start">start point</param>
        /// <param name="limit">the total count you need to search</param>
        /// <returns></returns>
        public DataSet SearchPagingDataSet(int start, int limit)
        {
            string commandText = " SELECT TOP " + limit + " * FROM " + GetTableName()
                               + " WHERE " + GetPrimaryKey() + " NOT IN("
                               + " SELECT TOP " + start + " " + GetPrimaryKey() + " FROM " + GetTableName() 
                               + " ORDER BY " + GetPrimaryKey() + " DESC) ORDER BY " + GetPrimaryKey() + " DESC";

            return SqlHelper.FillDataSet(commandText);
        }

        /// <summary>
        /// Selects all objects from the database
        /// </summary>
        /// <param name="start">start point</param>
        /// <param name="limit">the total count you need to search</param>
        /// <returns></returns>
        public DataSet SearchPagingDataSet(int start, int limit,Hashtable orderBy)
        {
            string strOrderBy = BuildHashtableToOrderByString(orderBy);
            StringBuilder commandText = new StringBuilder();
            commandText.Append(" SELECT TOP ")
                       .Append(limit)
                       .Append(" * FROM ")
                       .Append(GetTableName())
                       .Append(" WHERE ")
                       .Append(GetPrimaryKey())
                       .Append(" NOT IN(")
                       .Append(" SELECT TOP ")
                       .Append(start)
                       .Append(" ")
                       .Append(GetPrimaryKey())
                       .Append(" FROM ")
                       .Append(GetTableName())
                       .Append(" ORDER BY ")
                       .Append(strOrderBy)
                       .Append(" ) ORDER BY ")
                       .Append(strOrderBy)
                       .Append(" ");

            return SqlHelper.FillDataSet(commandText.ToString());
        }

        /// <summary>
        /// Selects all objects from the database
        /// </summary>
        /// <param name="start">start point</param>
        /// <param name="limit">the total count you need to search</param>
        /// <returns></returns>
        public List<T> SearchPaging(int start, int limit)
        {
            // Create object collection
            List<T> colObjects = new List<T>();

            // Create command
            SqlCommand cmd = null;
            string commandText = " SELECT TOP " + limit + " * FROM " + GetTableName()
                               + " WHERE " + GetPrimaryKey() + " NOT IN("
                               + " SELECT TOP " + start + " " + GetPrimaryKey() + " FROM " + GetTableName() 
                               + " ORDER BY " + GetPrimaryKey() + " DESC) ORDER BY " + GetPrimaryKey() + " DESC";

            try
            {
                cmd = SqlHelper.CreateSqlCommand(commandText);
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    PropertyInfo[] properties = GetGenericTypeProperties();       

                    object obj = Activator.CreateInstance(GetGenericType());
                    for (int i = 0; i < properties.Length; i++)
                    {
                        Type propertyType = properties[i].PropertyType;
                        if (propertyType.Equals(Type.GetType("System.Byte[]")))
                        {
                            properties[i].SetValue(obj, null, null);
                        }

                        if (reader[properties[i].Name] == System.DBNull.Value)
                        {
                            continue;
                        }
                        else
                        {
                            properties[i].SetValue(obj, reader[properties[i].Name], null);
                        }
                        
                    }

                    colObjects.Add((T)obj);
                }
            }catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                cmd.Connection.Close();
                cmd.Dispose();
            }
            return colObjects;
        }

        /// <summary>
        /// Get all data's count from database
        /// </summary>
        /// <returns></returns>
        public int GetAllCount()
        {
            string strSql = "SELECT COUNT(*) FROM " + GetTableName();
            return (int)SqlHelper.ExecuteScalar(strSql);
        }

        /// <summary>
        /// Get all data's count from database
        /// </summary>
        /// <returns></returns>
        public int GetAllCount(Hashtable wheres)
        {
            string strWhere = " WHERE 1=1 ";
            foreach (object key in wheres.Keys)
            {
                object value = wheres[key];
                strWhere += " AND " + key + "=" + MyString.GetSqlString(value, value.GetType());
            }
            string strSql = "SELECT COUNT(*) FROM " + GetTableName() + strWhere;
            return (int)SqlHelper.ExecuteScalar(strSql);
        }
        #endregion 查询方法集

        #region Insert Method
        /// <summary>
        /// Inserts a new object into the database
        /// </summary>
        /// <param name="obj">the object to insert</param>
        public int Insert(T obj)
        {
            int count = 0;

            Type type = obj.GetType();
            string commandText = "INSERT " + GetTableName() + " (";
            PropertyInfo[] properties = type.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                //if (GetPrimaryKey().Equals(property.Name, StringComparison.CurrentCultureIgnoreCase))
                //    continue;

                commandText += property.Name + ",";
            }
            commandText = commandText.Substring(0,commandText.Length-1);
            commandText += ") VALUES (";
            foreach (PropertyInfo property in properties)
            {
                Type propertyType = property.PropertyType;

                //if (GetPrimaryKey().Equals(property.Name, StringComparison.CurrentCultureIgnoreCase))
                //    continue;

                // 如果值为空，则付默认值
                if (property.GetValue(obj, null) == null)
                {
                    if (propertyType.Equals(Type.GetType("System.DateTime")))
                    {
                        commandText += "'1971-1-1 0:00:00',";
                        continue;
                    }
                    if (propertyType.Equals(Type.GetType("System.String")))
                    {
                        commandText += "'',";
                        continue;
                    }
                    if (propertyType.Equals(Type.GetType("System.Int32")))
                    {
                        commandText += "0,";
                        continue;
                    }
                    if (propertyType.Equals(Type.GetType("System.Byte[]")))
                    {
                        commandText += "null,";
                        continue;
                    }
                }

                if (propertyType.Equals(Type.GetType("System.DateTime")))
                {
                    if (property.GetValue(obj, null).Equals(DateTime.Parse("0001-1-1 0:00:00")))
                        commandText += "'1971-1-1 0:00:00',";
                    else
                        commandText += "'" + property.GetValue(obj, null) + "',";
                }
                else　if (propertyType.Equals(Type.GetType("System.String")))
                {
                    commandText += "'" + property.GetValue(obj, null) + "',";
                }
                else
                {
                    commandText += property.GetValue(obj, null) + ",";
                }
            }
            commandText = commandText.Substring(0,commandText.Length-1);
            commandText += ")";

            try
            {
                count = SqlHelper.ExecuteNonQuery(commandText);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return count;
        }
        #endregion Insert Method

        #region Update Method
        /// <summary>
        /// Updates an existing object into the database
        /// </summary>
        /// <param name="obj">the object to update</param>
        public int Update(T obj)
        {
            return Update(obj, GetPrimaryKey());
        }

        /// <summary>
        /// Updates an existing object into the database
        /// </summary>
        /// <param name="obj">the object to update</param>
        public int Update(T obj, string propertyName)
        {
            int count = 0;

            string commandText = "UPDATE " + GetTableName() + " SET ";
            Type genericType = obj.GetType();
            Type propertyType = Type.GetType("System.String");
            PropertyInfo[] properties = genericType.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                if (GetPrimaryKey().Equals(property.Name, StringComparison.CurrentCultureIgnoreCase) ||
                    propertyName.Equals(property.Name, StringComparison.CurrentCultureIgnoreCase))
                {
                    propertyType = property.PropertyType;
                    propertyName = property.Name;
                    continue;
                }
                if (property.GetValue(obj, null) == null)//属性没赋值，就不更新
                    continue;

                commandText += property.Name + "="
                    + MyString.GetSqlString(property.GetValue(obj, null), property.PropertyType) + ",";
            }
            commandText = commandText.Substring(0, commandText.Length - 1);

            commandText += " WHERE " + propertyName + "=";

            commandText += MyString.GetSqlString(
                genericType.GetProperty(propertyName).GetValue(obj, null), propertyType);

            try
            {
                count = SqlHelper.ExecuteNonQuery(commandText);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return count;
        }

        /// <summary>
        /// 根据属性更新对象
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        public int UpdateByProperty(T obj, string propertyName)
        {
            return Update(obj, propertyName);
        }
        #endregion Update Method

        #region Delete Method
        /// <summary>
        /// Deletes an existing object in the database
        /// </summary>
        /// <param name="ID">primary key</param>
        public int Delete(ID id)
        {
            int count = 0;

            string commandText = "DELETE " + GetTableName() + " WHERE " + GetPrimaryKey() + "=" + id;
            try
            {
                count = SqlHelper.ExecuteNonQuery(commandText);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return count;
        }
        #endregion Delete Method

        /// <summary>
        /// Default Constructor
        /// </summary>
        public GenericDaoSql()
        {
        }

    }
}