package net.northking.cctp.element.core.support;

import net.northking.cctp.element.core.annotation.Autowired;
import net.northking.cctp.element.util.Assert;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * BeanPostProcessor
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class BeanPostProcessor {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(BeanPostProcessor.class);

    private BeanFactory beanFactory;

    public BeanPostProcessor(BeanFactory beanFactory) {
        Assert.notNull(beanFactory);
        this.beanFactory = beanFactory;
    }

    /**
     * Bean Autowired annotation field injection
     *
     * @throws Exception
     */
    public void process() throws Exception {
        for (Object bean : beanFactory.getAllBeans()) {
            Class<?> objectClass = bean.getClass();
            while (objectClass != null) {
                Field[] fields = objectClass.getDeclaredFields();
                for (Field field : fields) {
                    setFieldByType(bean, field);
                }
                objectClass = objectClass.getSuperclass();
            }
        }
    }

    private void setFieldByType(Object object, Field field) throws Exception {
        if (filter(field)) {
            return;
        }
        if ((
                    !Modifier.isPublic(field.getModifiers())
                    || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
            )
            && !field.isAccessible()) {
            field.setAccessible(true);
        }
        Object value = beanFactory.getBean(field.getType());
        field.set(object, value);
        logger.debug("Class [{}] object field [{}] set value type [{}]",
                object.getClass().getCanonicalName(), field.getName(), value.getClass());
    }

    private boolean filter(Field field) {
        return !field.isAnnotationPresent(Autowired.class);
    }

}
