/********************************
 *
 * 作者:吴隆健
 * 创建时间: 2019/7/3 13:06:48
 *
 ********************************/

using GameData;
using GameDAL.Game;
using MrWu.Debug;
using Server.DB.Redis;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Data;
using MrWu.Time;
using System.Threading;
using Server.Data;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Server.Core;
using Server.DB.Sql;

///// <summary>
///// 玩家数据管理
///// </summary>
namespace Server
{
    /// <summary>
    /// 
    /// </summary>
    public class PlayerDataManager
    {
        /// <summary>
        /// 
        /// </summary>
        public static PlayerDataManager instance { get; private set; }

        private PlayerDataManager()
        {
        }

        static PlayerDataManager()
        {
            instance = new PlayerDataManager();
        }


        #region player redis 字段

        /// <summary>
        /// 玩家的userid 字段
        /// </summary>
        public const string useridField = "userid";

        /// <summary>
        /// 用户名
        /// </summary>
        public const string UserNameField = "UserName";
        
        /// <summary>
        /// 玩家昵称
        /// </summary>
        public const string nickNameField = "NickName";

        /// <summary>
        /// 数据版本
        /// </summary>
        public const string verField = "ver";

        /// <summary>
        /// 玩家金额字段
        /// </summary>
        public const string moneyField = "money";

        /// <summary>
        /// 游戏币变化字段
        /// </summary>
        public const string moneyChangeField = "moneyChange";

        /// <summary>
        /// 金币字段名
        /// </summary>
        public const string goldFiled = "Glod";

        /// <summary>
        /// 金币变化字段名
        /// </summary>
        public const string goldChangeField = "GlodChange";

        /// <summary>
        /// 性别字段名
        /// </summary>
        public const string sexField = "Sex";

        /// <summary>
        /// 地区字段
        /// </summary>
        public const string areaField = "Area";

        /// <summary>
        /// 经验字段
        /// </summary>
        public const string expField = "hjha_Exp";

        /// <summary>
        /// 经验变化字段
        /// </summary>
        public const string expChangeField = "expChange";

        /// <summary>
        /// 玩家的奖券字段
        /// </summary>
        public const string couponField = "coupon";

        /// <summary>
        /// 奖券变化字段名
        /// </summary>
        public const string couponChangeField = "couponChange";

        /// <summary>
        /// 房卡字段名
        /// </summary>
        public const string fangkaField = "fangka";

        /// <summary>
        /// 房卡变化字段名
        /// </summary>
        public const string FangKaChangeField = "fangkaChange";

        /// <summary>
        /// 用户类型
        /// </summary>
        public const string UserTypeField = "UserType";

        /// <summary>
        /// 代理字段
        /// </summary>
        public const string DailiField = "daili";

        /// <summary>
        /// 防沉迷字段
        /// </summary>
        public const string PiField = "pi";

        /// <summary>
        /// 比赛卷字段
        /// </summary>
        public const string MatchRollField = "mmnn";

        /// <summary>
        /// 比赛卷变化字段
        /// </summary>
        public const string MatchRollChangeField = "mmnnChange";

        /// <summary>
        /// 推广的标识
        /// </summary>
        public const string platTagField = "platTag";

        /// <summary>
        /// 用户头像对应的字段
        /// </summary>
        public const string HeadField = "hjha_LeagueId";

        /// <summary>
        /// 平台类型
        /// </summary>
        public const string PlatEnumField = "hjha_fy_Value";

        /// <summary>
        /// 在redis中的玩家列表
        /// </summary>
        public const string PlayerListKey = "Game:PlayerLRU:Players";

        #endregion

        /// <summary>
        /// 获取玩家的数据的key
        /// </summary>
        /// <param name="userid"></param>
        /// <returns></returns>
        public static string GetKey(int userid)
        {
            return RedisPlayerDataKeyPrefix + userid;
        }

        /// <summary>
        /// redis中玩家数据的key
        /// </summary>
        public const string RedisPlayerDataKeyPrefix = "Game:PlayerData:";

        /// <summary>
        /// 头像
        /// </summary>
        public const string AvatarFidle = "avatar";
        
