/*
 * 由SharpDevelop创建。
 * 用户： Administrator
 * 日期: 2018-09-14
 * 时间: 15:16
 *
 * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
 */

using System;
using StackExchange.Redis;
using System.Collections.Generic;
using MrWu.Debug;
using System.Linq;
using GameData;
using Server.Config;

namespace Server.DB.Redis
{
    /// <summary>
    /// 数据库类型
    /// </summary>
    public enum DbStyle
    {
        /// <summary>
        /// 主库
        /// </summary>
        main,

        /// <summary>
        /// 比赛
        /// </summary>
        bisai
    }

    /// <summary>
    /// redis数据库管理
    /// </summary>
    public class redisManager
    {
        /* redis 命令大全
         * del key 删除键
         * dump key 序列化key 返回序列化的值
         * exists key 检查key是否存在
         * exipire key seconds 设置过期的时间
         * pexipire key milliseconds 设置过期时间毫秒
         * keys keyspattem 查找给定格式的key
         * move key db 将key移动到给定数据库db中
         * persist key 移除key的过期时间
         * pttl key 返回剩余过期时间以毫秒计
         * ttl key 返回剩余的过期时间以秒计
         * randomkey 随机返回一个key
         * rename key newkey 修改key的名称
         * renamenx key newkey 仅当newkey不存在时，将key改名为newkey
         * type key 返回key所存储的值类型
         *
         * 字符串类型的命令
         * set key value 设置key 字符串值
         * get key		 获取key的字符串值
         * getrange key start end 获取key的字符串一段值
         * getset key value 从新设置key的值 并返回原来的值
         * getbit key offset ------
         * strlen key 返回key存储的字符串长度
         * mset key1 value1 key2 value2 设置一个或多个字符串的值
         * msetnx key1 value1 key2 value2 设置一个活多个字符串的值不如果存在key则不设置
         * setex key seconds value 将值value关联到key,并将key的过期时间设为seconds秒
         * psetex key millseconds value 将value关联到key，并将key的过期时间设为milliseconds毫秒
         * incr key 将key中存储的数字值增一 只能value是整数才能使用 并返回存储后的值
         * incrby key increment 将key中存储的数字值增加increment 并返回存储后的值
         * incrbyfloat key increment 将key中存储的数字值增加increment的浮点值 并返回存储后的值 存在误差
         * decr key 将key中存储的数字减一 只有value是数字型的时候才能使用 并返回存储后的值
         * decrby key decrement 将key中存储的数字值减少decrement 并返回存储后的值
         * append key value 如果key已经存在并且是一个字符串 append 命令将指定的value追加到key原来的值的末尾 并返回追加后的字符串长度
         *
         * hash类型的命令
         * hmset key hashkey1 value1 hashkey2 vlaue2 将新的hash字段插入到hash表中
         * hget key hashkey 获取hash中的key
         * hdel key hashkey1 hashkey2 删除一个或多个hash字段
         * hexists key filed 查询哈希表中是否存在filed
         * hgetall key 获取在哈希表中指定的key的所有字段和值
         * hincrby key field increment 为哈希表中key 的值增加1
         * hincrbyfloat key field increment 为哈希表中key 的值浮点值增量
         * hkeys key 获取所有的哈希表中key值
         * hlen key 获取哈希表中字段的数量
         * hmget key field1 field2 获取给定字段的值
         * hset key field vlaue 如果hash表中存在key则 修改key的值 如果不存在则添加
         * hsernx key filed value 只有在hash表中不存在field字段时才设置值
         * hvals key 获取hash表中的所有值
         *
         * List 列表类型命令
         * lpush key value1 value2 将值插入到key列表中的头部
         * blpop key key1 timeout 优先取key中的值如果key中有值返回第一个值 如果key中没有值取key1中的值 如果key1也没有值 则会等待timeout后超时
         * brpop key key1 timeout 获取最后一个值
         * brpoplpush source destination timeout 从source 中取出一个值并插入到destination 中 如果没有值知道timeout才停止
         * lindex key index 通过索引获取表中的元素
         * linsert key before value
         * lpop key 移出并获取列表中的第一个元素
         * lpush key value1 value2 将值插入到已存在key列表中的头部
         * lpushx key value 将一个值插入到已存在key表中的头部
         * lrange key start end 获取列表中指定范围的内的元素
         * lrem key count value count>0表示从头向尾搜索，移除value相等的元素 数量为count count<0表示从尾巴向头搜索，移除value相等的元素 数量为count的绝对值 count = 0 时移除列表中所有的value相等的元素
         * lset key index value 设置列表index上的值
         * ltrim key satrt stop 指定列表保留的元素区间，不在区间内的元素将会被删除
         * rpop key 移除并获取列表中的最后一个元素\
         * rpoplpush source destination 移除source中的最后一个元素并将该元素添加到destination表中
         * rpush key value1 value2 将值插入到列表的尾部
         * rpushx key value 向已存在的表中添加值
         *
         * Set 集合命令
         * sadd key menber1 menber2 向集合中添加一个或多个成员
         * scard key 获取集合中的成员数量
         * sdiff key key1 返回所有集合的差集
         * sdiffstore destination key key1 返回给定所有集合的差集 并存储到destination 中
         * sinter key key1 缩回所有集合的交集
         * sindterstore destination key key1 返回所有集合的交集 并存储到destination中
         * sismember key member 判断member元素是否是集合key的成员
         * smemers key 返回集合中的所有成员
         * smove source destination member 将member元素从source集合移动到destination集合中
         * spop key 移除并返回集合中的一个随机元素
         * srandmember key count 返回集合中的一个或多个随机元素
         * serm key member member1 移除集合中的一个或多个成员
         * sunion key key1 返回所有给定集合的并集
         * sunionstore destination key1 key2 返回所有给定集合的并集并存储在destination集合中
         * 迭代集合中的元素xxxx
         *
         * 有序集合
         * zadd key score member1 socre1 member2 向有序集合添加一个或多个成员，或者更新已存在的成员的分数
         * zcard key 获取有序集合的成员数量
         * zcount key min max 计算在有序集合中指定区间分数的成员数
         * zincrby key increment member 有序集合中对指定成员的分数加增量increment
         * ---zinterstore destination numkeys key key1
         * --zlexcount key min max 在有序集合中计算指定字典区间内成员数量
         *
         *
         *
         *
         *
         * */


