package me.chyxion.tigon.model;

import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import me.chyxion.tigon.mybatis.Transient;
import me.chyxion.tigon.mybatis.NotUpdate;
import org.springframework.util.ReflectionUtils;
import me.chyxion.tigon.mybatis.NotUpdateWhenNull;
import org.springframework.util.ReflectionUtils.FieldFilter;
import org.springframework.util.ReflectionUtils.FieldCallback;

/**
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Feb 23, 2017 11:09:20
 */
public class MappableSupport implements Mappable {
    private static final long serialVersionUID = 1L;

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Object> toMap() {
        return toMap(false);
    }

    /**
     * @return toMap().toString()
     */
    @Override
    public String toString() {
        return toMap().toString();
    }

    // for override
    protected Map<String, Object> toMap(boolean forUpdate) {
        final Map<String, Object> mapRtn = new HashMap<String, Object>();
        ReflectionUtils.doWithFields(getClass(), new FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                field.setAccessible(true);
                mapRtn.put(field.getName(),
                    ReflectionUtils.getField(field, MappableSupport.this));
            }
        }, fieldFilter(forUpdate));
        return mapRtn;
    }

    protected FieldFilter fieldFilter(final boolean forUpdate) {
        return new ReflectionUtils.FieldFilter() {
            @Override
            public boolean matches(final Field field) {
                field.setAccessible(true);
                final int modifiers = field.getModifiers();
                final Object fieldValue =
                    ReflectionUtils.getField(field, MappableSupport.this);
                return !Modifier.isTransient(modifiers) &&
                    !field.isAnnotationPresent(Transient.class) &&
                    !Modifier.isFinal(modifiers) &&
                    !Modifier.isPublic(modifiers) &&
                    !Modifier.isStatic(modifiers) &&
                    (forUpdate ? !field.isAnnotationPresent(NotUpdate.class)
                        && (fieldValue != null
                            || !field.isAnnotationPresent(NotUpdateWhenNull.class)) : true);
            }
        };
    }
}
