using CommonLang.Log;
using CommonAI.Zone;
using CommonAI.Zone.Instance;
using System;
using System.Collections.Generic;
using System.Web.Helpers;
using XmdsServerNode.Node;
using XmdsCommon.Message;
using XmdsCommon.Plugin;
using XmdsCommonServer.Plugin;
using XmdsCommonServer.Plugin.Units;
using CommonAI.Zone.ZoneEditor;
using XmdsCommon.EditorData;
using CommonLang.Vector;
using CommonLang;
using static CommonAI.XmdsConstConfig;
using CommonAI.Data;

namespace XmdsServerEdgeJS.Zone
{
    public class XmdsZoneNode
    {
        private readonly Logger log;
        private readonly ZoneService service;
        private readonly string instanceID;
        private readonly ServerZoneNode node;

        //连斩生效时间
        private long continueKillsValidTime = 0;

        public string InstanceID { get { return instanceID; } }
        public ServerZoneNode Node { get { return node; } }

        public void SendToGameServer(string name, Object param)
        {
            node.SendToGameServer(name, param);
        }

        public string GetBindGameSrvId()
        {
            return node.GetBindGameSrvId();
        }

        internal XmdsZoneNode(ZoneService svc, string instanceId, string gameServerId)
        {
            this.log = LoggerFactory.GetLogger(string.Format("XmdsZoneNode-{0}", instanceId));
            this.service = svc;
            this.instanceID = instanceId;
            this.node = new ServerZoneNode(gameServerId);
        }

        internal void Start(int mapTemplateId, string data, Action<XmdsZoneNode> started)
        {
            dynamic config = Json.Decode(data);
            this.node.OnZoneStart += (z) => { started(this); };
            string monsterHard = (string)config.monsterHard;
            byte sceneType = (byte)config.sceneType;
            byte areaType = (byte)config.areaType;
            int averageLevel = Convert.ToInt32(config.averageLevel);
            double propRatio = 0;
            double floorRatio = 0;
            if (averageLevel > 0)
            {
                propRatio = Convert.ToDouble(config.propRatio);
                floorRatio = Convert.ToDouble(config.floorRatio);
            }
            bool canRiding = true;
            bool isteam = config.isTeam == null ? false : (bool)config.isTeam;
            if (config.canRiding != null)
            {
                canRiding = config.canRiding;
            }

            int killInterval = (config.killInterval != null) ? (int)(config.killInterval) : 0;
            int killFullNum = (config.killFullNum != null) ? (int)(config.killFullNum) : 0;
            int killFullCollSec = (config.killFullCollSec != null) ? (int)(config.killFullCollSec) : 0;

            GSCreateAreaData gsData = new GSCreateAreaData();
            gsData.value1 = (config.value1 != null) ? ((int)(config.value1)) : 0;
            gsData.value2 = (config.value2 != null) ? ((int)(config.value2)) : 0;
            this.node.Start(instanceID, mapTemplateId, config.monsterHard, (byte)config.allowAutoGuard, config.calPKValue,
                averageLevel, floorRatio, propRatio, killInterval, killFullNum, killFullNum, config.usespaceDiv,
                (CommonAI.Data.SceneType)sceneType, gsData, (AreaType)areaType, isteam, canRiding);
            this.node.Zone.OnUnitDead += zone_onUnitDead;
            //this.node.Zone.OnGameOver += zone_onGameOver;
            this.node.Zone.OnUnitGotInstanceItem += zone_onUnitGotInstanceItem;
            this.node.Zone.OnUnitPickUnit += zone_onUnitPickUnit;
            this.node.Zone.OnSendMessageToGS += zone_onSendMessageToGS;
            this.node.Zone.OnTryPickItem += zone_onTryPickItem;
        }


        internal void Stop(Action<XmdsZoneNode> stopped)
        {
            if (node.IsDisposed)
            {
                stopped(this);
            }
            else
            {
                node.OnZoneStop += (z) => { stopped(this); };
                node.Stop();
            }


            if(this.node != null && this.node.Zone != null)
            {
                this.node.Zone.OnUnitDead -= zone_onUnitDead;
                //this.node.Zone.OnGameOver -= zone_onGameOver;
                this.node.Zone.OnUnitGotInstanceItem -= zone_onUnitGotInstanceItem;
                this.node.Zone.OnUnitPickUnit -= zone_onUnitPickUnit;
                this.node.Zone.OnSendMessageToGS -= zone_onSendMessageToGS;
                this.node.Zone.OnTryPickItem -= zone_onTryPickItem;
            }
        }

