using CommonAI;
using CommonAI.Zone.Instance;
using CommonAI.ZoneServer.JSGModule;
using CommonLang;
using CommonLang.Concurrent;
using CommonLang.Log;
using Pomelo;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Dynamic;
using System.Threading;
using System.Web.Helpers;
using XmdsCommon.Plugin;
using XmdsCommonServer.Plugin;
using XmdsServerNode.Node;
using XmdsServerNode.Node.R2bNotify;
using static CommonAI.ZoneServer.JSGModule.JSGServerProfile;

namespace XmdsServerEdgeJS.Zone
{
	public abstract class ZoneService : IZone
	{
		//战斗服版本号
		public static int s_bsVersion = 1;

		public static ZoneService Instance { get; private set; }
		protected ZoneService()
		{
			Instance = this;
			updatePrivateMemory(null);
		}

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

		protected Logger log = LoggerFactory.GetLogger(typeof(ZoneService).Name);
		protected Logger monitorLog = LoggerFactory.GetLogger("monitor");
		private ZoneNodeManager zoneNodeManager;
		private BattleCodec codec;
		private AtomicLong privateMemoryMB = new AtomicLong(0);
		private Timer systemUpdateTimer;
		private Timer logUpdateTimer;
		protected XmdsServerProxy b2r_proxy = new XmdsServerProxy();

		/// <summary>
		/// 副本列表
		/// </summary>
		private HashMap<string, XmdsZoneNode> nodes = new HashMap<string, XmdsZoneNode>();

		/// <summary>
		/// 玩家列表
		/// </summary>
		private HashMap<string, XmdsPlayer> players = new HashMap<string, XmdsPlayer>();


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

		public int PlayerCount
		{
			get
			{
				lock (players)
				{
					return players.Count;
				}
			}
		}
		public int ZoneNodeCount
		{
			get
			{
				lock (nodes)
				{
					return nodes.Count;
				}
			}
		}

		public long ProcessPrivateMemoryMB
		{
			get { return privateMemoryMB.Value; }
		}
		//----------------------------------------------------------------------------------------------
		#region Internal

		public XmdsPlayer getPlayer(string playerID)
		{
			lock (players)
			{
				return players.Get(playerID);
			}
		}
		protected XmdsZoneNode getZoneNode(string instanceID)
		{
			lock (nodes)
			{
				return nodes.Get(instanceID);//[instanceID];
			}
		}
		//internal void sendToGameServer(string name, Object param)
		//{
		//    try
		//    {
		//        //转化为json          
		//        string json = Json.Encode(param);
		//        IceManager.instance().eventNotify(name, json);
		//    }
		//    catch (Exception err)
		//    {
		//        log.Error(err.Message, err);
		//    }
		//}
		protected void notifyBattleServer(string instanceId, R2BNotifyMessage param)
		{
			var node = getZoneNode(instanceId);
			if (node != null)
			{
				//b2r_proxy.SendToBattleServer(node.Node.Zone, param);
				param.OnHandle(node.Node.Zone);
			}
		}
		private void updatePrivateMemory(object state)
		{
			long private_memory_mb = Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024;
			privateMemoryMB.Value = private_memory_mb;
		}

		private void updateLog(object state)
		{
			dynamic eventStat = new ExpandoObject();
			eventStat.Type = "monitor";
			eventStat.Object = InstanceZoneObject.ActiveObjectCount + "/" + InstanceZoneObject.AllocObjectCount;
			eventStat.Zone = InstanceZone.ActiveZoneCount + "/" + InstanceZone.AllocZoneCount;
			eventStat.Node = ZoneNodeCount;
			eventStat.Player = PlayerCount;
			eventStat.Memory_MB = ProcessPrivateMemoryMB;
			eventStat.Pool = ObjectPoolStatus.TotalActive + "/" + ObjectPoolStatus.TotalCount;
			monitorLog.Debug(eventStat);

			// 输出统计日志
			if (JSGServerProfile.CheckPrintAllData())
			{
				this.PrintAllSceneInfo();
			}
		}


