/********************************************************************************
 *	文件名:	ObjManager.cs
 *	全路径:	\Script\Obj\ObjManager.cs
 *	创建人:	李嘉
 *	创建时间:2013-10-25
 *
 *	功能说明:游戏逻辑Obj管理器,对游戏中所有Obj提供创建,移除和管理
 *	修改记录:
 *	//2013-11-18 修改list结构为Dictionary结构,
*********************************************************************************/

using System;
using System.Collections.Generic;
using Games.GlobeDefine;
using Games.LogicObj;
using GCGame.Table;
using Module.Log;
using UnityEngine;

public class OtherPlayerVisibility
{
    public readonly Obj_OtherPlayer character;
    public readonly Collider collider;
    public bool isHide;

    public OtherPlayerVisibility(Obj_OtherPlayer character, bool isHide)
    {
        this.character = character;
        collider = character.GetComponent<Collider>();
        this.isHide = isHide;
    }

    public void SetHide(bool hide)
    {
        if (isHide != hide)
        {
            isHide = hide;
            if (collider != null)
                collider.enabled = !isHide;
            character.SetHideByMask(GetHideMask(isHide));
        }
    }

    public static int GetHideMask(bool hide)
    {
        return hide ? int.MaxValue : PlayerPreferenceData.OtherPlayerHideMask;
    }
}

/// <summary>
/// 死亡Npc延迟回收装置
/// </summary>
// Npc死亡后,如果收到死亡删除协议,客户端将延迟回收Npc
public class DeadNpcObjHolder
{
    private readonly List<DeadNpcData> _npcData = new List<DeadNpcData>();

    public DeadNpcObjHolder()
    {
        GameManager.AddLateUpdate(OnLateUpdate, GameManager.deadNpcUpdateOrder);
    }
    
    public void Destroy()
    {
        GameManager.RemoveLateUpdate(OnLateUpdate, GameManager.deadNpcUpdateOrder);
    }

    public bool TryAddObj(Obj obj)
    {
        var objNpc = obj as Obj_NPC;
        if (objNpc != null &&
            objNpc.IsDie() &&
            objNpc.ModelNode.model != null &&
            objNpc.ObjEffectLogic != null &&
            objNpc.BaseAttr != null)
        {
            var roleBaseAttr = TableManager.GetRoleBaseAttrByID(objNpc.BaseAttr.RoleBaseID, 0);
            if (roleBaseAttr != null && roleBaseAttr.CharModelID > GlobeVar.INVALID_ID &&
                (roleBaseAttr.RampType == 2 || roleBaseAttr.RampType == 3))
            {
                var charModel = TableManager.GetCharModelByID(roleBaseAttr.CharModelID, 0);
                if (charModel != null && charModel.DieShadetime > 0f)
                {
                    var effectTable = TableManager.GetEffectByID(GlobeVar.deathEffectId, 0);
                    if (effectTable != null)
                    {
                        objNpc.ObjEffectLogic.PlaySpecialEffect(effectTable, charModel.DieShadetime);
                        _npcData.Add(new DeadNpcData(objNpc, Time.realtimeSinceStartup + charModel.DieShadetime));
                    }
                    return true;
                }
            }
        }
        return false;
    }

    public void Clear()
    {
        for (var i = 0; i < _npcData.Count; i++)
            if (_npcData[i].objNpc != null)
                _npcData[i].objNpc.DestroyObj();
        _npcData.Clear();
    }

    private void OnLateUpdate()
    {
        for (var i = _npcData.Count - 1; i >= 0; i--)
            if (_npcData[i].removeTime < Time.realtimeSinceStartup)
            {
                var npcData = _npcData[i];
                _npcData.RemoveAt(i);
                if (npcData.objNpc != null)
                    npcData.objNpc.DestroyObj();
            }
    }

    private struct DeadNpcData
    {
        public readonly Obj_NPC objNpc;
        public readonly float removeTime;

