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

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Reflection;
using Infrastructure.Utilities;

namespace Infrastructure.Utilities
{
	public static class DbUtil
	{
		public static string GetTableName(Type type)
		{
			FieldInfo fi = type.GetField("TableName");
			if (fi != null)
			{
				return fi.GetValue(null).ToString();
			}

			string tableName = type.Name;
			return tableName;
		}

		public static string GetTableName<T>() where T : new()
		{
			return GetTableName(typeof(T));
		}

		public static string GetTimestampFieldName(Type type)
		{
			List<PropertyInfo> list = ReflectHelper.GetPropertyList(type, typeof(TimestampAttribute));
			PropertyInfo pi = list.FirstOrDefault();
			if (pi == null)
			{
				return "";
			}

			return pi.Name;
		}

		private static string GenerateFieldNames(Type type)
		{
			string fieldNames = "";
			List<PropertyInfo> piList = ReflectHelper.GetPropertyList(type);
			foreach (PropertyInfo pi in piList)
			{
				fieldNames += pi.Name + ",";
			}
			return fieldNames.TrimEnd(',');
		}

		public static string GenerateInsertSql(DataSourceSetting.DbTypes dbType, Type entityType, object entity)
		{
			return GenerateInsertSql(dbType, entityType, entity, null);
		}

		public static string GenerateInsertSql(DataSourceSetting.DbTypes dbType, Type entityType, object entity, IEnumerable<string> excludeFieldNames)
		{
			string sql = "insert into {0}({1}) values({2})";
			string tableName = GetTableName(entityType);
			string fieldNames = "";
			string fieldValues = "";

			List<string> readonlyFieldNames = GetReadOnlyFieldNames(entityType);
			if (excludeFieldNames != null)
			{
				readonlyFieldNames.AddRange(excludeFieldNames);
			}

			Dictionary<string, string> dict = GetFieldNamesAndValues(dbType, entityType, entity, true);
			foreach (KeyValuePair<string, string> kvp in dict)
			{
				if (readonlyFieldNames.Contains(kvp.Key))
				{
					continue;
				}

				fieldNames += kvp.Key + ",";
				fieldValues += kvp.Value + ",";
			}

			fieldNames = fieldNames.TrimEnd(',');
			fieldValues = fieldValues.TrimEnd(',');

			sql = sql.Fmt(tableName, fieldNames, fieldValues);
			return sql;
		}

		public static string GenerateUpdateSql(DataSourceSetting.DbTypes dbType, Type entityType, object entity)
		{
			string sql = "update {0} set {1} where {2}";
			string tableName = GetTableName(entityType);
			string fieldNameAndValues = "";
			string where = "{0} = {1}";

			List<string> readonlyFieldNames = GetReadOnlyFieldNames(entityType);

			Dictionary<string, string> dict = GetFieldNamesAndValues(dbType, entityType, entity, false);
			KeyValuePair<string, string> keyField = dict.First(); //默认第一个属性是主键字段。
			where = where.Fmt(keyField.Key, keyField.Value);

			foreach (KeyValuePair<string, string> kvp in dict)
			{
				if (readonlyFieldNames.Contains(kvp.Key))
				{
					continue;
				}

				if (kvp.Key == keyField.Key)
				{
					continue;
				}
				fieldNameAndValues += "{0} = {1},".Fmt(kvp.Key, kvp.Value);
			}

			fieldNameAndValues = fieldNameAndValues.TrimEnd(',');

			sql = sql.Fmt(tableName, fieldNameAndValues, where);
			return sql;
		}

		public static string GenerateDeleteSql(DataSourceSetting.DbTypes dbType, Type entityType, object id)
		{
			string sql = "delete from {0} where {1}";
			string tableName = GetTableName(entityType);

			List<PropertyInfo> piList = ReflectHelper.GetPropertyList(entityType);
			PropertyInfo keyProperty = piList.First(); //默认第一个属性是主键字段。
			string sValue = ConvertFieldValue(dbType, id, keyProperty);
			string where = "{0} = {1}".Fmt(keyProperty.Name, sValue);

			sql = sql.Fmt(tableName, where);
			return sql;
		}

