//==============================================================
//  版权所有：深圳杰文科技
//  文件名：SqlServerDao.cs
//  版本：V1.0  
//  创建者：Jay  ( QQ： 85363208 )
//  创建时间：2017-11-28 16:18
//  创建描述：
//  修改者：
//  修改时间：
//  修改说明： 
//==============================================================

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using Infrastructure.Utilities;
using System.Linq;

namespace Infrastructure.DAO
{
	//有连接池，自动管理事务。
	public class SqlServerDao : IDbDao
	{
		private const DataSourceSetting.DbTypes dbType = DataSourceSetting.DbTypes.SqlServer;

		private Dictionary<string, SqlConnection> connPool = new Dictionary<string, SqlConnection>();
		private Dictionary<string, SqlTransaction> transPool = new Dictionary<string, SqlTransaction>();

		private SqlConnection GetConnection(string connectionString, bool autoNewConnection = true)
		{
			if (!connPool.ContainsKey(connectionString))
			{
				if (autoNewConnection)
				{
					connPool.Add(connectionString, new SqlConnection(connectionString));
				}
				else
				{
					throw new Exception("未发现SqlConnection");
				}
			}

			SqlConnection conn = connPool[connectionString];
			if (conn == null)
			{
				conn = new SqlConnection(connectionString);
			}

			if (conn.State == ConnectionState.Closed)
			{
				conn.Open();
			}

			return conn;
		}

		public bool BeginTransaction(string connectionString)
		{
			if (!connPool.ContainsKey(connectionString))
			{
				GetConnection(connectionString);
			}

			if (transPool.ContainsKey(connectionString))
			{
				return true;
			}

			if (!transPool.ContainsKey(connectionString))
			{
				SqlConnection conn = connPool[connectionString];
				SqlTransaction trans = conn.BeginTransaction();
				transPool.Add(connectionString, trans);
			}

			return true;
		}

		public bool CommitTransaction(string connectionString)
		{
			if (!connPool.ContainsKey(connectionString))
			{
				throw new Exception("未发现连接");
			}

			if (!transPool.ContainsKey(connectionString))
			{
				throw new Exception("未发现事务");
			}

			SqlTransaction trans = transPool[connectionString];
			trans.Commit();

			trans.Dispose();
			transPool.Remove(connectionString);

			return true;
		}

		public bool RollbackTransaction(string connectionString)
		{
			if (!connPool.ContainsKey(connectionString))
			{
				throw new Exception("未发现连接");
			}

			if (!transPool.ContainsKey(connectionString))
			{
				throw new Exception("未发现事务");
			}

			SqlTransaction trans = transPool[connectionString];
			trans.Rollback();

			trans.Dispose();
			transPool.Remove(connectionString);

			return true;
		}

		public List<T> Select<T>(string connectionString, string where) where T : new()
		{
			Type type = typeof(T);

			string tableName = DbUtil.GetTableName(type);
			string sql = "select * from {0} where 1=1 {1}".Fmt(tableName, where);
			DbUtil.Log(sql);

			List<T> list = null;
			SqlConnection conn = GetConnection(connectionString, true);
			using (SqlDataAdapter da = new SqlDataAdapter(sql, conn))
			{
				using (DataTable table = new DataTable())
				{
					da.Fill(table);
					list = table.ToList<T>();
				}
			}
			return list;
		}

		public T FirstOrDefault<T>(string connectionString, string where) where T : new()
		{
			Type type = typeof(T);

			string tableName = DbUtil.GetTableName(type);
			string sql = "select top 1 * from {0} where 1=1 {1}".Fmt(tableName, where);
			DbUtil.Log(sql);

			SqlConnection conn = GetConnection(connectionString, true);
			using (SqlDataAdapter da = new SqlDataAdapter(sql, conn))
			{
				using (DataTable table = new DataTable())
				{
					da.Fill(table);
					if (table.Rows.Count == 0)
					{
						return default(T);
					}
					else
					{
						T obj = table.Rows[0].To<T>();
						return obj;
					}
				}
			}
		}

		public int Insert<T>(string connectionString, T entity)
		{
			string sql = DbUtil.GenerateInsertSql(dbType, typeof(T), entity);
			int cnt = Execut(connectionString, sql);
			return cnt;
		}

		public int Update<T>(string connectionString, T entity)
		{
			string sql = DbUtil.GenerateUpdateSql(dbType, typeof(T), entity);
			int cnt = Execut(connectionString, sql);
			return cnt;
		}

		public int Delete<T>(string connectionString, object id)
		{
			string sql = DbUtil.GenerateDeleteSql(dbType, typeof(T), id);
			int cnt = Execut(connectionString, sql);
			return cnt;
		}

		private int Execut(string connectionString, string sql)
		{
			DbUtil.Log(sql);

			SqlConnection conn = GetConnection(string.Format(connectionString));
			SqlCommand cmd = null;

			SqlTransaction tran;
			if (transPool.TryGetValue(connectionString, out tran))
			{
				cmd = new SqlCommand(sql, conn, tran);
			}
			else
			{
				cmd = new SqlCommand(sql, conn);
			}


			int cnt = cmd.ExecuteNonQuery();
			return cnt;
		}

		public void Proc(string connectionString, string procName, IEnumerable<DbParameter> parameters)
		{
			DbUtil.Log("exec {0} ...".Fmt(procName));

			List<SqlParameter> sqlParameters = new List<SqlParameter>();
			foreach (DbParameter parameter in parameters)
			{
				sqlParameters.Add((SqlParameter)parameter);
			}

			SqlConnection conn = GetConnection(string.Format(connectionString));
			SqlCommand cmd = conn.CreateCommand();
			cmd.CommandType = CommandType.StoredProcedure;
			cmd.CommandText = procName;
			cmd.Parameters.AddRange(sqlParameters.ToArray());
			cmd.Transaction = transPool[connectionString];
			int cnt = cmd.ExecuteNonQuery();
			conn.Close();
			conn.Dispose();
		}

		public bool Open(string connectionString, out string exception)
		{
			exception = "";
			using (SqlConnection conn = new SqlConnection(connectionString))
			{
				try
				{
					conn.Open();
				}
				catch (Exception ex)
				{
					exception = ex.InnerException == null ? ex.Message : ex.InnerException.Message;
					return false;
				}
			}
			return true;
		}

		public string GetTimestampWhere(Type type, DateTime startTimestamp, DateTime endTimestamp)
		{
			if (startTimestamp == endTimestamp)
			{
				throw new Exception("时间戳的开始和结束时间不能相等");
			}

			string timestampWhere = "";
			string timestampFieldName = DbUtil.GetTimestampFieldName(type);
			if (!timestampFieldName.IsNullOrEmpty())
			{
				timestampWhere = "({0} >= '{1}' and {0} <= '{2}')".Fmt(timestampFieldName, startTimestamp, endTimestamp);
			}
			return timestampWhere;
		}

		public void Dispose()
		{
			foreach (KeyValuePair<string, SqlTransaction> kvp in transPool)
			{
				if (kvp.Value != null)
				{
					kvp.Value.Rollback(); //事务不提交则自动回滚。
				}
			}
			transPool.Clear();

			foreach (KeyValuePair<string, SqlConnection> kvp in connPool)
			{
				if (kvp.Value != null)
				{
					kvp.Value.Dispose(); //释放连接。
				}
			}
			connPool.Clear();
		}
	}
}