        //------------------------------------------------------------------------------------------------------

        private HashMap<int, TVector2> _BornPlace = null;
        private HashMap<String, TVector2> _BornPlaceExt = null;

        public TVector2 r2b_get_getBornPlace(int areaId, string pointId)
        {
            lock (this)
            {
                if (_BornPlace == null)
                {
                    _BornPlace = new HashMap<int, TVector2>();
                    _BornPlaceExt = new HashMap<string, TVector2>();
                    List<DecorationData> Decorations = this.Node.SceneData.Decorations;
                    foreach (DecorationData decoration in Decorations)
                    {
                        foreach (AbilityData ability in decoration.Abilities)
                        {
                            XmdsSceneTransportAbilityData sceneAbility = ability as XmdsSceneTransportAbilityData;
                            if (sceneAbility != null)
                            {
                                if (!string.IsNullOrEmpty(sceneAbility.NextScenePosition))
                                {
                                    string[] pos = sceneAbility.NextScenePosition.Split(new Char[] { ',' });
                                    TVector2 posInfo = new TVector2((float)Convert.ToSingle(pos[0]), (float)Convert.ToSingle(pos[1]));

                                    _BornPlace.Put(sceneAbility.NextSceneID, posInfo);
                                    _BornPlaceExt.Put(decoration.Name, posInfo);
                                }
                            }
                        }
                    }
                }
                TVector2 ret;
                if (pointId != null && _BornPlaceExt.TryGetValue(pointId, out ret))
                {
                    return ret;
                }
                else if (_BornPlace.TryGetValue(areaId, out ret))
                {
                    return ret;
                }
                return new TVector2(0, 0);
            }
        }

        //------------------------------------------------------------------------------------------------------
        #region _场景内事件_

        private bool zone_onTryPickItem(InstanceZone zone, InstanceUnit unit, InstanceItem item)
        {
            XmdsInstancePlayer player = unit as XmdsInstancePlayer;
            XmdsItemProperties prop = item.Info.Properties as XmdsItemProperties;

            if (player != null && prop != null && prop.IsTaskItem)
            {
                List<CommonAI.Zone.Helper.QuestData> quests = player.GetQuests();
                for (int i = 0; i < quests.Count; i++)
                {
                    var q = quests[i];
                    var t = XmdsQuestData.GetTaskType(q);
                    var targetId = XmdsQuestData.GetTargetID(q);
                    var is_complete = XmdsQuestData.IsTargetComplete(q);
                    if (!is_complete && t == (int)XmdsQuestData.TaskType.InterActiveItem && targetId == item.Info.TemplateID)
                    {
                        return true;
                    }
                }
                return false;
            }
            else
            {
                return true;
            }
        }


        private void zone_onSendMessageToGS(InstanceZone zone, CommonAI.ZoneServer.SendMessageB2R e)
        {
            var eventParam = new
            {
                instanceId = zone.UUID,
                eventName = "message",
                msg = e.Message
            };
            node.SendToGameServer(EventType.areaEvent.ToString(), eventParam);
        }


        private void zone_onUnitPickUnit(InstanceZone zone, InstanceUnit obj, InstanceUnit pickable)
        {
            if (obj is InstancePlayer)
            {
                string uuid = (obj as InstancePlayer).PlayerUUID;
                var eventParam = new
                {
                    eventName = "interActiveItem",
                    playerId = uuid,
                    itemId = pickable.Info.TemplateID,
                    x = obj.X,
                    y = obj.Y
                };
                node.SendToGameServer(EventType.playerEvent.ToString(), eventParam);
            }
        }