		private static Dictionary<string, string> GetFieldNamesAndValues(DataSourceSetting.DbTypes dbType, Type entityType, object entity, bool ignoreIncKey)
		{
			Dictionary<string, string> dict = new Dictionary<string, string>();

			List<PropertyInfo> piList = ReflectHelper.GetPropertyList(entityType);
			foreach (PropertyInfo pi in piList)
			{
				if (!pi.PropertyType.IsValueType && pi.PropertyType != typeof(string))
				{
					continue;
				}

				bool notMapped = false;
				object[] attributes = pi.GetCustomAttributes(false);
				foreach (object attr in attributes)
				{
					if (attr.ToString().Contains("NotMappedAttribute") || attr.ToString().Contains("NotWritedAttribute"))
					{
						notMapped = true;
						continue;
					}
				}

				if (notMapped)
				{
					continue;
				}

				object fieldValue = pi.GetValue(entity, null);

				if (ignoreIncKey) //如果忽略自增主键；
				{
					if (pi.Name == piList[0].Name && pi.PropertyType == typeof(int)) //第一个Property默认为主键；
					{
						int keyValue = (int)fieldValue;
						if (keyValue <= 0) //如果主键是int类型，且值小于等于0，那么默认为是自增主键。
						{
							continue;
						}
					}
				}

				string value = ConvertFieldValue(dbType, fieldValue, pi);
				dict.Add(pi.Name, value);
			}

			return dict;
		}

		//fieldValue：字段值；pi：Entity的属性信息。
		private static string ConvertFieldValue(DataSourceSetting.DbTypes dbType, object fieldValue, PropertyInfo pi)
		{
			string sValue = "null";
			if (fieldValue == null)
			{
				return sValue;
			}

			Type fieldValueType = fieldValue.GetType();

			if (fieldValueType == typeof(String))
			{
				sValue = fieldValue.ToString().Replace("'", "''"); //把单引号改为双引号。
				sValue = "'{0}'".Fmt(sValue);
			}
			else if (fieldValueType == typeof(DateTime))
			{
				if (dbType == DataSourceSetting.DbTypes.Oracle)
				{
					sValue = "to_date('{0}', 'yyyy-mm-dd hh24:mi:ss')".Fmt(((DateTime)fieldValue).ToString("yyyy-MM-dd HH:mm:ss"));
				}
				else
				{
					DateTime d = (DateTime)fieldValue;
					if (d < DateTimeUtil.DefaultMinDate)
					{
						d = DateTimeUtil.DefaultMinDate;
					}
					sValue = "'{0}'".Fmt(d.ToString("yyyy-MM-dd HH:mm:ss"));
				}
			}
			else if (fieldValueType == typeof(Guid))
			{
				sValue = "'{0}'".Fmt(fieldValue = fieldValue.ToString());
			}
			else if (fieldValueType == typeof(byte))
			{
				if (pi.PropertyType == typeof(short))
				{
					sValue = (short)(byte)fieldValue == 1 ? "1" : "0";
				}
				else if (pi.PropertyType == typeof(bool) || pi.PropertyType == typeof(Nullable<bool>))
				{
					sValue = (byte)fieldValue == 1 ? "1" : "0";
				}
			}
			else if (fieldValueType == typeof(sbyte))
			{
				if (pi.PropertyType == typeof(bool) || pi.PropertyType == typeof(Nullable<bool>))
				{
					sValue = (byte)fieldValue == 1 ? "1" : "0";
				}
			}
			else if (fieldValueType == typeof(Boolean))
			{
				sValue = ((bool)fieldValue ? 1 : 0).ToString();
			}
			else
			{
				sValue = fieldValue.ToString();
			}
			return sValue;
		}

		//只读（含NotWritedAttribute）的属性不需要插入和更新。
		private static List<string> GetReadOnlyFieldNames(Type type)
		{
			List<string> excludeFieldNames = new List<string>();
			foreach (PropertyInfo pi in ReflectHelper.GetPropertyList(type))
			{
				if (!pi.CanWrite)
				{
					excludeFieldNames.Add(pi.Name);
				}
				else
				{
					object[] attributes = pi.GetCustomAttributes(true);
					if (attributes != null)
					{
						foreach (object attr in attributes)
						{
							if (attr is NotWritedAttribute)
							{
								excludeFieldNames.Add(pi.Name);
							}
						}
					}
				}
			}
			return excludeFieldNames;
		}

		#region - Other Helper -

		public static void Log(string sql)
		{
			Console.WriteLine("[{0}] {1}", DateTime.Now.ToString("HH:mm:ss.fff"), sql);
		}

		#endregion
	}
}
