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

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.northking.cctp.element.core.annotation.Library;
import net.northking.cctp.element.core.annotation.Bean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.northking.cctp.element.core.annotation.Keywords;

/**
 * ATP keyword library default class filter
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class DefaultClassFilter implements IClassFilter {

    private static Logger logger = LoggerFactory.getLogger(DefaultClassFilter.class);

    private List<Object> conditions = new ArrayList<Object>();

    public DefaultClassFilter() {
        conditions.add(new PublicClassCondition());
        conditions.add(new NotAbstractClassCondition());
        conditions.add(new HasDefaultConstructor());
        conditions.add(new HasKeywordAnnotationCondition());
    }

    public boolean accept(Class<?> clazz) {
        Iterator<?> iterator = conditions.iterator();
        while (iterator.hasNext()) {
            Condition condition = (Condition) iterator.next();
            if (!condition.check(clazz)) {
                return false;
            }
        }
        logger.debug("Filter accept {}", clazz);
        return true;
    }

    public void addCondition(Condition condition) {
        conditions.add(condition);
    }

    public interface Condition {
        public boolean check(Class<?> clazz);
    }

    private class PublicClassCondition implements Condition {
        public boolean check(Class<?> clazz) {
            return Modifier.isPublic(clazz.getModifiers());
        }
    }

    private class NotAbstractClassCondition implements Condition {
        public boolean check(Class<?> clazz) {
            return !Modifier.isAbstract(clazz.getModifiers());
        }
    }

    private class HasDefaultConstructor implements Condition {
        public boolean check(Class<?> clazz) {
            try {
                clazz.getConstructor(new Class[]{});
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    }

    private class HasKeywordAnnotationCondition implements Condition {
        public boolean check(Class<?> clazz) {
            return clazz.isAnnotationPresent(Keywords.class)
                   || clazz.isAnnotationPresent(Bean.class)
                   || clazz.isAnnotationPresent(Library.class);
        }
    }
}