		private void PrintAllSceneInfo()
		{
			lock (nodes)
			{
				foreach (var p in new List<XmdsZoneNode>(nodes.Values))
				{
					try
					{
						string ext = p.Node.Zone.IsObjectMapNull() ? ", 场景已销毁:" : ", units=" + p.Node.Zone.AllUnitsCount
							+ ", spells=" + p.Node.Zone.AllSpellsCount + ", items=" + p.Node.Zone.AllItemsCount + ", players: " + p.Node.Zone.AllPlayersCount;

						log.Info("PrintAllSceneInfo-ID:" + p.InstanceID + ", " + p.Node.GetBindGameSrvId() + ", 场景ID: " + p.Node.SceneID + ext);
					}
					catch (Exception e)
					{
						log.Error("PrintAllSceneInfo: " + p.InstanceID + ", e: " + e);
					}
				}
			}
		}

		#endregion
		//----------------------------------------------------------------------------------------------

		public void Start(ZoneConfig zoneConfig)
		{
			lock (this)
			{
				try
				{
					log.Info("startPath:" + zoneConfig.startPath);
					log.Info("assetPath:" + zoneConfig.assetPath);
					JSGServerProfile.init();

					systemUpdateTimer = new Timer(updatePrivateMemory, this, 0, 10000);
					zoneNodeManager = new ZoneNodeManager();
					zoneNodeManager.Init(zoneConfig.startPath, b2r_proxy, zoneConfig.assetPath);
					codec = new BattleCodec(ZoneNodeManager.Templates.Templates);

					//日志定时器
					logUpdateTimer = new Timer(updateLog, this, 60000, 60000);
				}
				catch (Exception err)
				{
					//log.Error("初始化失败");
					//log.Error(err.Message, err);
					//Environment.Exit(0);
					throw err;
				}
			}
		}

		public void Stop()
		{

			this.ClearAllPlayers((e, c) => { });
			this.DestoryAllZones((e, c) => { });
			lock (this)
			{
				if (zoneNodeManager != null)
				{
					b2r_proxy.Dispose();
					zoneNodeManager.Shutdown();
					systemUpdateTimer.Dispose();
					logUpdateTimer.Dispose();
					zoneNodeManager = null;
					codec = null;
				}
			}
		}

		public void SetCallBack(string gameSrvId)
		{
			JSGMountainKingModule.Init(gameSrvId);
		}

		public void CreateZone(string playerId, string gameServerId, int mapTemplateId, string instanceId, bool forceCreate,
			string data, Action<int> cb, Action<Exception> err)
		{
			long lstart = TimeUtil.GetTimestampMS();
			int resCode = 0;
			if (!forceCreate && playerId != null && playerId.Length > 0)
			{
				var player = getPlayer(playerId);
				if (player != null && player.BindingActor.IsActive && player.BindingActor.Virtual.IsInPVP())
				{
					resCode = 1;
					XmdsVirtual playerVirtual = player.BindingActor.Virtual as XmdsVirtual;
					log.Warn("PVP状态下传送2:" + playerVirtual.mUnit.PlayerUUID + ", 场景ID: " + playerVirtual.mUnit.Parent.GetSceneID() + ",hateInfo: " 
						+ playerVirtual.GetHateSystem().GetHatePlayerInfo() + ", 触发PVP玩家:" + playerVirtual.mPvpTriggerPlayerId);

				}
			}

			if (resCode == 0)
			{
				long private_memory_mb = ProcessPrivateMemoryMB;
				if (private_memory_mb > ZoneNodeManager.NodeConfig.SYSTEM_MAX_HEAP_SIZE_MB)
				{
					string msg = string.Format("CreateZone failed : Private memory {0}MB out of range {1}MB", private_memory_mb, ZoneNodeManager.NodeConfig.SYSTEM_MAX_HEAP_SIZE_MB);
					log.Error(msg);
					throw new OutOfMemoryException(msg);
				}

				XmdsZoneNode node = new XmdsZoneNode(this, instanceId, gameServerId);
				node.Node.Callback = IceManager.instance().getCallback(gameServerId);
				lock (nodes)
				{
					if (nodes.ContainsKey(instanceId))
					{
						throw new Exception(string.Format("node instance id ({0}) already exist!", instanceId));
					}
					nodes.Add(node.InstanceID, node);
				}

				{
					node.Start(mapTemplateId, data, (z) =>
					{
						//监控日志
						dynamic logEvent = new ExpandoObject();
						logEvent.type = "createZone";
						logEvent.mapTemplateId = mapTemplateId;
						logEvent.instanceId = instanceId;
						monitorLog.Debug(logEvent);
					});
				}

				JSGServerProfile.RecordZoneCreate(mapTemplateId, (int)(TimeUtil.GetTimestampMS() - lstart));
			}
			else
			{
				log.Warn("玩家pvp状态创建场景:" + playerId + ", " + mapTemplateId + ", " + data);
			}

			cb(resCode);
		}

