//==============================================================
//  版权所有：深圳杰文科技
//  文件名：ObjectMapper.cs
//  版本：V1.0  
//  创建者：Jay  ( QQ： 85363208 )
//  创建时间：2017-11-28 16:18
//  创建描述：
//  修改者：
//  修改时间：
//  修改说明： 
//==============================================================

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Reflection;
using System.Linq.Expressions;

namespace Infrastructure.Utilities
{
	public static class ObjectMapper
	{
		private static Dictionary<Type, List<PropertyInfo>> _Dict = new Dictionary<Type, List<PropertyInfo>>();

		private static List<PropertyInfo> ParseProperty(Type type)
		{
			List<PropertyInfo> list = new List<PropertyInfo>();
			PropertyInfo[] pies = type.GetProperties();
			foreach (PropertyInfo pi in pies)
			{
				if (!pi.CanWrite)
				{
					continue;
				}

				//只为简单类型赋值。
				if (pi.PropertyType == typeof(string) || pi.PropertyType == typeof(int) || pi.PropertyType == typeof(decimal) ||
					pi.PropertyType == typeof(double) || pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(bool) ||
					pi.PropertyType == typeof(short) || pi.PropertyType == typeof(long) || pi.PropertyType == typeof(byte[]) ||
					pi.PropertyType == typeof(Guid) || pi.PropertyType == typeof(Nullable<Guid>) ||
					pi.PropertyType == typeof(Nullable<int>) || pi.PropertyType == typeof(Nullable<decimal>) ||
					pi.PropertyType == typeof(Nullable<double>) || pi.PropertyType == typeof(Nullable<DateTime>) ||
					pi.PropertyType == typeof(Nullable<bool>) || pi.PropertyType == typeof(Nullable<short>) ||
					pi.PropertyType == typeof(Nullable<long>))
				{
					list.Add(pi);
				}
			}
			return list;
		}

		public static T Map<T>(T source) where T : new()
		{
			T destination = new T();
			Map<T>(source, destination);
			return destination;
		}

		public static List<T> Map<T>(IEnumerable<T> source) where T : new()
		{
			List<T> list = new List<T>();

			if (source == null)
			{
				return list;
			}

			foreach (T item in source)
			{
				T obj = new T();
				Map<T>(item, obj);
				list.Add(obj);
			}

			return list;
		}

		/*****************************************************/

		public static void Map<T>(T source, T destination)
		{
			Type type = typeof(T);

			if (source == null)
			{
				throw new Exception(string.Format("ObjectMapper {0}：source不能为空", type.FullName));
			}

			if (destination == null)
			{
				throw new Exception(string.Format("ObjectMapper {0}：destination不能为空", type.FullName));
			}

			if (!_Dict.ContainsKey(type))
			{
				List<PropertyInfo> list = ParseProperty(type);
				_Dict.Add(type, list);
			}

			foreach (PropertyInfo pi in _Dict[type])
			{
				object value = pi.GetValue(source, null);
				if (!CheckVersion<T>(destination, pi, ref value))
				{
					throw new Exception("{0} 数据已被其他业务修改".Fmt(type.Name));
				}

				pi.SetValue(destination, value, null);
			}
		}

		public static bool NotAllowCheckVersion
		{
			get;
			set;
		}

		private static bool CheckVersion<T>(T destination, PropertyInfo pi, ref object value)
		{
			if (NotAllowCheckVersion)
			{
				return true;
			}

			if (value == null)
			{
				return true;
			}

			if (pi.Name.ToLower() != "version")
			{
				return true;
			}

			object value2 = pi.GetValue(destination, null);
			if (value2 == null)
			{
				return true;
			}

			if (pi.PropertyType == typeof(byte[]))
			{
				byte[] version = (byte[])value;
				byte[] version2 = (byte[])value2;
				if (!version.SequenceEqual(version2))
				{
					return false;
				}
			}
			else if (pi.PropertyType == typeof(DateTime))
			{
				DateTime version = (DateTime)value;
				DateTime version2 = (DateTime)value2;
				if (version > DateTimeUtil.DefaultMinDate &&
					version2 > DateTimeUtil.DefaultMinDate &&
					version != version2)
				{
					return false;
				}
				value = DateTime.Now;
			}

			return true;
		}

		/*****************************************************/

		public static void Map<T1, T2>(T1 source, T2 dest)
		{
			Type type1 = typeof(T1);
			if (source == null)
			{
				throw new Exception(string.Format("ObjectMapper {0}：source不能为空", type1.FullName));
			}

			Type type2 = typeof(T2);
			if (dest == null)
			{
				throw new Exception(string.Format("ObjectMapper {0}：destination不能为空", type2.FullName));
			}

			List<PropertyInfo> list1 = null;
			if (!_Dict.ContainsKey(type1))
			{
				list1 = ParseProperty(type1);
				_Dict.Add(type1, list1);
			}
			else
			{
				list1 = _Dict[type1];
			}

			List<PropertyInfo> list2 = null;
			if (!_Dict.ContainsKey(type2))
			{
				list2 = ParseProperty(type2);
				_Dict.Add(type2, list2);
			}
			else
			{
				list2 = _Dict[type2];
			}

			foreach (PropertyInfo pi2 in list2)
			{
				PropertyInfo pi1 = list1.FirstOrDefault(a => a.Name == pi2.Name);
				if (pi1 == null || pi1.PropertyType != pi2.PropertyType)
				{
					continue;
				}

				object value = pi1.GetValue(source, null);
				pi2.SetValue(dest, value, null);
			}
		}