        public DeadNpcData(Obj_NPC objNpc, float removeTime)
        {
            this.objNpc = objNpc;
            this.removeTime = removeTime;
        }
    }
}

public class ObjManager : Singleton<ObjManager>
{
    /// <summary>
    ///     在角色创建前,收到的特效数据将会统一保存在这里
    /// </summary>
    private readonly List<ObjEffectCache> _effectCacheList = new List<ObjEffectCache>();

    // OtherPlayer隐藏逻辑
    // 创建时,如果当前服务器创建角色小于限制,记录数据并创建Obj;否则只记录数据不创建。
    // 删除时,删除数据,如果角色有Obj,则为最早无Obj数据创建Obj。
    // 暂时没有限制数值改变时,处理主动删除和主动创建
    private readonly List<OtherPlayerVisibility> _otherList = new List<OtherPlayerVisibility>();

    // 当前客户端带ServerId的Obj池,使用Obj(场景中)名字(唯一)索引,对应Obj的数据
    //动态的,注意使用上限
    public readonly Dictionary<int, Obj> ObjPools = new Dictionary<int, Obj>();

    // 采集物等无ServerId物体的保存位置
    public readonly Dictionary<string, Obj> otherObjPools = new Dictionary<string, Obj>();

    // 死亡Npc的延迟回收器
    public readonly DeadNpcObjHolder deadNpcObjHolder = new DeadNpcObjHolder();

    //怪物归属缓存
    public List<int> m_Belongs = new List<int>();

    //当前客户端主角色
    public Obj_MainPlayer MainPlayer { get; private set; }

    public void AddPoolObj(Obj obj)
    {
        try
        {
            Obj contain;
            if (ObjPools.TryGetValue(obj.ServerID, out contain))
            {
                // 试图第二次创建一个物体,直接忽略
                if (contain != obj)
                {
                    // 重复创建ServerId相同的物体,报错,然后按之前逻辑摧毁物体
                    LogModule.ErrorLog(string.Format("试图创建ServerId重复的物体{0}", obj.ServerID));
                    if (contain != null)
                        contain.DestroyObj();
                    ObjPools[obj.ServerID] = obj;
                }
            }
            else
            {
                ObjPools.Add(obj.ServerID, obj);
            }
        }
        catch (Exception ex)
        {
            LogModule.ErrorLog(string.Format("ObjManager AddObj {0} Error: ", obj.ServerID) + ex);
        }
    }

    public ObjManager()
    {
        PlayerPreferenceData.ShowOtherPlayerCount.onSettingUpdate += RefreshOtherPlayerCount;
    }

    public void RemoveMainPlayer()
    {
        MainPlayer = null;
    }

    public bool RemovePoolObj(int serverId, bool isDead)
    {
        var result = false;
        // 注:存在其他玩家Obj未建立,但是数据存在的情况
        RemoveHidePlayerByServerId(serverId);
        GetEffectCache(serverId, ListGetMode.remove);
        Obj obj;
        if (ObjPools.TryGetValue(serverId, out obj))
        {
            ObjPools.Remove(serverId);
            if (!deadNpcObjHolder.TryAddObj(obj))
                DestroyObj(obj);
            result = true;
        }
        return result;
    }
    // 已经不再维护这个接口!
    //public bool RemovePoolObj(Obj removeObj)
    //{
    //    var result = false;
    //    // 注:存在其他玩家Obj未建立,但是数据存在的情况
    //    RemoveHidePlayerByServerId(removeObj.ServerID);
    //    GetEffectCache(removeObj.ServerID, ListGetMode.remove);
    //    Obj obj;
    //    if (ObjPools.TryGetValue(removeObj.ServerID, out obj))
    //    {
    //        if (removeObj != obj)
    //        {
    //            LogModule.ErrorLog(string.Format("试图移除ServerId为{0}的物体,但是实际ObjManager中存的是另一个物体!", removeObj.ServerID));
    //            DestroyObj(obj);
    //        }
    //        ObjPools.Remove(removeObj.ServerID);
    //        DestroyObj(removeObj);
    //        result = true;
    //    }
    //    return result;
    //}