		public void DestroyZone(string instanceId, Action<Exception, object> cb)
		{

			XmdsZoneNode node;
			lock (nodes)
			{
				node = this.nodes.RemoveByKey(instanceId);
			}
			if (node != null)
			{
				//删除场景实例的所有玩家
				lock (players)
				{
					node.Node.ForEachPlayers((client) =>
					{
						var player = players.Get(client.PlayerUUID);
						if (player != null)
						{
							if (client.Node == player.Node)
							{
								players.RemoveByKey(client.PlayerUUID);
							}
							//else
							//{
							//    log.Warn(client.Node + " destory zone player " + client.PlayerUUID + " in " + player.Node);
							//}
						}
					});
				}
				node.Stop((z) =>
				{
					//监控日志
					dynamic logEvent = new ExpandoObject();
					logEvent.type = "destroyZone";
					logEvent.mapTemplateId = z.Node.SceneID;
					logEvent.instanceId = instanceId;
					monitorLog.Debug(logEvent);
				});
			}
			cb(null, "done");
		}

		public void DestoryAllZones(Action<Exception, object> cb)
		{
			lock (nodes)
			{
				foreach (var p in new List<XmdsZoneNode>(nodes.Values))
				{
					try
					{
						this.DestroyZone(p.InstanceID, (e, c) => { });
					}
					catch (Exception e)
					{
						log.Error(e);
					}
				}
			}
			cb(null, "done");
		}

