using Microsoft.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;

namespace WS
{
	///<summary>TCP服务</summary>
	public sealed class TService : AService
    {
		///<summary>一个异步操作一个TChannel（一个Socket链接）</summary>
		private readonly Dictionary<long, TChannel> idChannels = new Dictionary<long, TChannel>();

		///<summary>Socket异步操作</summary>
		private readonly SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs();
		///<summary>Socket</summary>
		private Socket server;

		///<summary>性能的.NET流管理</summary>
		public RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();

		///<summary>需要开始发送的TChannel 哈希表</summary>
		public HashSet<long> needStartSendChannel = new HashSet<long>();

		///<summary>包头长度</summary>
		public int PacketSizeLength { get; }

		/// <summary>server</summary>
		/// <param name="packetSizeLength">包头长度</param>
		/// <param name="ipEndPoint">IP 端口</param>
		/// <param name="acceptCallback">Accept 接收回调</param>
		public TService(int packetSizeLength, IPEndPoint ipEndPoint, Action<AChannel> acceptCallback)
		{
			PacketSizeLength = packetSizeLength;
			AcceptCallback += acceptCallback;
			server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建服务器监听
			server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
			innArgs.Completed += OnComplete;//注册异步操作回调
			server.Bind(ipEndPoint);//创建Serer链接
			server.Listen(1000);
			Log.LogDebug("服务器启动成功!");
			AcceptAsync();
		}

		/// <summary>client</summary>
		public TService(int packetSizeLength)
		{
			PacketSizeLength = packetSizeLength;
		}

		public override void Dispose()
		{
			if (IsDisposed)
			{
				return;
			}

			base.Dispose();

			foreach (long id in idChannels.Keys.ToArray())
			{
				TChannel channel = idChannels[id];
				channel.Dispose();
			}
			server?.Close();
			server = null;
			innArgs.Dispose();
		}

		///<summary>异步操作回调</summary>
		private void OnComplete(object sender, SocketAsyncEventArgs e)
		{
			switch (e.LastOperation)
			{
				case SocketAsyncOperation.Accept://接收客户端链接操作
					OneThreadSynchronizationContext.Instance.Post(OnAcceptComplete, e);
					break;
				default:
					throw new Exception($"socket accept error: {e.LastOperation}");
			}
		}

		///<summary>接收客户端的Socket</summary>
		public void AcceptAsync()
		{
			innArgs.AcceptSocket = null;
			if (server.AcceptAsync(innArgs))//开始监听客服端链接=>如果返回值为true 表明挂起异步等待返回，如果返回值为false 表明回调完成跳过回调直接执行
			{
				return;
			}
			OnAcceptComplete(innArgs);
		}

		///<summary>接收客户端的Socket链接完成回调</summary>
		private void OnAcceptComplete(object o)
		{
			if (server == null) return;
			
			SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;

			if (e.SocketError != SocketError.Success)//如果不等于操作成功
			{
				Log.LogError($"accept error {e.SocketError}");
				AcceptAsync();//继续监听客户端链接
				return;
			}
			TChannel channel = new TChannel(e.AcceptSocket, this);//创建客户端Socket链接
			idChannels[channel.InstanceId] = channel;

			try
			{
				OnAccept(channel);//Socket链接成功 =》调用接收成功事件 到=》NetworkComponent
			}
			catch (Exception exception)
			{
				Log.LogError(exception);
			}

			if (server == null) return;

			AcceptAsync();//继续监听客户端链接
		}

		///<summary>获取Channel 渠道</summary>
		public override AChannel GetChannel(long id)
		{
			TChannel channel = null;
			idChannels.TryGetValue(id, out channel);
			return channel;
		}

		#region -- Channel --
		///<summary>链接Channel 渠道</summary>
		public override AChannel ConnectChannel(IPEndPoint ipEndPoint)
		{
			TChannel channel = new TChannel(ipEndPoint, this);
			idChannels[channel.InstanceId] = channel;

			return channel;
		}

		///<summary>链接Channel 渠道</summary>
		public override AChannel ConnectChannel(string address)
		{
			IPEndPoint ipEndPoint = NetworkHelper.ToIPEndPoint(address);
			return ConnectChannel(ipEndPoint);
		}

		///<summary>标记需要开始发送</summary>
		public void MarkNeedStartSend(long id)
		{
			needStartSendChannel.Add(id);
		}

		///<summary>删除Channel 渠道</summary>
		public override void Remove(long id)
		{
			TChannel channel;
			if (!idChannels.TryGetValue(id, out channel)) return;
			
			if (channel == null) return;
			
			idChannels.Remove(id);
			channel.Dispose();
		}

        #endregion

        public override void Update()
		{
			foreach (long id in needStartSendChannel)
			{
				TChannel channel;
				if (!idChannels.TryGetValue(id, out channel))
					continue;

				if (channel.IsSending)
					continue;

				try
				{
					channel.StartSend();
				}
				catch (Exception e)
				{
					Log.LogError(e);
				}
			}

			needStartSendChannel.Clear();
		}
	}
}