    public ObjEffectCache GetEffectCache(int serverId, ListGetMode mode)
    {
        ObjEffectCache result = null;
        for (var i = 0; i < _effectCacheList.Count; i++)
            if (_effectCacheList[i].serverId == serverId)
            {
                result = _effectCacheList[i];
                if (mode == ListGetMode.remove)
                    _effectCacheList.RemoveAt(i);
                break;
            }

        if (mode == ListGetMode.create && result == null)
        {
            result = new ObjEffectCache(serverId);
            _effectCacheList.Add(result);
        }

        return result;
    }
    
    private void DestroyObj(Obj obj)
    {
        if (obj != null)
            obj.DestroyObj();
    }

    private void RemoveHidePlayerByServerId(int serverId)
    {
        // 注:存在其他玩家Obj未建立,但是数据存在的情况
        // 删除一个其他玩家后,重新监视是否拥有足够的其他玩家。注:其他玩家排序正常应该是严格队列,前面部分都有Obj,后面部分都没有Obj
        var playerIndex = _otherList.FindIndex(a => a.character.ServerID == serverId);
        if (playerIndex >= 0)
        {
            var otherPlayer = _otherList[playerIndex];
            _otherList.RemoveAt(playerIndex);
            if (!otherPlayer.isHide && !IsOtherFull())
            {
                if (MainPlayer != null)
                {
                    OtherPlayerVisibility visibility = null;
                    var minDistSqr = float.PositiveInfinity;
                    for (var i = 0; i < _otherList.Count; i++)
                        if (_otherList[i].isHide)
                        {
                            var distSqr = (_otherList[i].character.Position - MainPlayer.Position).sqrMagnitude;
                            if (distSqr < minDistSqr)
                            {
                                visibility = _otherList[i];
                                minDistSqr = distSqr;
                            }
                        }
                    if (visibility != null)
                        visibility.SetHide(false);
                }
            }
        }
    }
    
    public void CleanSceneObj(bool isCleanMainPlayer = false)
    {
        foreach (var otherObj in otherObjPools.Values)
            if (otherObj != null)
                otherObj.DestroyObj();
        otherObjPools.Clear();
        if (isCleanMainPlayer)
            MainPlayer = null;
        foreach (var obj in ObjPools.Values)
            // 不删除主角时,不摧毁主角的Obj,清空字典后再将主角Obj添加回去
            if (MainPlayer == null || obj != MainPlayer)
                DestroyObj(obj);
        ObjPools.Clear();
        deadNpcObjHolder.Clear();
        if (!isCleanMainPlayer && MainPlayer != null)
            AddPoolObj(MainPlayer);
        _otherList.Clear();
        _effectCacheList.Clear();
    }

    public bool IsOtherFull()
    {
        var count = 0;
        for (var i = 0; i < _otherList.Count; i++)
            if (!_otherList[i].isHide)
                count++;
        return count >= PlayerPreferenceData.ShowOtherPlayerCount;
    }

    //如果先创建NPC后创建主角的话,NPC名字颜色等会表现异常,服务器大爷说他们是先发的创建的主角的协议的,没办法客户端这边先处理一下吧
    public void OnCreateMainPlayer()
    {
        var valus = new List<Obj>(ObjPools.Values);
        for (var i = 0; i < valus.Count; i++)
        {
            var Character = valus[i] as Obj_Character;
            if (Character != null)
                Character.OptForceChange();
        }
    }

    public void SetMainPlayer(ObjParent player)
    {
        var mainPlayer = player as Obj_MainPlayer;
        if (mainPlayer != null)
            MainPlayer = mainPlayer;
    }