        /**
         * 通配符 * 表示任意一个或多个字符
         *        ? 表示任意字符
         *        [abc] 表示方括号中任意一个字母
         *
         * */
        private redisManager()
        {
        }

        /// <summary>
        /// 主redis 数据库连接
        /// </summary>
        private static RedisConnectionHelp mainDBConnection;

        /// <summary>
        /// 比赛redis 数据库连接
        /// </summary>
        private static RedisConnectionHelp bisaiDBConnection;

        private static volatile bool isStart = false;

        /// <summary>
        /// redis操作数据库代理_逻辑数据库处理
        /// </summary>
        public static IDatabaseProxy db
        {
            get
            {
                if (isStart)
                {
                    return new IDatabaseProxy(mainDBConnection.Instance.GetDatabase());
                }

                return null;
            }
        }

        /// <summary>
        /// redis 操作数据库代理_逻辑数据库处理
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public static IDatabaseProxy Getdb(int idx)
        {
            if (isStart)
                return new IDatabaseProxy(mainDBConnection.Instance.GetDatabase(idx));
            return null;
        }

        /// <summary>
        /// 获取比赛数据库处理
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public static IDatabaseProxy GetBiSaidb(int idx = -1)
        {
            if (isStart)
                return new IDatabaseProxy(bisaiDBConnection.Instance.GetDatabase(idx));
            return null;
        }

        /// <summary>
        /// 获取数据库操作
        /// </summary>
        /// <returns></returns>
        public static IDatabase GetDataBase(DbStyle sty = DbStyle.main, int idx = -1)
        {
            ConnectionMultiplexer cm = null;
            switch (sty)
            {
                case DbStyle.main:
                    cm = mainDBConnection.Instance;
                    break;
                case DbStyle.bisai:
                    cm = bisaiDBConnection.Instance;
                    break;
            }

            if (cm == null)
                return null;
            return cm.GetDatabase(idx);
        }