        private void zone_onUnitGotInstanceItem(InstanceZone zone, InstanceUnit unit, InstanceItem item)
        {
            InstancePlayer player = (unit as InstancePlayer);
            string uuid = player.PlayerUUID;

            XmdsDropableInstanceItem it = item as XmdsDropableInstanceItem;
            XmdsItemProperties prop = it.Info.Properties as XmdsItemProperties;
            XmdsCommon.Message.DropItem dropitem = it.GenSyncInfo(false).ExtData as XmdsCommon.Message.DropItem;

            if (dropitem != null)
            {
                var eventParam = new
                {
                    eventName = "pickItem",
                    instanceId = zone.UUID,
                    playerId = uuid,
                    itemId = dropitem.ObjID,
                    isGuard = player.IsGuard,
                    objectId = item.Info.ID,
                    //type = prop.ItemType,
                    //templateID = it.Info.TemplateID
                };
                node.SendToGameServer(EventType.areaEvent.ToString(), eventParam);
                //u.queueEvent(new UnitGotInstanceItemEvent(u.ID, this.ID));
            }
            else
            {
                dynamic eventParam = new
                {
                    eventName = "interActiveItem",
                    playerId = uuid,
                    itemId = item.Info.TemplateID,
                    objId = item.ID,
                    type = prop.ItemType,
                    isRemove = item.IsPickAll(),
                    x = item.X,
                    y = item.Y
                };
                node.SendToGameServer(EventType.playerEvent.ToString(), eventParam);
            }
        }