		/*****************************************************/

		public static void Compare<T>(IEnumerable<T> voList, IEnumerable<T> poList, Expression<Func<T, object>> keySelector,
			out IList<T> addedList, out IList<T> deletedList, out IList<T> modifiedList) where T : class, new()
		{
			IList<T> unchangedList;
			Compare<T>(voList, poList, keySelector, out addedList, out deletedList, out modifiedList, out unchangedList);
		}

		public static void Compare<T>(IEnumerable<T> voList, IEnumerable<T> poList, Expression<Func<T, object>> keySelector,
			out IList<T> addedList, out IList<T> deletedList, out IList<T> modifiedList, out IList<T> unchangedList) where T : class, new()
		{
			addedList = new List<T>();
			deletedList = new List<T>();
			modifiedList = new List<T>();
			unchangedList = new List<T>();

			if (voList == null || poList == null)
			{
				throw new Exception("比较的列表不能为空");
			}

			PropertyInfo keyPI = ReflectHelper.GetProperty<T>(keySelector);

			foreach (T vo in voList)
			{
				if (vo == null)
				{
					throw new Exception("VO不能为空");
				}

				object idValue = keyPI.GetValue(vo, null);

				T po = ObjectMapper.Find<T>(poList, keyPI.Name, idValue);
				if (po != null) //如果数据库中已存在，那么更新（用VO覆盖PO）；
				{
					DataRowState state = Compare<T>(po, vo);
					if (state == DataRowState.Modified)
					{
						modifiedList.Add(vo);
					}
					else
					{
						unchangedList.Add(vo);
					}
				}
				else //否则就是新增。
				{
					addedList.Add(vo);
				}
			}

			foreach (T po in poList.ToList())
			{
				object idValue = keyPI.GetValue(po, null);
				T vo = ObjectMapper.Find<T>(voList, keyPI.Name, idValue);
				if (vo == null) //如果数据库中存在，但VO中不存在，说明此item需要删除。
				{
					deletedList.Add(po);
				}
			}
		}

		public static DataRowState Compare<T>(T src, T dest)
		{
			if (src == null && dest == null)
			{
				return DataRowState.Detached; //表示两个对象都为空。
			}

			if (src == null && dest != null)
			{
				return DataRowState.Added;
			}

			if (src != null && dest == null)
			{
				return DataRowState.Deleted;
			}

			Type type = typeof(T);
			if (!_Dict.ContainsKey(type))
			{
				List<PropertyInfo> list = ParseProperty(type);
				_Dict.Add(type, list);
			}

			foreach (PropertyInfo pi in _Dict[type])
			{
				object srcValue = pi.GetValue(src, null);
				object destValue = pi.GetValue(dest, null);
				if (srcValue == null && destValue == null)
				{
					continue;
				}

				if (!object.Equals(srcValue, destValue)) //只要有一个属性的值不同，就是Modified。
				{
					return DataRowState.Modified;
				}
			}

			return DataRowState.Unchanged;
		}

		public static T Find<T>(IEnumerable<T> list, string key, object value)
		{
			Type type = typeof(T);
			foreach (T item in list)
			{
				PropertyInfo pi = ReflectHelper.GetProperty(type, key);
				object keyValue = pi.GetValue(item, null);

				if (keyValue.Equals(value)) //必须比较值，否则两个GUID对象的值相同，用=无法判断正确。
				{
					return item;
				}
			}

			return default(T);
		}
	}

	class Test
	{
		public Guid Id
		{
			get;
			set;
		}

		public int No
		{
			get;
			set;
		}

		public string Name
		{
			get;
			set;
		}

		public DateTime Date
		{
			get;
			set;
		}

		public decimal Price
		{
			get;
			set;
		}

		void aa()
		{
			Test a = new Test()
			{
				Id = Guid.NewGuid(),
				No = 1,
				Name = "A",
				Date = DateTime.Today,
				Price = 0.1m
			};
			List<Test> list1 = new List<Test>();
			list1.Add(a);

			Test b = new Test()
			{
				Id = a.Id,
				No = 1,
				Name = "A",
				Date = DateTime.Today,
				Price = 0.1m
			};
			List<Test> list2 = new List<Test>();
			list1.Add(b);

			IList<Test> addedList = new List<Test>();
			IList<Test> deletedList = new List<Test>();
			IList<Test> modifiedList = new List<Test>();
			IList<Test> unchangedList = new List<Test>();
			ObjectMapper.Compare<Test>(list1, list2, item => item.Id, out addedList, out deletedList, out modifiedList, out unchangedList);
		}

	}

}