        public static List<RedisKey> Keys(string pattern, int dbidx, int pageSize = 1000, DbStyle sty = DbStyle.main)
        {
            if (!isStart) return null;

            ConnectionMultiplexer cm = null;
            switch (sty)
            {
                case DbStyle.main:
                    cm = mainDBConnection.Instance;
                    break;
                case DbStyle.bisai:
                    cm = bisaiDBConnection.Instance;
                    break;
            }

            if (cm == null)
                return null;

            List<RedisKey> allkeys = new List<RedisKey>();

            try
            {
                var points = cm.GetEndPoints();
                if (points.Length < 1)
                {
                    Debug.Error("redis 没有任何连接!");
                    return null;
                }

                IServer server = cm.GetServer(points[0]);

                if (points.Length > 1)
                {
                    ClusterConfiguration clusternodes = server.ClusterNodes();
                    foreach (var item in clusternodes.Nodes)
                    {
                        if (!item.IsSlave)
                        {
                            IServer temp = cm.GetServer(item.EndPoint);
                            IEnumerable<RedisKey> rks = temp.Keys(dbidx, pattern,pageSize);
                            foreach (RedisKey rk in rks)
                            {
                                allkeys.Add(rk);
                            }
                        }
                    }
                }
                else
                {
                    var rks = server.Keys(dbidx, pattern, pageSize);

                    foreach (var key in rks)
                    {
                        allkeys.Add(key);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.Error("查询错误asdsahdjk" + e.ToString());
                return allkeys;
            }


            return allkeys;
        }

        /// <summary>
        /// 启动redis 服务器
        /// </summary>
        /// <param name="AllowAdmin">管理员模式</param>
        public static void Start(bool AllowAdmin = false)
        {
            for (int i = 0; i < RedisConfigCategory.Instance.DataList.Count; i++)
            {
                var item = RedisConfigCategory.Instance.DataList[i];
                switch (item.Name)
                {
                    case "main":
                        mainDBConnection = new RedisConnectionHelp();
                        mainDBConnection.Start(item, AllowAdmin);
                        break;
                    case "bisai":
                        bisaiDBConnection = new RedisConnectionHelp();
                        bisaiDBConnection.Start(item, AllowAdmin);
                        break;
                }
            }

            isStart = true;
        }
    }

    /// <summary>
    /// ConnectionMultiplexer对象管理帮助类__单例换多例
    /// </summary>
    public class RedisConnectionHelp
    {
        private readonly object Locker = new object();
        private ConnectionMultiplexer _instance;

        /// <summary>
        /// 单例获取
        /// </summary>
        public ConnectionMultiplexer Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (Locker)
                    {
                        if (_instance == null || !_instance.IsConnected)
                        {
                            //Debug.Error("获取新的实例!!!!");
                            _instance = GetManager();
                        }
                    }
                }

                return _instance;
            }
        }

        RedisConfig redisConfig;
        bool AllowAdmin;

        /// <summary>
        /// 启动redis
        /// </summary>
        /// <param name="redisConfig_"></param>
        /// <param name="AllowAdmin_"></param>
        public void Start(RedisConfig redisConfig_, bool AllowAdmin_ = false)
        {
            redisConfig = redisConfig_;
            AllowAdmin = AllowAdmin_;
        }

        private ConnectionMultiplexer GetManager(string connectionString = null)
        {
            ConfigurationOptions options = new ConfigurationOptions();

            options.ConnectTimeout = redisConfig.ConnectTimeOut;
            options.AllowAdmin = AllowAdmin;
            options.AbortOnConnectFail = false; //true 表示连接失败将不会创建链接实例
            options.ConnectRetry = 5; //失败尝试重新链接的次数

            options.EndPoints.Add(redisConfig.Host, redisConfig.Port);

            ConnectionMultiplexer cml = ConnectionMultiplexer.Connect(options);

            //redisPool.TimeoutMilliseconds = 3000;

            cml.ConnectionFailed += MuxerConnectionFailed;
            cml.ConnectionRestored += MuxerConnectionRestored;
            cml.ErrorMessage += MuxerErrorMessage;
            cml.ConfigurationChanged += MuxerConfigurationChanged;
            cml.HashSlotMoved += MuxerHashSlotMoved;
            cml.InternalError += MuxerInternalError;

            return cml;
        }

        #region 事件

        /// <summary>
        /// 配置更改时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MuxerConfigurationChanged(object sender, EndPointEventArgs e)
        {
            Debug.Fatal("Configuration changed: " + e.EndPoint);
        }

        /// <summary>
        /// 发生错误时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MuxerErrorMessage(object sender, RedisErrorEventArgs e)
        {
            Debug.Fatal("ErrorMessage: " + e.Message);
        }

        /// <summary>
        /// 重新建立连接之前的错误
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)
        {
            Debug.Fatal("ConnectionRestored: " + e.EndPoint);
        }

        /// <summary>
        /// 连接失败 ， 如果重新连接成功你将不会收到这个通知
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            Debug.Fatal("重新连接：Endpoint failed: " + e.EndPoint + ", " + e.FailureType +
                        (e.Exception == null ? "" : (", " + e.Exception.Message)));
        }

        /// <summary>
        /// 更改集群
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)
        {
            Debug.Fatal("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint);
        }

        /// <summary>
        /// redis类库错误
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MuxerInternalError(object sender, InternalErrorEventArgs e)
        {
            Debug.Fatal("InternalError:Message" + e.Exception.Message);
        }

        #endregion 事件
    }
}