using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Vinno.IUS.Common.IO;
using Vinno.IUS.Common.Log;
namespace Vinno.IUS.Common.Network.Transfer
{
///
/// Message Pool
///
public static class MessagePool
{
private static int _maximumMsgOfEachType = 10;
private static bool _enabled;
private static readonly object _getMessageLock = new Object();
///
/// get Message pool is or not cached
///
public static bool Enabled => _enabled;
///
/// Maxinum of messages of each type
///
public static int MaximumMessageOfEachType => _maximumMsgOfEachType;
private static readonly ConcurrentDictionary MaxSpecialTypeMessageDictionary = new ConcurrentDictionary();
private static readonly ConcurrentDictionary> AvailableMessagePool = new ConcurrentDictionary>();
///
/// Get idle message from message pool
/// if all message of type of T is working ,so get new message
///
/// Message
/// T
public static T GetMessage() where T : Message
{
try
{
if (Enabled)
{
if (AvailableMessagePool.TryGetValue(typeof(T), out Stack messageStack))
{
lock (_getMessageLock)
{
if (messageStack.Count > 0)
{
var message = messageStack?.Pop();
if (message != null)
{
return message as T;
}
}
}
}
}
}
catch (Exception e)
{
throw new Exception($"{e}");
}
return NewMessage();
}
private static T NewMessage() where T : Message
{
var message = Activator.CreateInstance(typeof(T), new object[] { }) as Message;
return message as T;
}
///
/// Release current message
/// this message changed busy to idle
///
///
public static void Recover(Message message)
{
if (message == null)
{
return;
}
if (Enabled)
{
try
{
var messageType = message.GetType();
AvailableMessagePool.AddOrUpdate(messageType, s =>
{
var stack = new Stack();
stack.Push(RevertToMessage(message));
return stack;
}, (s, m) =>
{
var maximum = _maximumMsgOfEachType;
MaxSpecialTypeMessageDictionary.TryGetValue(messageType.Name, out int? specialMaximum);
if (specialMaximum != null)
{
maximum = specialMaximum.Value;
}
if (m.Count < maximum)
{
m.Push(RevertToMessage(message));
}
return m;
});
}
catch (Exception e)
{
throw new InvalidOperationException($"MessagePool Recover,Error:{e}");
}
}
}
private static Message RevertToMessage(Message message)
{
var properties = message.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
if (propertyInfo.CanWrite)
{
if (propertyInfo.Name == nameof(message.Element) || propertyInfo.Name == nameof(message.Tag))
{
continue;
}
var defaultvalue = GetDefaultValue(propertyInfo.PropertyType);
propertyInfo.SetValue(message, defaultvalue);
}
}
return message;
}
private static object GetDefaultValue(Type type)
{
object value = null;
switch (type.Name)
{
case nameof(String):
value = string.Empty;
break;
case nameof(Enum):
value = default(Enum);
break;
case nameof(Int32):
value = 0;
break;
case nameof(Int16):
value = 0;
break;
case nameof(Int64):
value = 0;
break;
case nameof(Single):
value = 0;
break;
case nameof(Double):
value = 0;
break;
case nameof(Boolean):
value = false;
break;
case nameof(DateTime):
value = default(DateTime);
break;
case nameof(IBuffer):
value = new ByteBuffer(new byte[0]);
break;
case "Byte[]":
value = new byte[0];
break;
}
return value;
}
public static void Initialize(IMessagePoolConfig config)
{
if (config !=null)
{
_enabled = config.Enabled;
_maximumMsgOfEachType = config.MaximumNumberOfMessages;
foreach (var key in config.CustomMessageCountDictionary)
{
MaxSpecialTypeMessageDictionary.GetOrAdd(key.Key, key.Value);
}
}
}
}
///
/// interface message pool config
///
public interface IMessagePoolConfig
{
///
/// MessagePool Enabled
///
bool Enabled { get; }
///
/// Maximum number of messages per type
///
int MaximumNumberOfMessages { get; }
///
/// custom message count,string:message name,int:maximum count of message
///
Dictionary CustomMessageCountDictionary { get; }
}
}