/*
 * Copyright (c) 京北方信息技术股份有限公司 Corporation 2021 . All rights reserved.
 *
 */
package net.northking.cctp.common.util;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

/**
 * 简单实体类的工具类
 *
 * @author : xiusong.xie
 * @version : 1.0
 */
public class EntityUtils
{
  private EntityUtils()
  {
  }

  /**
   * 判断实体对象是否为空值，或者所有的属性值是否均为空值
   *
   * @param obj 实体对象
   * @return 是否为空值
   * @throws IllegalAccessException
   */
  public static boolean isEmpty(Object obj) throws IllegalAccessException
  {
    if (obj == null)
    {
      return true;
    }

    Field[] fields = obj.getClass().getDeclaredFields();
    for (Field field : fields)
    {
      field.setAccessible(true);
      Object value = field.get(obj);
      if (value != null)
      {
        if (value instanceof CharSequence)
        {
          if (!"".equals(value))
          {
            return false;
          }
        } else if (obj instanceof Optional)
        {
          if (((Optional) obj).isPresent())
          {
            return false;
          }
        } else if (obj.getClass().isArray())
        {
          if (Array.getLength(obj) > 0)
          {
            return false;
          }
        } else if (obj instanceof Collection)
        {
          if (!((Collection) obj).isEmpty())
          {
            return false;
          }
        } else if (obj instanceof Map)
        {
          if (!((Map) obj).isEmpty())
          {
            return false;
          }
        } else
        {
          return false;
        }
      }
    }

    return true;
  }

  public static Object getFieldValue(Object entity, String fieldName)
  {
    PropertyDescriptor pd = getPropertyDescriptor(entity, fieldName);
    Method readMethod = pd.getReadMethod();
    if (readMethod == null)
    {
      throw new FatalBeanException("Could not get property '" + fieldName + "''s readMethod ");
    }
    try
    {
      readMethod.setAccessible(true);
      return readMethod.invoke(entity);
    } catch (Throwable ex)
    {
      throw new FatalBeanException("Could not get property '" + fieldName + "' ", ex);
    }
  }

  public static void setFieldValue(Object entity, String fieldName, Object value) throws ParseException
  {
    PropertyDescriptor pd = getPropertyDescriptor(entity, fieldName);
    Method writeMethod = pd.getWriteMethod();
    if (writeMethod == null)
    {
      throw new FatalBeanException("Could not get property '" + fieldName + "''s writeMethod ");
    }
    if (writeMethod.getParameterCount() != 1)
    {
      throw new FatalBeanException("Could not get property '" + fieldName + "''s writeMethod ");
    }
    Class<?> parameterType = writeMethod.getParameterTypes()[0];
    if (parameterType.isInstance(value))
    {
      setFieldValue(writeMethod, entity, fieldName, value);
    } else
    {
      if (parameterType.isAssignableFrom(CharSequence.class))
      {
        setStringField(writeMethod, entity, fieldName, value);
      } else if (parameterType.isAssignableFrom(Date.class))
      {
        setDateField(writeMethod, entity, fieldName, value);
      } else if (parameterType.isAssignableFrom(Integer.class))
      {
        setIntegerField(writeMethod, entity, fieldName, value);
      } else if (parameterType.isAssignableFrom(Long.class))
      {
        setLongField(writeMethod, entity, fieldName, value);
      } else if (parameterType.isAssignableFrom(BigDecimal.class))
      {
        setBigDecimalField(writeMethod, entity, fieldName, value);
      } else
      {
        throw new ClassCastException("Could not set property '" + fieldName + "', value type is " + value.getClass() + " can not cast to " + parameterType);
      }
    }
  }

  private static void setBigDecimalField(Method writeMethod, Object entity, String fieldName, Object value)
  {
    BigDecimal fieldValue;
    if (value instanceof CharSequence || value instanceof Number)
    {
      fieldValue = new BigDecimal(value.toString());
    } else
    {
      throw new ClassCastException("Could not set property '" + fieldName + "', value type is " + value.getClass() + " can not cast to BigDecimal");
    }
    setFieldValue(writeMethod, entity, fieldName, fieldValue);
  }

  private static void setLongField(Method writeMethod, Object entity, String fieldName, Object value)
  {
    Long fieldValue;
    if (value instanceof String)
    {
      fieldValue = Long.parseLong((String) value);
    } else if (value instanceof Number)
    {
      fieldValue = ((Number) value).longValue();
    } else
    {
      throw new ClassCastException("Could not set property '" + fieldName + "', value type is " + value.getClass() + " can not cast to Long");
    }
    setFieldValue(writeMethod, entity, fieldName, fieldValue);
  }

  private static void setIntegerField(Method writeMethod, Object entity, String fieldName, Object value)
  {
    Integer fieldValue;
    if (value instanceof String)
    {
      fieldValue = Integer.parseInt((String) value);
    } else if (value instanceof Number)
    {
      fieldValue = ((Number) value).intValue();
    } else
    {
      throw new ClassCastException("Could not set property '" + fieldName + "', value type is " + value.getClass() + " can not cast to Integer");
    }
    setFieldValue(writeMethod, entity, fieldName, fieldValue);
  }

  private static void setDateField(Method writeMethod, Object entity, String fieldName, Object value) throws ParseException
  {
    Date fieldValue;
    if (value instanceof String)
    {
      fieldValue = DateUtil.parse((String) value);
    } else if (value instanceof Long)
    {
      fieldValue = new Date((Long) value);
    } else if (value instanceof Double)
    {
      fieldValue = new Date((Long) value);
    } else
    {
      throw new ClassCastException("Could not set property '" + fieldName + "', value type is " + value.getClass() + "[" + value.toString() + "] can not cast to java.util.Date");
    }
    setFieldValue(writeMethod, entity, fieldName, fieldValue);
  }

  private static void setStringField(Method writeMethod, Object entity, String fieldName, Object value)
  {
    String fieldValue = value.toString();
    setFieldValue(writeMethod, entity, fieldName, fieldValue);
  }


  /**
   * 赋值
   *
   * @param writeMethod 写函数
   * @param entity      实体对象
   * @param fieldName   字段名称
   * @param value       值
   */
  private static void setFieldValue(Method writeMethod, Object entity, String fieldName, Object value)
  {
    try
    {
      writeMethod.setAccessible(true);
      writeMethod.invoke(entity, value);
    } catch (Throwable ex)
    {
      throw new FatalBeanException("Could not set property '" + fieldName + "' ", ex);
    }
  }

  private static PropertyDescriptor getPropertyDescriptor(Object entity, String fieldName)
  {
    if (entity == null)
    {
      throw new IllegalArgumentException("Entity must not be null");
    }
    if (StringUtils.isEmpty(fieldName))
    {
      throw new IllegalArgumentException("FieldName must not be null");
    }

    PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(entity.getClass(), fieldName);
    if (pd == null)
    {
      throw new FatalBeanException("Could not get property '" + fieldName + "' ");
    }
    return pd;
  }
}
