package org.seasar.struts.plugin;

import java.io.File;
import java.lang.reflect.Field;
import java.util.Iterator;

import javax.servlet.ServletException;

import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.FormBeanConfig;
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.util.FieldUtil;
import org.seasar.struts.action.StrutsAction;
import org.seasar.struts.action.StrutsActionForward;
import org.seasar.struts.form.StrutsActionForm;
import org.seasar.struts.util.ClassFinder;
import org.seasar.struts.util.ClassFinderImpl;

/**
 * <set-property property="enableJar" value="false"/>
 * @author Satoshi Kimura
 */
public class AutoStrutsConfigRegisterPlugIn implements PlugIn {
    
    private static final String WEB_CLASSES_DIR = "/WEB-INF/classes";

    private static final String WEB_LIB_DIR = "/WEB-INF/lib";

    private ClassFinder classFinder = new ClassFinderImpl();
    
    private boolean enableJar;
    
    public AutoStrutsConfigRegisterPlugIn() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.struts.action.PlugIn#destroy()
     */
    public void destroy() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.struts.action.PlugIn#init(org.apache.struts.action.ActionServlet,
     *      org.apache.struts.config.ModuleConfig)
     */
    public void init(ActionServlet actionServlet, ModuleConfig config) throws ServletException {
        try {
            classFinder.find(isEnableJar());
            
            if (actionServlet != null) {
                String classesDirPath = actionServlet.getServletContext().getRealPath(WEB_CLASSES_DIR);
                classFinder.find(classesDirPath, isEnableJar());
                
                String libDirPath = actionServlet.getServletContext().getRealPath(WEB_LIB_DIR);
                File[] files = new File(libDirPath).listFiles();
                for (int i = 0; i < files.length; i++) {
                    classFinder.find(files[i], isEnableJar());
                }
            }

            regist(config);
        } finally {
            classFinder.destroy();
        }
    }

    private void regist(ModuleConfig config) {
        for (Iterator iterator = classFinder.getClassCollection().iterator(); iterator.hasNext();) {
            Class clazz = (Class) iterator.next();
            Annotation annotation = Annotations.getAnnotation(StrutsActionForm.class, clazz);
            if (annotation != null) {
                registActionForm((StrutsActionForm) annotation, clazz, config);
            }
            annotation = Annotations.getAnnotation(StrutsAction.class, clazz);
            if (annotation != null) {
                registAction((StrutsAction) annotation, clazz, config);
            }
        }
    }

    private void registActionForm(StrutsActionForm form, Class formClass, ModuleConfig config) {
        String name = form.name();
        if (config.findFormBeanConfig(name) == null) {
            regist(config, form, formClass);
        }
    }

    private void registAction(StrutsAction action, Class actionClass, ModuleConfig config) {
        if (!hasActionConfig(config, action)) {
            addActionConfig(config, action, actionClass);
        }
    }
    
    private boolean hasActionConfig(ModuleConfig config, StrutsAction action) {
        String path = action.path();
        ActionConfig[] actionConfigs = config.findActionConfigs();
        for (int i = 0; i < actionConfigs.length; ++i) {
            if (path.equals(actionConfigs[i].getPath())) {
                return true;
            }
        }
        return false;
    }
    
    private void regist(ModuleConfig config, StrutsActionForm form, Class formClass) {
        FormBeanConfig formBeanConfig = new FormBeanConfig();
        formBeanConfig.setName(form.name());
        formBeanConfig.setType(formClass.getName());
        formBeanConfig.setRestricted(form.restricted());
        config.addFormBeanConfig(formBeanConfig);
    }

    private void addActionConfig(ModuleConfig config, StrutsAction action, Class actionClass) {
        ActionConfig actionConfig = new ActionMapping();
        actionConfig.setAttribute(action.attribute());
        actionConfig.setForward(action.forward());
        actionConfig.setInclude(action.include());
        actionConfig.setInput(action.input());
        actionConfig.setModuleConfig(config);
        actionConfig.setName(action.name());
        actionConfig.setParameter(action.parameter());
        actionConfig.setPath(action.path());
        actionConfig.setPrefix(action.prefix());
        actionConfig.setRoles(action.roles());
        actionConfig.setScope(action.scope());
        actionConfig.setSuffix(action.suffix());
        actionConfig.setType(actionClass.getName());
        actionConfig.setUnknown(action.unknown());
        actionConfig.setValidate(action.validate());

        addFowardConfig(actionConfig, actionClass);

        config.addActionConfig(actionConfig);
    }

    private void addFowardConfig(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;
                ForwardConfig forwardConfig = new ActionForward();
                forwardConfig.setName(FieldUtil.get(fields[i], actionClass).toString());
                forwardConfig.setPath(actionForward.path());
                forwardConfig.setRedirect(actionForward.redirect());
                actionConfig.addForwardConfig(forwardConfig);
            }
        }
    }

    public boolean isEnableJar() {
        return enableJar;
    }

    public void setEnableJar(boolean enableJar) {
        this.enableJar = enableJar;
    }
}