        /// <summary>
        /// 从redis中加载玩家数据
        /// </summary>
        /// <param name="pl"></param>
        /// <param name="cgdata">变化量</param>
        /// <returns></returns>
        public static int LoadRedisPlayerData(playerData pl, out PlayerDataChange cgdata)
        {
            var changeData = PlayerDataChange.Create(pl.userid);
            cgdata = changeData;
            //-1表示为加载到玩家
            int result = -1;

            string key = GetKey(pl.userid);

            //临时变量
            long tmplong;
            int tmpint;

            var fields = redisManager.db.HashGetAll(key);

            if (fields == null) //redis 中没有玩家数据
                return result;

            foreach (var he in fields)
            {
                switch (he.Name)
                {
                    case nickNameField:
                        pl.nickname = he.Value;
                        break;
                    case UserNameField:
                        pl.UserName = he.Value;
                        break;
                    case moneyField:
                        if (he.Value.TryParse(out tmplong))
                        {
                            pl.money = tmplong;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家游戏币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case moneyChangeField:
                        if (he.Value.TryParse(out tmplong))
                        {
                            changeData.moneyChange = tmplong;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的游戏币变化值竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case goldFiled:
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.gold = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的金币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case goldChangeField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            changeData.goldChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的游戏币数据竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case sexField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.sex = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的性别字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case areaField:
                        pl.area = he.Value;
                        break;
                    case expField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.exp = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误，玩家的经验字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case expChangeField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            changeData.expChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的经验字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case couponField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.coupon = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的奖券字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case couponChangeField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            changeData.couponChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的奖券变化字段竟然不是数值 userid:{0} vlaue:{1}", pl.userid, he.Value);
                        }

                        break;
                    case fangkaField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.fangka = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的房卡字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case FangKaChangeField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            changeData.fangkaChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的房卡变化值字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case UserTypeField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.userType = tmpint;
                        }
                        else
                        {
                            Debug.Error("asckjagbshdbashbsmbaxas :" + he.Value);
                        }

                        break;
                    case AvatarFidle:
                        pl.avatar = he.Value;
                        break;
                    case DailiField:
                        tmpint = 0;
                        if (he.Value.TryParse(out tmpint))
                            pl.daili = tmpint;
                        else
                            Debug.Error("错误错误,玩家代理字段竟然不是数值 userid{0} value{1}", pl.userid, he.Value);
                        break;
                    case PlatEnumField:
                        tmpint = 0;
                        if (he.Value.TryParse(out tmpint))
                            pl.PlatEnum = tmpint;
                        else
                            Debug.Error("错误错误,玩家代理字段竟然不是数值 userid{0} value{1}", pl.userid, he.Value);
                        break;
                    case PiField:
                        pl.pi = he.Value;
                        break;
                    case platTagField:
                        pl.platTag = he.Value;
                        break;
                    case HeadField:
                        tmpint = 0;
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.HeadType = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的头像字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case MatchRollField:
                        tmpint = 0;
                        if (he.Value.TryParse(out tmpint))
                        {
                            pl.MatchRoll = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的比赛卷字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case MatchRollChangeField:
                        if (he.Value.TryParse(out tmpint))
                        {
                            changeData.MatchRollChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("错误错误,玩家的比赛卷变化值字段竟然不是数值 userid:{0} value:{1}", pl.userid, he.Value);
                        }

                        break;
                    case "RegTime":
                        string temp = he.Value;
                        try
                        {
                            pl.RegTime = string.IsNullOrWhiteSpace(temp) ? default(DateTime) : Convert.ToDateTime(temp);
                        }
                        catch
                        {
                            pl.RegTime = default(DateTime);
                        }

                        break;
                }
            }

            PlayerFun.PlayerAddChangeData(pl, changeData);
            result = 1;
            return result;
        }

        /// <summary>
        /// 加载玩家数据如果 sql中的ver 与 参数 ver 不一致 更新redis  以及 pl 原始数据 并添加版本ver信息
        /// </summary>
        //加载sql 数据 并更新 redis 数据
        public static bool LoadSqlPlayerData(playerData pl, int ver, PlayerDataChange pdc)
        {
            int userid = pl.userid;

            string key = GetKey(pl.userid);

            DataSet ds = PlayerDB.ReadPlayerData(pl.userid, !string.IsNullOrEmpty(pl.pi));

            if (ds.Tables == null || ds.Tables.Count < 1 || ds.Tables[0].Rows.Count <= 0)
            {
                //版本一致,数据取redis 原来的数据

                return false; //redis 中的版本 与数据库中的版本一致 则有玩家, 版本不一致却没数据，表示没有这个用户
            }

            //读取玩家数据
            DataTable gameTable = ds.Tables[0];

            //昵称
            string db_nickname;
            //地区
            string db_area;
            //头像
            string db_avatar; //头像

            string db_userName;
            //用户类型
            int db_UserType;
            //经验值
            int db_exp;
            //性别
            int db_sex;
            //游戏币
            long db_money;
            //奖券
            int db_coupon = 0;
            //房卡
            int db_fangka;
            //金币
            int db_glod;

            int db_daili = 0;
            db_nickname = gameTable.Rows[0]["nickNameEm"] as string;
            if (string.IsNullOrEmpty(db_nickname))
            {
                db_nickname = gameTable.Rows[0][nickNameField] as string;
            }
            
            db_userName = gameTable.Rows[0]["userName"] as string;

            if (db_nickname == null)
                db_nickname = string.Empty;

            db_area = gameTable.Rows[0][areaField] as string;
            if (db_area == null)
                db_area = string.Empty;
            db_sex = (int)gameTable.Rows[0][sexField];
            db_money = (long)gameTable.Rows[0][moneyField];
            db_exp = (int)gameTable.Rows[0][expField];
            db_fangka = (int)gameTable.Rows[0]["xu"];
            db_UserType = (int)gameTable.Rows[0][UserTypeField];
            db_coupon = (int)gameTable.Rows[0][couponField];
            db_avatar = gameTable.Rows[0][AvatarFidle] as string;
            db_daili = (int)gameTable.Rows[0][DailiField];
            var platEnum = (int)gameTable.Rows[0][PlatEnumField];
            string pi = pl.pi;
            if (string.IsNullOrEmpty(pl.pi))
            {
                pi = (gameTable.Rows[0][PiField] as string) ?? string.Empty;
            }

            string platTag = (gameTable.Rows[0][platTagField] as string) ?? string.Empty;
            int matchRoll = (int)gameTable.Rows[0][MatchRollField];
            int head = (int)gameTable.Rows[0][HeadField];
            DateTime regTime = default(DateTime);
            try
            {
                regTime = (DateTime)gameTable.Rows[0]["RegTime"];
            }
            catch
            {
            }

            if (db_avatar == null)
                db_avatar = string.Empty;
            db_glod = (int)gameTable.Rows[0]["hjha_Gold"];

            List<HashEntry> hes = new List<HashEntry>();
            hes.Add(new HashEntry(useridField, userid));
            hes.Add(new HashEntry(UserNameField,db_userName));
            hes.Add(new HashEntry(nickNameField, db_nickname));
            hes.Add(new HashEntry(moneyField, db_money));
            hes.Add(new HashEntry(sexField, db_sex));
            hes.Add(new HashEntry(areaField, db_area));
            hes.Add(new HashEntry(expField, db_exp));
            hes.Add(new HashEntry(couponField, db_coupon));
            hes.Add(new HashEntry(fangkaField, db_fangka));
            hes.Add(new HashEntry(UserTypeField, db_UserType.ToString()));
            hes.Add(new HashEntry(AvatarFidle, db_avatar));
            hes.Add(new HashEntry(goldFiled, db_glod));
            hes.Add(new HashEntry(verField, ver));
            hes.Add(new HashEntry(DailiField, db_daili));
            hes.Add(new HashEntry(PiField, pi));
            hes.Add(new HashEntry(platTagField, platTag));
            hes.Add(new HashEntry("RegTime", regTime.ToString("yyyy-MM-dd HH:mm:ss")));
            hes.Add(new HashEntry(MatchRollField, matchRoll));
            hes.Add(new HashEntry(HeadField, head));
            hes.Add(new HashEntry(PlatEnumField, platEnum));

            //Debug.Info("设置玩家原始数据:" + userid + "," + db_money);
            redisManager.db.HashSetAsync(key, hes.ToArray());

            pl.nickname = db_nickname;
            pl.UserName = db_userName;
            pl.money = db_money;
            pl.sex = db_sex;
            pl.area = db_area;
            pl.exp = db_exp;
            pl.coupon = db_coupon;
            pl.fangka = db_fangka;
            pl.userType = db_UserType;
            pl.avatar = db_avatar;
            pl.gold = db_glod;
            pl.daili = db_daili;
            pl.pi = pi;
            pl.PlatEnum = platEnum;
            pl.MatchRoll = matchRoll;
            pl.platTag = platTag;
            pl.RegTime = regTime;
            pl.HeadType = head;
            if (pdc != null)
                PlayerFun.PlayerAddChangeData(pl, pdc);

            return true;
        }

        /// <summary>
        /// 把玩家的变化数据清楚掉-和写sql一起使用, 原始数据+ 变化数据-
        /// </summary>
        /// <param name="pdc"></param>
        /// <returns>是否存在改玩家</returns>
        public static bool ClearRedisDataChange(PlayerDataChange pdc)
        {
            string key = GetKey(pdc.userid);

            var tran = redisManager.GetDataBase().CreateTransaction();
            tran.AddCondition(Condition.KeyExists(key));
            if (pdc.moneyChange != 0)
            {
                tran.HashDecrementAsync(key, moneyChangeField, pdc.moneyChange);
                tran.HashIncrementAsync(key, moneyField, pdc.moneyChange);
            }

            if (pdc.expChange != 0)
            {
                tran.HashDecrementAsync(key, expChangeField, pdc.expChange);
                tran.HashIncrementAsync(key, expField, pdc.expChange);
            }

            if (pdc.fangkaChange != 0)
            {
                tran.HashDecrementAsync(key, FangKaChangeField, pdc.fangkaChange);
                tran.HashIncrementAsync(key, fangkaField, pdc.fangkaChange);
            }

            if (pdc.goldChange != 0)
            {
                tran.HashDecrementAsync(key, goldChangeField, pdc.goldChange);
                tran.HashIncrementAsync(key, goldFiled, pdc.goldChange);
            }

            if (pdc.couponChange != 0)
            {
                tran.HashDecrementAsync(key, couponChangeField, pdc.couponChange);
                tran.HashIncrementAsync(key, couponField, pdc.couponChange);
            }

            if (pdc.MatchRollChange != 0)
            {
                tran.HashDecrementAsync(key, MatchRollChangeField, pdc.MatchRollChange);
                tran.HashIncrementAsync(key, MatchRollField, pdc.MatchRollChange);
            }

            return tran.Execute();
        }

        /// <summary>
        /// 变化数据写入redis中
        /// </summary>
        /// <param name="pdc">玩家数据变化值</param>
        /// <returns>是否存在这个玩家</returns>
        public static bool WriteToRedis(PlayerDataChange pdc)
        {
            string key = GetKey(pdc.userid);

            var tran = redisManager.GetDataBase().CreateTransaction();

            if (pdc.moneyChange != 0)
                tran.HashIncrementAsync(key, moneyChangeField, pdc.moneyChange);
            if (pdc.expChange != 0)
                tran.HashIncrementAsync(key, expChangeField, pdc.expChange);
            if (pdc.goldChange != 0)
                tran.HashIncrementAsync(key, goldChangeField, pdc.goldChange);
            if (pdc.fangkaChange != 0)
                tran.HashIncrementAsync(key, FangKaChangeField, pdc.fangkaChange);
            if (pdc.couponChange != 0)
                tran.HashIncrementAsync(key, couponChangeField, pdc.couponChange);
            if (pdc.MatchRollChange != 0)
                tran.HashIncrementAsync(key, MatchRollChangeField, pdc.MatchRollChange);
            tran.AddCondition(Condition.KeyExists(key));
            return tran.Execute();
        }

        /// <summary>
        /// 加载redis 中的变化的数据
        /// </summary>
        /// <returns></returns>
        private static PlayerDataChange LoadRedisPlayerDataChange(int userid, bool isLock)
        {
            string key = GetKey(userid);

            //加载redis 中变化的数据
            var hes = redisManager.db.HashGetAll(key);

            if (hes == null)
                return null;

            int tmpint;
            long tmplong;

            PlayerDataChange pdc =PlayerDataChange.Create(userid);
            foreach (var item in hes)
            {
                switch (item.Name)
                {
                    case moneyChangeField:
                        if (long.TryParse(item.Value, out tmplong))
                        {
                            pdc.moneyChange = tmplong;
                        }
                        else
                        {
                            Debug.Error("redis 中玩家的游戏币变化值不是数值userid:{0},value:{1}", userid, item.Value);
                        }

                        break;
                    case expChangeField:
                        if (int.TryParse(item.Value, out tmpint))
                        {
                            pdc.expChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("redis 中玩家的经验变化值不是数值userid:{0},value:{1}", userid, item.Value);
                        }

                        break;
                    case goldChangeField:
                        if (int.TryParse(item.Value, out tmpint))
                        {
                            pdc.goldChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("redis 中金币变化值不是数值userid:{0},value:{1}", userid, item.Value);
                        }

                        break;
                    case couponChangeField:
                        if (int.TryParse(item.Value, out tmpint))
                        {
                            pdc.couponChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("redis 中奖券变化值不是数值userid:{0},value:{1}", userid, item.Value);
                        }

                        break;
                    case FangKaChangeField:
                        if (int.TryParse(item.Value, out tmpint))
                        {
                            pdc.fangkaChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("redis 中房卡变化值不是数值userid:{0},value:{1}", userid, item.Value);
                        }

                        break;
                    case MatchRollChangeField:
                        if (int.TryParse(item.Value, out tmpint))
                        {
                            pdc.MatchRollChange = tmpint;
                        }
                        else
                        {
                            Debug.Error("redis 中比赛卷变化值不是数值userid:{0},value:{1}", userid, item.Value);
                        }

                        break;
                }
            }

            return pdc;
        }

        /// <summary>
        /// 删除玩家redis key
        /// </summary>
        /// <param name="userid"></param>
        public static void DeletePlayReids(int userid)
        {
            redisManager.db.KeyDelete(GetKey(userid));
        }


        /// <summary>
        /// 保存至sql数据库
        /// </summary>
        /// <returns></returns>
        public static bool SaveToSql(int userid)
        {
            PlayerDataChange pdc = LoadRedisPlayerDataChange(userid, false);
            try
            {
                if (pdc != null && pdc.IsChanged)
                {
                    //读取变化的数据
                    Debug.Info("保存玩家数据:" + pdc.userid + "," + pdc.moneyChange);
                    //先清楚redis， sql未保存成功 再手动回滚
                    if (!ClearRedisDataChange(pdc))
                    {
                        Debug.Error("数据竟然没有了,,,......玩家数据被穿透!!");
                        return false;
                    }

                    bool isOK = true;
                    try
                    {
                        isOK = PlayerDB.SavePlayerData(pdc);
                    }
                    catch (Exception e)
                    {
                        Debug.Error("保存玩家数据时出现错误:" + e.ToString());
                        isOK = false;
                    }

                    //写入要志

                    if (!isOK)
                    {
                        try
                        {
                            //获取相反值，回滚
                            PlayerDataChange pdc2 = pdc.GetOppositeValue(true);
                            ClearRedisDataChange(pdc2);
                        }
                        catch (Exception e)
                        {
                            Debug.Error($"回滚失败!错误错误!:{e.Message}\r\n" + pdc.ToString());
                        }
                    }

                    return isOK;
                }
            }finally
            {
                if (pdc != null)
                {
                    ReferencePool.Recycle(pdc);
                }
            }
            
            return true;
        }

        public Task<UserAccountInfo> GetUserAccountInfoAsync(int userid)
        {
            return Task.Run(() => GetUserAccountInfo(userid));
        }

        public UserAccountInfo GetUserAccountInfo(int userid)
        {
            var dt =GameDB.Instance.selectDb(dbbase.gamedb,"tbUserBaseInfo",new string[]{"UserName","NickName","nickNameEm"},$"userid={userid}");
            if (dt.Rows.Count == 1)
            {
                var row = dt.Rows[0];
                UserAccountInfo info = new UserAccountInfo();
                info.UserId = userid;
                var userName = Convert.ToString(row["UserName"]);
                var nickName = Convert.ToString(row["NickName"]);
                var nickNameEm = Convert.ToString(row["nickNameEm"]);
                info.UserName = userName;
                info.NickName = string.IsNullOrEmpty(nickNameEm) ? nickName : nickNameEm;
                return info;
            }
            else
            {
                if (dt.Rows.Count < 1)
                {
                    Debug.Error($"GetUserAccountInfo 未找到用户 {userid}");
                }
                else
                {
                    Debug.Error($"GetUserAccountInfo 多条数据 userid={userid}");
                }
                return null;
            }
        }
    }

    /// <summary>
    /// 用户信息
    /// </summary>
    public class UserAccountInfo
    {
        /// <summary>
        /// UserId
        /// </summary>
        public int UserId;
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName;
        /// <summary>
        /// 昵称
        /// </summary>
        public string NickName;
    }
}