package org.seasar.struts.config;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Iterator;

import javax.servlet.ServletContext;

import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.ForwardConfig;
import org.apache.struts.config.ModuleConfig;
import org.codehaus.backport175.reader.Annotation;
import org.codehaus.backport175.reader.Annotations;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;
import org.seasar.framework.util.FieldUtil;
import org.seasar.struts.action.NullStrutsAction;
import org.seasar.struts.action.StrutsAction;
import org.seasar.struts.action.StrutsActionForward;
import org.seasar.struts.config.rule.ZeroConfigActionRule;

/**
 * @author Katsuhiko Nagashima
 */
public class AutoActionRegister {

    private AutoActionRegister() {
    }

    public static void regist(ServletContext servletContext, ModuleConfig config, Collection classes) {
        classes = ClassComparator.sort(classes);
        
        for (Iterator iterator = classes.iterator(); iterator.hasNext();) {
            Class clazz = (Class) iterator.next();
            Annotation annotation = Annotations.getAnnotation(StrutsAction.class, clazz);
            StrutsAction strutsAction = null;
            if (annotation != null) {
                strutsAction = (StrutsAction) annotation;
            } else if (clazz.getName().matches(configRule().getActionClassPattern())) {
                strutsAction = new NullStrutsAction();
            }
            if (strutsAction != null) {
                registAction(strutsAction, clazz, config, servletContext);
            }
        }
    }
    

    private static void registAction(StrutsAction action, Class actionClass, ModuleConfig config,
            ServletContext servletContext) {
        String path = getPath(action, actionClass, config);
        if (!hasActionConfig(config, path)) {
            addActionConfig(servletContext, config, action, actionClass);
        }
    }

    public static boolean hasActionConfig(ModuleConfig config, String path) {
        ActionConfig[] actionConfigs = config.findActionConfigs();
        for (int i = 0; i < actionConfigs.length; ++i) {
            if (path.equals(actionConfigs[i].getPath())) {
                return true;
            }
        }
        return false;
    }

    private static void addActionConfig(ServletContext servletContext, ModuleConfig config, StrutsAction action,
            Class actionClass) {
        ActionConfig actionConfig = new ActionMapping();
        actionConfig.setAttribute(getAttribute(action, actionClass, config));
        actionConfig.setForward(getForward(action, actionClass, config));
        actionConfig.setInclude(getInclude(action, actionClass, config));
        actionConfig.setInput(getInput(action, actionClass, config));
        actionConfig.setModuleConfig(config);
        actionConfig.setName(getName(action, actionClass, config));
        actionConfig.setParameter(getParameter(action, actionClass, config));
        actionConfig.setPath(getPath(action, actionClass, config));
        actionConfig.setPrefix(getPrefix(action, actionClass, config));
        actionConfig.setRoles(getRoles(action, actionClass, config));
        actionConfig.setScope(getScope(action, actionClass, config));
        actionConfig.setSuffix(getSuffix(action, actionClass, config));
        actionConfig.setType(actionClass.getName());
        actionConfig.setUnknown(getUnknown(action, actionClass, config));
        actionConfig.setValidate(getValidate(action, actionClass, config));

        addFowardConfig(servletContext, actionConfig, actionClass);

        config.addActionConfig(actionConfig);
    }

    private static void addFowardConfig(ServletContext servletContext, ActionConfig actionConfig, Class actionClass) {
        Field[] fields = actionClass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Annotation annotation = Annotations.getAnnotation(StrutsActionForward.class, fields[i]);
            if (annotation != null) {
                fields[i].setAccessible(true);
                StrutsActionForward actionForward = (StrutsActionForward) annotation;
                String name = FieldUtil.get(fields[i], actionClass).toString();
                ForwardConfig forwardConfig = new ActionForward();
                forwardConfig.setName(name);
                forwardConfig.setPath(actionForward.path());
                forwardConfig.setRedirect(actionForward.redirect());
                actionConfig.addForwardConfig(forwardConfig);
            }
        }
        if (actionClass.getName().matches(configRule().getActionClassPattern())) {
            rule().addFowardConfig(actionClass, actionConfig, servletContext);
        }
    }

    private static AutoStrutsConfigRule configRule() {
        S2Container container = SingletonS2ContainerFactory.getContainer();
        return (AutoStrutsConfigRule) container.getComponent(AutoStrutsConfigRule.class);
    }

    private static ZeroConfigActionRule rule() {
        S2Container container = SingletonS2ContainerFactory.getContainer();
        return (ZeroConfigActionRule) container.getComponent(ZeroConfigActionRule.class);
    }

    private static String getPath(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.path() == StrutsAction.DEFAULT_PATH ? rule().getPath(actionClass, config) : action.path();
    }

    private static String getAttribute(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.attribute() == StrutsAction.DEFAULT_ATTRIBUTE ? rule().getAttribute(actionClass, config) : action
                .attribute();
    }

    private static String getForward(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.forward() == StrutsAction.DEFAULT_FORWARD ? rule().getForward(actionClass, config) : action
                .forward();
    }

    private static String getInclude(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.include() == StrutsAction.DEFAULT_INCLUDE ? rule().getInclude(actionClass, config) : action
                .include();
    }

    private static String getInput(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.input() == StrutsAction.DEFAULT_INPUT ? rule().getInput(actionClass, config) : action.input();
    }

    private static String getName(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.name() == StrutsAction.DEFAULT_NAME ? rule().getName(actionClass, config) : action.name();
    }

    private static String getParameter(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.parameter() == StrutsAction.DEFAULT_PARAMETER ? rule().getParameter(actionClass, config) : action
                .parameter();
    }

    private static String getPrefix(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.prefix() == StrutsAction.DEFAULT_PREFIX ? rule().getPrefix(actionClass, config) : action.prefix();
    }

    private static String getRoles(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.roles() == StrutsAction.DEFAULT_ROLES ? rule().getRoles(actionClass, config) : action.roles();
    }

    private static String getScope(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.scope() == StrutsAction.DEFAULT_SCOPE ? rule().getScope(actionClass, config) : action.scope();
    }

    private static String getSuffix(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.suffix() == StrutsAction.DEFAULT_SUFFIX ? rule().getSuffix(actionClass, config) : action.suffix();
    }

    private static boolean getUnknown(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.unknown() == StrutsAction.DEFAULT_UNKNOWN ? rule().getUnknown(actionClass, config) : action
                .unknown();
    }

    private static boolean getValidate(StrutsAction action, Class actionClass, ModuleConfig config) {
        return action.validate() == StrutsAction.DEFAULT_VALIDATE ? rule().getValidate(actionClass, config) : action
                .validate();
    }

}
