/*
 * 由SharpDevelop创建。
 * 用户：吴隆健
 * 日期: 2019-03-21
 * 时间: 10:07
 *
 */

#if MMSQL

using MrWu.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MrWu.DB
{
    /// <summary>
    /// sqlServer 操作
    /// </summary>
    public class MMSSQL : SqlEx<SqlConnection, SqlCommand, SqlDbType, SqlParameter>
    {
        #region 属性与字段

        /// <summary>
        /// 链接池
        /// </summary>
        public override IConnectionPool<SqlConnection> connPool
        {
            get => null; // _ConnPool;
        }

        /// <summary>
        /// 
        /// </summary>
        public string connectionString { get; }

        #endregion

        #region CTOR

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="config"></param>
        public MMSSQL(SqlConfig config)
        {
            //this._ConnPool = new ConnectionPool(config);
            this.connectionString =
                string.Format("Data Source={0},{1};user id={2};pwd={3};Max Pool Size=512;initial catalog=", config.host,
                    config.port, config.username, config.pwd);
        }

        #endregion

        #region SQL存储过程

        /// <summary>
        /// 开始存储过程
        /// </summary>
        /// <param name="dbbase">存储过程所在的数据库</param>
        /// <param name="procedure">存储过程名</param>
        /// <returns></returns>
        public override IProdureParameter<SqlCommand> BeginProcedure(string dbbase, string procedure)
        {
            //if (_ConnPool == null)
            //    throw new Exception("you dont hava init");

            SqlCommand cmd = new SqlCommand(procedure); //, GetConnection(dbbase)
            SqlProcedureParameter result = new SqlProcedureParameter(cmd, dbbase);
            //存储过程模式
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            return result;
        }


        /// <summary>
        /// 添加参数
        /// </summary>
        /// <param name="spp">存储过程查询键</param>
        /// <param name="paramName">参数名</param>
        /// <param name="value">参数值</param>
        /// <param name="type">参数类型</param>
        /// <param name="pd">参数种类</param>
        public override void AddParam(IProdureParameter<SqlCommand> spp, string paramName, object value, SqlDbType type,
            ParameterDirection pd = ParameterDirection.Input)
        {
            if (spp == null || spp.command == null)
                throw new Exception("param spp null");
            SqlParameter param = spp.command.Parameters.Add(paramName, type);
            param.Direction = pd;
            if (value == null)
                param.Value = DBNull.Value;
            else
                param.Value = value;
        }

        /// <summary>
        /// 提交存储过程
        /// </summary>
        /// <param name="spp">存储过程查询键</param>
        /// <param name="ds">如果 存储过程中有查询表操作,则需要传递DataSet实例</param>
        /// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
        /// <returns>参数值,输出参数从字典中获取</returns>
        public override Dictionary<string, object> SubProcedure(IProdureParameter<SqlCommand> spp, DataSet ds = null,
            int againCount = -1)
        {
            if (spp == null || spp.command == null)
                throw new Exception("param spp null");

            int cnt = Execute(spp.command, spp.dbName, ds, againCount);

            Dictionary<string, object> dic = new Dictionary<string, object>();
            var pms = spp.command.Parameters;
            int len = pms.Count;
            for (int i = 0; i < len; i++)
            {
                dic.Add(pms[i].ParameterName, pms[i].Value);
            }

            if (!dic.ContainsKey(DbConst.RowsAffected))
            {
                dic[DbConst.RowsAffected] = cnt;
            }

            spp.Dispose();
            return dic;
        }

        #endregion

        #region SQL Query

        /// <summary>
        /// 执行sql语句
        /// </summary>
        /// <param name="dbbase">数据库</param>
        /// <param name="sql">sql语句</param>
        /// <param name="sqlparams">单独设置sql语句的参数 一般用来设置二进制参数 或者过长的文字字符串</param>
        /// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
        /// <param name="ds">如果是查询 使用传入ds实例</param>
        public override int ExecuteSql(string dbbase, string sql, IEnumerable<SqlParameter> sqlparams = null,
            DataSet ds = null, int againCount = -1)
        {
            SqlCommand cmd = new SqlCommand(sql); //, GetConnection(dbbase)
            cmd.CommandType = CommandType.Text;

            if (sqlparams != null)
            {
                //int len = sqlparams.Count();
                //for (int i = 0; i < len; i++) {
                //    ISqlAloneParameter<SqlDbType> sap = sqlparams[i];
                //    cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength);
                //    cmd.Parameters[sap.paramname].Value = sap.paramvalue;
                //}
                foreach (var param in sqlparams)
                {
                    cmd.Parameters.Add(param);
                }
            }

            return Execute(cmd, dbbase, ds, againCount);
        }

        /// <summary>
        /// 普通查询一个表
        /// </summary>
        /// <param name="dbbase">数据库名称</param>
        /// <param name="table">表名</param>
        /// <param name="column">列名</param>
        /// <param name="where">条件</param>
        /// <param name="count">查询的数量</param>
        /// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
        /// <param name="sqlparams">sql参数</param>
        /// <returns>查询到的表</returns>
        public override DataTable Select(string dbbase, string table, string[] column = null, string where = null,
            int count = 0, int againCount = -1, IEnumerable<SqlParameter> sqlparams = null)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("select ");
            if (count > 0)
            {
                sb.Append(string.Format("Top {0} ", count));
            }

            if (column != null && column.Length > 0)
            {
                int len = column.Length;
                for (int i = len - 1; i >= 0; i--)
                {
                    sb.Append(column[i]);
                    if (i > 0)
                        sb.Append(",");
                }
            }
            else
            {
                sb.Append("*");
            }

            sb.Append(" from ");
            sb.Append(table);
            if (!string.IsNullOrEmpty(where))
            {
                sb.Append(" where ");
                sb.Append(where);
            }

            DataSet ds = new DataSet();

            //Debug.Debug.Info("sql:" + sb.ToString());
            ExecuteSql(dbbase, sb.ToString(), sqlparams, ds, againCount);
            if (ds.Tables == null || ds.Tables.Count == 0)
                return null;
            return ds.Tables[0];
        }

        #endregion

        #region SQL 事务

        /// <summary>
        /// 开始一个事务
        /// </summary>
        /// <param name="dbbase">数据库</param>
        /// <returns></returns>
        public override IProdureParameter<SqlCommand> BeginTransaction(string dbbase)
        {
            SqlCommand cmd = new SqlCommand();
            SqlProcedureParameter spp = new SqlProcedureParameter(cmd, dbbase);
            try
            {
                var conn = GetConnection(dbbase);
                {
                    conn.Open();
                    cmd.Connection = conn;
                    cmd.Connection.ChangeDatabase(dbbase);
                    cmd.Transaction = cmd.Connection.BeginTransaction(); //开始事务，还可以设置隔离等级
                }
            }
            catch (Exception e)
            {
                spp.exception = e;
                if (cmd.Connection != null)
                {
                    cmd.Connection.Dispose();
                }
            }

            return spp;
        }

        /// <summary>
        /// 事务添加sql语句
        /// </summary>
        /// <param name="spp"></param>
        /// <param name="sql"></param>
        /// <param name="sqlparams"></param>
        public override int TransactionAddSql(IProdureParameter<SqlCommand> spp, string sql,
            List<ISqlAloneParameter<SqlDbType>> sqlparams = null)
        {
            if (spp.isException)
                return 0;
            try
            {
                SqlCommand cmd = spp.command;
                cmd.CommandText = sql;

                cmd.Parameters.Clear();
                if (sqlparams != null)
                {
                    int len = sqlparams.Count;
                    for (int i = 0; i < len; i++)
                    {
                        ISqlAloneParameter<SqlDbType> sap = sqlparams[i];
                        if (sap.valueLength > 0)
                            cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength);
                        else
                            cmd.Parameters.Add(sap.paramname, sap.type);
                        cmd.Parameters[sap.paramname].Value = sap.paramvalue;
                        cmd.Parameters[sap.paramname].Direction = sap.Direction;
                    }
                }

                var result = cmd.ExecuteNonQuery();
                if (sqlparams != null)
                {
                    for (int i = 0; i < sqlparams.Count; i++)
                    {
                        sqlparams[i].paramvalue = cmd.Parameters[i].Value;
                    }
                }

                return result;
            }
            catch (Exception e)
            {
                spp.command.Transaction.Rollback(); //事务步骤异常，主动回滚事务
                spp.exception = e;

                throw e;
            }
        }

        /// <summary>
        /// 提交事务
        /// </summary>
        /// <param name="spp"></param>
        /// <returns>true 表示提交成功 false 表示提交失败</returns>
        public override bool SubTransaction(IProdureParameter<SqlCommand> spp)
        {
            using (spp)
            {
                using (spp.command)
                {
                    using (spp.command.Connection)
                    {
                        using (spp.command.Transaction)
                        {
                            try
                            {
                                spp.command.Transaction.Commit();
                            }
                            catch (Exception e)
                            {
                                spp.exception = e;
                                spp.command.Transaction.Rollback();
                            }

                            return spp.exception == null;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 回滚事务
        /// </summary>
        /// <param name="spp"></param>
        public override void RollbackTransaction(IProdureParameter<SqlCommand> spp)
        {
            try
            {
                spp.command.Transaction.Rollback(); //事务步骤异常，主动回滚事务
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            spp.command.Transaction?.Dispose();
            spp.command.Connection?.Dispose();
            spp.command.Dispose();
            spp.Dispose();
        }

        #endregion


        #region 异步方法

        /// <summary>
        /// 异步提交存储过程
        /// </summary>
        public override async Task<Dictionary<string, object>> SubProcedureAsync(IProdureParameter<SqlCommand> spp,
            DataSet ds = null, int againCount = -1)
        {
            if (spp == null || spp.command == null)
                throw new Exception("param spp null");

            int cnt = await ExecuteAsync(spp.command, spp.dbName, ds, againCount);

            Dictionary<string, object> dic = new Dictionary<string, object>();
            var pms = spp.command.Parameters;
            int len = pms.Count;
            for (int i = 0; i < len; i++)
            {
                dic.Add(pms[i].ParameterName, pms[i].Value);
            }

            if (!dic.ContainsKey(DbConst.RowsAffected))
            {
                dic[DbConst.RowsAffected] = cnt;
            }

            spp.Dispose();
            return dic;
        }

        /// <summary>
        /// 异步执行sql语句
        /// </summary>
        public override async Task<int> ExecuteSqlAsync(string dbbase, string sql,
            IEnumerable<SqlParameter> sqlparams = null, DataSet ds = null, int againCount = -1)
        {
            SqlCommand cmd = new SqlCommand(sql);
            cmd.CommandType = CommandType.Text;

            if (sqlparams != null)
            {
                foreach (var param in sqlparams)
                {
                    cmd.Parameters.Add(param);
                }
            }

            return await ExecuteAsync(cmd, dbbase, ds, againCount);
        }

        /// <summary>
        /// 异步查询
        /// </summary>
        public override async Task<DataTable> SelectAsync(string dbbase, string table, string[] column = null,
            string where = null, int count = 0, int againCount = -1, IEnumerable<SqlParameter> sqlparams = null)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("select ");
            if (count > 0)
            {
                sb.Append(string.Format("Top {0} ", count));
            }

            if (column != null && column.Length > 0)
            {
                int len = column.Length;
                for (int i = len - 1; i >= 0; i--)
                {
                    sb.Append(column[i]);
                    if (i > 0)
                        sb.Append(",");
                }
            }
            else
            {
                sb.Append("*");
            }

            sb.Append(" from ");
            sb.Append(table);
            if (!string.IsNullOrEmpty(where))
            {
                sb.Append(" where ");
                sb.Append(where);
            }

            DataSet ds = new DataSet();

            await ExecuteSqlAsync(dbbase, sb.ToString(), sqlparams, ds, againCount);
            if (ds.Tables == null || ds.Tables.Count == 0)
                return null;
            return ds.Tables[0];
        }

        /// <summary>
        /// 异步执行cmd
        /// </summary>
        public override async Task<int> ExecuteAsync(SqlCommand cmd, string dbname, DataSet ds = null,
            int againCount = -1)
        {
            int result = 0;
            int exceCnt = 0;
            if (againCount < 0)
                againCount = errorTryAgainCount;

            while (exceCnt <= againCount)
            {
                try
                {
                    using (cmd)
                    {
                        using (var conn = GetConnection(dbname))
                        {
                            await conn.OpenAsync();
                            cmd.Connection = conn;
                            cmd.Connection.ChangeDatabase(dbname);
                            if (ds == null)
                            {
                                result = await cmd.ExecuteNonQueryAsync();
                            }
                            else
                            {
                                using (var sda = new SqlDataAdapter())
                                {
                                    sda.SelectCommand = cmd;
                                    sda.Fill(ds);
                                }
                            }
                        }
                    }

                    break;
                }
                catch (Exception e)
                {
                    if (exceCnt < againCount)
                    {
                        Debug.Debug.Warning("数据库异步执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message);
                    }
                    else
                    {
                        throw;
                    }
                }

                int time = errorAgainTime + errorAgainTimeInc * exceCnt;
                exceCnt++;
                await Task.Delay(time);
            }

            return result;
        }

        #endregion

        #region SQL Common

        /// <summary>
        /// 获取一个连接
        /// </summary>
        /// <param name="dbbase"></param>
        /// <returns></returns>
        public override SqlConnection GetConnection(string dbbase) => new SqlConnection(connectionString + dbbase);


        /// <summary>
        /// 执行sql命令
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="dbname">数据库名称</param>
        /// <param name="ds"></param>
        /// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
        public override int Execute(SqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1)
        {
            int result = 0;
            int exceCnt = 0;
            if (againCount < 0)
                againCount = errorTryAgainCount;

            while (exceCnt <= againCount)
            {
                try
                {
                    using (cmd)
                    {
                        // Debug.Debug.Info("链接字符串:" + connectionString);
                        using (var conn = GetConnection(dbname))
                        {
                            conn.Open();
                            cmd.Connection = conn;
                            cmd.Connection.ChangeDatabase(dbname);
                            if (ds == null)
                            {
                                result = cmd.ExecuteNonQuery();
                            }
                            else
                            {
                                using (var sda = new SqlDataAdapter())
                                {
                                    //一定要关闭,不然下次执行报错;
                                    sda.SelectCommand = cmd;
                                    sda.Fill(ds); //调用会自动执行 ExecuteNonQuery();	不要重复调用,很容易出错!
                                }
                            }
                        }
                    }

                    break;
                }
                catch (Exception e)
                {
                    if (exceCnt < againCount)
                    {
                        Debug.Debug.Warning("数据库执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message);
                    }
                    else
                    {
                        throw e;
                    }
                }

                int time = errorAgainTime + errorAgainTimeInc * exceCnt;
                exceCnt++; //执行次数+1
                Thread.Sleep(time);
            }

            return result;
        }

        #endregion
    }
}

#endif