        /// <summary>
        /// 单位死亡事件
        /// </summary>
        /// <param name="zone"></param>
        /// <param name="obj"></param>
        /// <param name="attacker"></param>
        private void zone_onUnitDead(InstanceZone zone, InstanceUnit obj, InstanceUnit attacker)
        {
            //玩家死亡为1,怪物死亡为0
            int type = 0;

            //玩家id
            string playerId = null;
            long pkTime = 0;
            int pkValue = 0;
            if (obj is XmdsInstancePlayer)
            {
                type = 1;//玩家.
                playerId = (obj as InstancePlayer).PlayerUUID;
                XmdsVirtual_Player virPlayer = obj.Virtual as XmdsVirtual_Player;
                pkTime = virPlayer.getPkTime();
                pkValue = virPlayer.getPkValue();
            }
            else if (obj is XmdsInstancePet && !(obj is XmdsInstanceSummonUnit))
            {
                type = 2;//宠物.

                var pet = obj as XmdsInstancePet;

                if (pet.Master != null && pet.Master is InstancePlayer)
                {
                    playerId = (pet.Master as InstancePlayer).PlayerUUID;
                }

            }

            int attackType = 0;
            //如果最后一击是宠物,则找主人
            if (attacker is InstancePet)
            {
                attacker = (attacker as InstancePet).Master;
                attackType = 1;
            }
            else if (attacker is InstanceSummon)
            {
                attacker = (attacker as InstanceSummon).SummonerUnit;
            }
            else if (attacker is XmdsInstanceSummonUnit)
            {
                attacker = (attacker as XmdsInstanceSummonUnit).SummonerUnit;
            }


            InstancePlayer hitFinal = attacker as InstancePlayer;

            //击中列表
            List<string> playerIds = new List<string>(2);
            //玩家被杀,改玩家添加到列表
            if (type == 1)
            {
                playerIds.Add(playerId);
            }

            if (hitFinal != null)
            {
                playerIds.Add(hitFinal.PlayerUUID);
            }

            #region 怪物ID特殊逻辑.

            //怪物生成时会随机出更高级的怪物,能力ID都会变更,但模板ID却一样。游戏服取MonsterID查找掉落和经验.Edit by Alex.Yu.

            int tpID = obj.Info.TemplateID;
            //击杀助攻列表.
            List<string> atkAssistantList = null;
            if (type == 0 && obj is XmdsInstanceMonster)
            {
                tpID = (obj as XmdsInstanceMonster).MonsterID;
                if (tpID <= 0)
                {
                    tpID = obj.Info.TemplateID;
                    log.Info("zone_onUnitDead单位id异常:" + obj.Info.TemplateID + ", " + obj.Info.Name + ", obj: " + obj + ", " + zone.GetSceneID());
                }
                atkAssistantList = (obj.Virtual as XmdsVirtual).mHateSystem.GetAtkAssistantList();
            }
            else if (type == 1)
            {
                atkAssistantList = (obj.Virtual as XmdsVirtual).mHateSystem.GetAtkAssistantList();
            }

            if (type != 2 && playerIds.Count == 0 && (atkAssistantList == null || atkAssistantList.Count == 0))
            {
                return;
            }

            #endregion
            //log.Info("monster dead objId:" + obj.ID + "   templateId:" + tpID + " type " + type);

            int aID = 0;
            int aQColor = 0;
            int aLevel = 0;
            string aName = "";
            string sceneType = "";
            if (attacker != null)
            {
                if (attacker is XmdsInstanceMonster)
                {
                    MonsterVisibleDataB2C mvd = attacker.GenSyncUnitInfo().VisibleInfo as MonsterVisibleDataB2C;
                    sceneType = (attacker.Virtual as XmdsVirtual).GetCurSceneType();

                    aID = attacker.Info.TemplateID;
                    aQColor = (int)mvd.Qcolor;
                    aName = mvd.DisplayName;
                    aLevel = attacker.GenSyncUnitInfo().Level;
                }
                else if (attacker.IsPlayerUnit && obj.IsMonster)
                {
                    InstanceUnit attackerUnit = attacker.IsPet ? attacker.Virtual.GetMasterUnit() : attacker;
                    if (attackerUnit != null)
                    {
                        attackerUnit.Virtual.DispatchKillOtherEvent(attackerUnit.Virtual, obj.Virtual);
                    }
                    else
                    {
                        log.Warn("zone_onUnitDead: 找不到人形攻击者:" + attacker.Info.ID + ", " + zone.GetSceneID());
                    }
                }
            }

            //连斩数计算
            if (zone is EditorScene)
            {
                EditorScene scene = zone as EditorScene;
                if (scene != null && scene.Data.killInterval > 0 && attacker != null)
                {
                    long curTime = CommonLang.CUtils.localTimeMS;

                    if (continueKillsValidTime < curTime && attacker.Statistic != null &&
                        curTime - attacker.Statistic.preKillTime < scene.Data.killInterval)
                    {
                        attacker.updateContinueKills();

                        //如果连斩有上限,并且达到上限后有一定冷却时间
                        if (scene.Data.killMax > 0 && scene.Data.killMaxCoolTime > 0 && attacker.Statistic.continueKills >= scene.Data.killMax)
                        {
                            attacker.Statistic.continueKills = 0;
                            this.continueKillsValidTime = curTime + scene.Data.killMaxCoolTime * 1000;
                        }
                    }
                    else
                    {
                        attacker.resetContinueKills();
                    }
                    attacker.Statistic.preKillTime = curTime;
                }
            }

            XmdsVirtual vir = (obj.Virtual as XmdsVirtual);
            XmdsHateSystem zhs = vir.GetHateSystem() as XmdsHateSystem;
            InstancePlayer belongPlayer = zhs.GetHeirs() as InstancePlayer;
            //Console.Write("attacker objId:" + aID + "   aQColor:" + aQColor + " aName " + aName + " aLevel  " + aLevel);
            var eventParam = new
            {
                eventName = "unitDead",
                playerIds = playerIds,
                areaId = zone.TerrainSrc.TemplateID,
                instanceId = zone.UUID,
                hitFinal = (hitFinal == null) ? null : hitFinal.PlayerUUID,
                belongPlayerId = (belongPlayer == null) ? null : belongPlayer.PlayerUUID,
                unitType = type,
                unitPlayerId = playerId,
                unitTemplateId = tpID,
                unitLevel = obj.Level,
                objId = obj.ID,
                posX = obj.get_x(),
                posY = obj.get_y(),
                attackerTemplateId = aID,
                attackerQColor = aQColor,
                attackerName = aName,
                attackerLevel = aLevel,
                attackerSceneType = sceneType,
                attackType = attackType,
                atkAssistantList = atkAssistantList,
                awardPlayer = zhs.GetSharedList(),
                refreshPoint = obj.GetAttribute("RefreshMonsterTag"),
                pkTime = pkTime,
                pkValue = pkValue,
                continuekills = attacker == null ? 0 : attacker.Statistic.continueKills,
                gsFlag = obj.gameServerFlag
            };

            if (obj.IsPlayer)
            {
                log.Info("玩家死亡:" + (obj == null ? "null" : obj.PlayerUUID) + ", 攻击者:" +
                    (attacker == null ? "null" : (attacker.PlayerUUID + ", " + attacker.Info.ID)) + ", Scene:" + zone.GetSceneID());
            }
            node.SendToGameServer(EventType.areaEvent.ToString(), eventParam);
        }


        #endregion
    }
}