    public ObjParent NewCharacterObj(GameDefine_Globe.OBJ_TYPE type, ObjParent_Init_Data initData)
    {
        ObjParent newObj = null;
        // 注:非服务器记录的Obj在读盘期间应该正常响应
        if (NeedCreate(initData as Obj_Init_Data))
            switch (type)
            {
                case GameDefine_Globe.OBJ_TYPE.OBJ_MAIN_PLAYER:
                {
                    if (MainPlayer == null)
                    {
                        MainPlayer = ObjParent.Create<Obj_MainPlayer>(initData, "Prefab/Model/PlayerRoot");
                        newObj = MainPlayer;
                        if (MainPlayer != null)
                        {
                            //Object.DontDestroyOnLoad(MainPlayer.gameObject);
                            //m_bBeginAsycCreateMainPlayer = true;
                            AddPoolObj(MainPlayer);
                            OnCreateMainPlayer();
                            ApplyCachedEffect(MainPlayer, GlobeVar.INVALID_ID);
                            ApplyCachedEffect(MainPlayer);
                        }
                    }
                    else
                    {
                        MainPlayer.Init(initData);
                    }
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_NPC:
                {
                    if (initData is Obj_Init_Data)
                        newObj = CreateCharacter<Obj_NPC>(initData, "Prefab/Model/NPCRoot");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_FELLOW:
                {
                    newObj = CreateCharacter<Obj_Fellow>(initData, "Prefab/Model/FellowRoot");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_OTHER_PLAYER:
                {
                    var otherPlayerData = initData as Obj_Player_Init_Data;
                    if (otherPlayerData != null)
                    {
                        var isHide = IsOtherFull();
                        otherPlayerData.m_hideMask = OtherPlayerVisibility.GetHideMask(isHide);
                        var otherPlayer = CreateOtherPlayer(otherPlayerData);
                        var visibilityData = new OtherPlayerVisibility(otherPlayer, isHide);
                        _otherList.Add(visibilityData);
                        newObj = otherPlayer;
                    }
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_ZOMBIE_PLAYER:
                {
                    newObj = CreateCharacter<Obj_ZombieUser>(initData, "Prefab/Model/OtherPlayerRoot");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_DROP_ITEM:
                {
                    newObj = ObjParent.Create<Obj_DropItem>(initData, "Prefab/Model/DropItemInfo");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_GHARRY:
                {
                    newObj = CreateCharacter<Obj_GuildGharry>(initData, "Prefab/Model/FellowRoot");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_WEDDINGCAR:
                    {
                        newObj = CreateCharacter<Obj_WeddingCar>(initData, "Prefab/Model/NPCRoot");
                    }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_CHILD:
                    {
                        newObj = CreateCharacter<Obj_Child>(initData, "Prefab/Model/NPCRoot");
                    }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_SNARE:
                {
                    newObj = ObjParent.Create<Obj_Snare>(initData, "Prefab/Model/SnareRoot");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_COLLECTITEM:
                {
                    newObj = ObjParent.Create<Obj_CollectItem>(initData, "Prefab/Model/CollectItemRoot");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_TELEPORTPOINT:
                {
                    newObj = ObjParent.Create<Obj_TeleportPoint>(initData, "Prefab/Model/Teleport");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_JUMPPOINT:
                {
                    newObj = ObjParent.Create<Obj_JumpPoint>(initData, "Prefab/Model/Jump");
                }
                    break;
                case GameDefine_Globe.OBJ_TYPE.OBJ_CLIENTNPC:
                {
                    newObj = CreateCharacter<Obj_ClientNPC>(initData, "Prefab/Model/NPCRoot");
                }
                    break;
            }

        return newObj;
    }

    //根据ServerID来查找Obj
    public Obj FindObjInScene(int nServerID)
    {
        Obj result;
        ObjPools.TryGetValue(nServerID, out result);
        return result;
    }

    //根据ServerID来查找Obj_Character
    public Obj_Character FindObjCharacterInScene(int nServerID)
    {
        return FindObjInScene(nServerID) as Obj_Character;
    }

    public Obj_OtherPlayer FindOtherPlayerByGuid(ulong guid)
    {
        if (MainPlayer != null && MainPlayer.GUID == guid)
            return MainPlayer;
        var visibility = _otherList.Find(a => a.character.GUID == guid);
        return visibility == null ? null : visibility.character;
    }

    //根据Obj_Character的BaseAttr中的RoleID来查找NPC
    //遍历,不推荐反复使用
    public Obj_Character FindObjCharacterInSceneByRoleID(int roleID, bool bIsAlive = true,bool isNearest = false)
    {
        Obj_Character objTarget = null;
        float distance = -1;
        foreach (var obj in ObjPools.Values)
        {
            var objChar = obj as Obj_NPC;
            if (objChar && objChar.BaseAttr != null && objChar.BaseAttr.RoleBaseID == roleID)
            {
                //是否要寻找非死亡目标
                if (bIsAlive && objChar.IsDie())
                    continue;
                if(isNearest==false)
                    return objChar;
                else
                {
                    float dis = Vector3.Distance(Singleton<ObjManager>.Instance.MainPlayer .Position, objChar.Position);
                    if (distance == -1 || dis < distance)
                    {
                        distance = dis;
                        objTarget = objChar;
                    }
                }
            }
        }

        return objTarget;
    }

    //根据Obj_Character的BaseAttr中的名字来查找NPC
    //遍历,不推荐反复使用
    public Obj_Character FindObjCharacterInSceneByName(string szBaseAttrName, bool bIsAlive = true)
    {
        Obj_Character objTarget = null;
        var minDistanceSqr = 8f * 8f;
        foreach (var obj in ObjPools.Values)
        {
            var objChar = obj as Obj_NPC;
            if (objChar == null)
                continue;
            string roleName = objChar.BaseAttr.RoleName;
            if (objChar.ObjType == GameDefine_Globe.OBJ_TYPE.OBJ_NPC && objChar.BaseAttr.RoleData != null && string.IsNullOrEmpty(objChar.BaseAttr.RoleData.Name) == false)
                roleName = objChar.BaseAttr.RoleData.Name;

            if (objChar && objChar.BaseAttr != null && string.Equals(roleName, szBaseAttrName,
                    StringComparison.InvariantCulture))
            {
                //是否要寻找非死亡目标
                if (bIsAlive && objChar.IsDie())
                    continue;
                var nType = Reputation.GetObjReputionType(objChar);
                if (nType == CharacterDefine.REPUTATION_TYPE.REPUTATION_INVALID
                    || nType == CharacterDefine.REPUTATION_TYPE.REPUTATION_FRIEND)
                {
                    objTarget = objChar;
                    break;
                }
                if (MainPlayer != null)
                {
                    var distanceSqr = (MainPlayer.Position - objChar.Position).sqrMagnitude;
                    if (distanceSqr < minDistanceSqr)
                    {
                        minDistanceSqr = distanceSqr;
                        objTarget = objChar;
                    }
                }
            }
        }

        return objTarget;
    }

    //查找某个玩家
    public Obj_OtherPlayer FindOtherPlayerInScene(ulong guid)
    {
        return FindOtherPlayerByGuid(guid);
    }

    public bool NeedCreate(Obj_Init_Data initData)
    {
        var result = true;
        if (initData != null)
        {
            var obj = FindObjCharacterInScene(initData.m_ServerID);
            if (obj != null)
            {
                obj.Init(initData);
                result = false;
            }
        }
        return result;
    }

    public void AddPoolOtherGameObj(string name, Obj obj)
    {
        if (!otherObjPools.ContainsKey(name))
            otherObjPools.Add(name, obj);
        else
            LogModule.ErrorLog("ObjManager AddPoolOtherGameObj already contain obj:" + name);
    }

    //根据Gameobject的Name在非Obj池查找
    public Obj FindOtherGameObj(string strName)
    {
        Obj result;
        otherObjPools.TryGetValue(strName, out result);
        return result;
    }

    public void UpdateHideMask(int hideMask)
    {
        for (var i = 0; i < _otherList.Count; i++)
            if (!_otherList[i].isHide && _otherList[i].character != null)
                _otherList[i].character.SetHideByMask(hideMask);
    }
    /// <summary>
    /// 强制刷新其他玩家数目
    /// </summary>
    public void RefreshOtherPlayerCount(int current)
    {
        int previous = 0;
        for (var i = 0; i < _otherList.Count; i++)
            if (!_otherList[i].isHide && _otherList[i].character != null)
                previous++;
        var delta = current - previous;
        if (delta > 0)
        {
            for (var i = 0; i < _otherList.Count; i++)
            {
                if (_otherList[i].isHide && _otherList[i].character != null)
                {
                    _otherList[i].SetHide(false);
                    delta--;
                    if (delta <= 0)
                        break;
                }
            }
        }
        else
        {
            for (var i = _otherList.Count - 1; i >= 0; i--)
            {
                if (!_otherList[i].isHide && _otherList[i].character != null)
                {
                    _otherList[i].SetHide(true);
                    delta++;
                    if (delta >= 0)
                        break;
                }
            }
        }
    }

    private Obj_OtherPlayer CreateOtherPlayer(ObjParent_Init_Data initData)
    {
        return CreateCharacter<Obj_OtherPlayer>(initData, "Prefab/Model/OtherPlayerRoot");
    }

    private T CreateCharacter<T>(ObjParent_Init_Data initData, string prefabPath) where T : Obj_Character
    {
        var character = ObjParent.Create<T>(initData, prefabPath);
        ApplyCachedEffect(character);
        return character;
    }

    private void ApplyCachedEffect(Obj_Character character, int? serverId = null)
    {
        var id = serverId ?? character.ServerID;
        var cache = GetEffectCache(id, ListGetMode.remove);
        if (cache != null)
        {
            if (cache.shieldCache != null)
                character.CreateShield(cache.shieldCache);
            for (var i = 0; i < cache.effectList.Count; i++)
            {
                var effect = cache.effectList[i];
                character.PlayEffect(effect.effectId, time: effect.startTime, handle: effect.handle);
            }
        }
    }

    /// <summary>
    ///     在没有完整的数据层之前,暂时使用这个数据来处理一些Obj协议接收顺序问题
    /// </summary>
    public class ObjEffectCache
    {
        public readonly List<ObjEffectCacheData> effectList = new List<ObjEffectCacheData>();
        public readonly int serverId;

        public ObjEffectCache(int serverId)
        {
            this.serverId = serverId;
        }

        public ObjShieldData shieldCache { get; private set; }

        public void AddEffect(int effectId, int? handle = null)
        {
            if (effectId > GlobeVar.INVALID_ID)
            {
                var effect = new ObjEffectCacheData(effectId, handle, Time.time);
                effectList.Add(effect);
            }
        }

        public void RemoveEffect(int effectId)
        {
            for (var i = effectList.Count - 1; i >= 0; i--)
                if (effectList[i].effectId == effectId)
                    effectList.RemoveAt(i);
        }

        public void RemoveEffectByHandle(int handle)
        {
            for (var i = effectList.Count - 1; i >= 0; i--)
                if (effectList[i].handle != null && effectList[i].handle.Value == handle)
                    effectList.RemoveAt(i);
        }

        public void AddShield(ObjShieldData shieldData)
        {
            shieldCache = shieldData;
        }

        public void UpdateShield(long handle, long health)
        {
            if (shieldCache != null && shieldCache.handle == handle)
                shieldCache.health = health;
        }

        public void RemoveShield(int handle)
        {
            if (shieldCache != null && shieldCache.handle == handle)
                shieldCache = null;
        }
    }

    public class ObjEffectCacheData
    {
        public readonly int effectId;
        public readonly int? handle;
        public readonly float startTime;

        public ObjEffectCacheData(int effectId, int? handle, float startTime)
        {
            this.effectId = effectId;
            this.handle = handle;
            this.startTime = startTime;
        }
    }
}