		public void PlayerEnter(string playerId, string instanceId, string input, Action<Exception, object> cb)
		{
			var node = getZoneNode(instanceId);
			if (node == null)
			{
				throw new InstanceNotExistException(instanceId);
			}

			long private_memory_mb = ProcessPrivateMemoryMB;
			if (private_memory_mb > ZoneNodeManager.NodeConfig.SYSTEM_MAX_HEAP_SIZE_MB)
			{
				string msg = string.Format("PlayerEnter failed : Private memory {0}MB out of range {1}MB", private_memory_mb, ZoneNodeManager.NodeConfig.SYSTEM_MAX_HEAP_SIZE_MB);
				log.Error(msg);
				throw new OutOfMemoryException(msg);
			}
			dynamic data = Json.Decode(input);
			string connectServerId = (string)data.connectServerId;
			var session = FastStream.instance().GetSessionByID(connectServerId);
			if (session == null)
			{
				throw new Exception("ConnectServerId not exist : " + connectServerId);
			}
			string uid = (string)data.uid;
			bool robot = data.robot != null ? (bool)data.robot : false;

			XmdsPlayerEnter enter = null;

			if (data["tempData"] != null)
			{
				int posX = 0;
				int posY = 0;
				float direction = 0;
				string flagName = null;
				flagName = data.tempData.flag;
				if (data.tempData["x"] != null)
				{
					posX = (int)data.tempData.x;
				}
				if (data.tempData["y"] != null)
				{
					posY = (int)data.tempData.y;
				}
				if (data.tempData["direction"] != null)
				{
					direction = (float)data.tempData.direction;
				}


				int unitTemplateID = (int)data.unitTemplateID;

				XmdsUnitProperties unitprop = new XmdsUnitProperties();
				XmdsUnitData unitData = XmdsPlayerUtil.instance().createXmdsUnitData(data, playerId);
				unitprop.ServerData = unitData;

				enter = new XmdsPlayerEnter();
				enter.Pos = new CommonLang.Vector.Vector2(posX, posY);
				enter.direction = direction;
				enter.FlagName = flagName;
				enter.UnitData = new CommonAI.ZoneServer.CreateUnitInfoR2B();
				enter.UnitData.Force = unitData.BaseInfo.force;
				enter.UnitData.alliesForce = unitData.BaseInfo.alliesForce;
				enter.UnitData.StartFlag = null;
				enter.UnitData.UnitTemplateID = unitTemplateID;
				enter.UnitData.UnitPropData = unitprop;

				foreach (dynamic obj in data.tasks)
				{
					int taskId = obj.QuestID;
					int state = obj.State;
					XmdsQuestData qData = new XmdsQuestData();
					qData.TaskID = taskId.ToString();
					qData.TaskState = state.ToString();
					foreach (dynamic attr in obj.Attributes)
					{
						qData.AddAttribute(attr.key, attr.value);
					}
					unitData.Tasks.Add(qData);
				}

				foreach (dynamic obj in data.flags)
				{
					XmdsQuestFlag flag = new XmdsQuestFlag();
					flag.FlagName = obj[0];
					flag.FlagValue = obj[1];
					unitData.QuestFlags.Add(flag);
				}
				unitData.PlayerEntered = data.playerEntered;

			}

			XmdsPlayer player = new XmdsPlayer(this, playerId, uid, session, node);
			lock (players)
			{
				if (players.ContainsKey(playerId))
				{
					this.players[playerId] = player;
					//string oldInstanceId = players.Get(playerId).InstanceId;
					//PlayerLeave(playerId, oldInstanceId, false, (err, ret) =>
					//{
					//    if (err != null)
					//    {
					//        log.Error(err);
					//    }
					//});
				}
				else
				{
					this.players.Add(playerId, player);
				}

				node.Node.OnPlayerEnter(player, enter,
					(c) =>
					{
						player.BindingActor.IsRobot = robot;
						//监控日志
						dynamic logEvent = new ExpandoObject();
						logEvent.type = "playerEnter";
						logEvent.mapTemplateId = node.Node.SceneID;
						logEvent.instanceId = instanceId;
						logEvent.playerId = playerId;
						monitorLog.Debug(logEvent);
						//cb(null, "done");
					},
					(e) =>
					{
						log.Error(e.Message, e);
						lock (this.players) { this.players.RemoveByKey(playerId); }
						//cb(e, null);
					});
			}
			cb(null, "done");
		}

		public void PlayerLeave(string playerId, string instanceId, bool keepObject, Action<Exception, object> cb)
		{
			XmdsPlayer player = null;
			try
			{
				lock (players)
				{
					player = players.Get(playerId);
					if (player != null)
					{
						if (instanceId.Equals(player.InstanceId))
						{
							this.players.RemoveByKey(playerId);
						}
						else
						{
							log.Warn(instanceId + " leave player " + player.InstanceId + player.Node);
						}
					}
				}
				var node = getZoneNode(instanceId);
				if (node == null)
				{
					//throw new InstanceNotExistException(instanceId);
					cb(null, "done");
					return;
				}
				//玩家已经离开场景
				//if (player == null)
				//{
				//    throw new PlayerNotExistException(playerId);
				//}
				node.Node.OnPlayerLeave(player != null ? player.BindingActor : node.Node.GetPlayer(playerId),
					(c) =>
					{

						//监控日志
						dynamic logEvent = new ExpandoObject();
						logEvent.type = "playerLeave";
						logEvent.mapTemplateId = node.Node.SceneID;
						logEvent.playerId = playerId;
						monitorLog.Debug(logEvent);
						//cb(null, "done");
					},
					(e) =>
					{
						//cb(e, null);
						log.Error(e.Message, e);
					},
					keepObject);
				cb(null, "done");
			}
			catch (Exception e)
			{
				log.Error(e.Message, e);
				cb(e, null);
			}
			finally
			{
				if (player != null) player.Dispose();
			}
		}

		public void PlayerNetStateChanged(string playerId, string state)
		{
			var player = getPlayer(playerId);
			if (player == null)
			{
				log.Error("player not exist :" + playerId + " PlayerNetStateChanged");
				return;
			}
			var node = player.Node;
			if (node == null)
			{
				log.Error("zone node not exist :" + player.InstanceId + " PlayerNetStateChanged");
				return;
			}
			log.Debug("player:" + playerId + " PlayerNetStateChanged " + state);
			node.OnPlayerNetStateChanged(player, state);
		}

		public void PlayerReceive(string playerId, byte[] msg)
		{
			var player = getPlayer(playerId);
			if (player == null)
			{
				log.Debug("fast stream receive not exist player : " + playerId);
				return;
			}
			var node = player.Node;
			if (node == null)
			{
				log.Error("zone node not exist : " + player.InstanceId + " fast stream receive");
				return;
			}
			//log.Debug("player:" + playerId + " socket.receive");
			node.OnPlayerReceivedMessage(player, msg);
		}

		public void GetAllPlayerCount(Action<Exception, int> cb)
		{
			int count = PlayerCount;
			cb(null, count);
		}

		public void ClearAllPlayers(Action<Exception, object> cb)
		{
			try
			{
				lock (players)
				{
					foreach (var p in new List<XmdsPlayer>(players.Values))
					{
						this.PlayerLeave(p.PlayerUUID, p.InstanceId, false, (e, c) => { });
					}
				}
				cb(null, "done");
			}
			catch (Exception e)
			{
				cb(e, null);
			}
		}

		public void GetAllPlayers(Action<Exception, object> cb)
		{
			try
			{
				var ret = new List<string>();
				lock (players)
				{
					foreach (var p in new List<XmdsPlayer>(players.Values))
					{
						ret.Add(p.PlayerUUID);
					}
				}
				cb(null, ret.ToArray());
			}
			catch (Exception e)
			{
				cb(e, null);
			}
		}

		public void GetServerState(string serverID, Action<Exception, object> cb)
		{
			try
			{
				int zc = ZoneNodeCount;
				int pc = PlayerCount;
				int ActiveObjectCount = InstanceZoneObject.ActiveObjectCount;
				int AllocObjectCount = InstanceZoneObject.AllocObjectCount;
				int ActiveZoneCount = InstanceZone.ActiveZoneCount;
				int AllocZoneCount = InstanceZone.AllocZoneCount;
				float w = zc * 1000 + pc;

				int flag = IceManager.instance().getCallback(serverID) == null ? 0 : 1;
				dynamic ret = new
				{
					weight = w,
					memory = ProcessPrivateMemoryMB,
					zone_count = zc,
					player_count = pc,
					ActiveObjectCount = ActiveObjectCount,
					AllocObjectCount = AllocObjectCount,
					ActiveZoneCount = ActiveZoneCount,
					AllocZoneCount = AllocZoneCount,
					flag = flag,
				};
				cb(null, ret);
			}
			catch (Exception e)
			{
				log.Error("GetServerState failed, error: " + e.Message, e);
				cb(e, e.Message);
			}
		}

		public void RegisterGameServer(int serverId, int crossId, Action<Exception, int> cb)
		{
			try
			{
				var str = ConfigurationManager.AppSettings["game.server.id"];
				if (!string.IsNullOrEmpty(str) && int.Parse(str) != serverId && int.Parse(str) != crossId)
				{
					cb(null, -1);
					return;
				}
			}
			catch (Exception e)
			{
				cb(e, -2);
				return;
			}

			cb(null, s_bsVersion);
		}

		//获取单位血量
		public void GetUnitHP(string instanceId, int objectId, Action<Exception, int> cb)
		{
			try
			{
				var node = getZoneNode(instanceId);
				if (node == null)
				{
					cb(null, -1);
					return;
				}

				InstanceUnit unit = node.Node.Zone.getUnit((uint)objectId);
				if (unit == null)
				{
					cb(null, -2);
					return;
				}

				cb(null, unit.CurrentHP);
			}
			catch (Exception e)
			{
				cb(e, -10);
				return;
			}
		}
		//----------------------------------------------------------------------------------------------
	}
}