/*
 * $Revision: 1.14 $ $Date: 2006/01/15 12:52:28 $
 * (C) 2004 SUGIMOTO Ken-ichi
 */

package feat.v1;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Iterator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import feat.v1.config.CommandDeclaration;
import feat.v1.config.FeatConfig;
import feat.v1.config.FeatureConfig;
import feat.v1.config.FeatureDeclaration;
import feat.v1.config.FileLocator;
import feat.v1.config.ServletFileLocator;

public class ActionServlet extends HttpServlet {

    protected static Log log;
    private static ResourceManager rm;

    static {
        log = LogFactory.getLog(ActionServlet.class);
        rm = new ResourceManager();
        ClassLoader loader = ActionServlet.class.getClassLoader();
        rm.addResourceFile("resources.xml", loader);
    }

    private long checktime;
    private FileLocator configLocation;
    private Processor processor;


    public ActionServlet() {
        checktime = 0L;
    }

    public void init() throws ServletException {
        ContextAccessor.setFeatConfig(getServletContext(), readConfig());
        processor = new Processor();
    }

    private FeatConfig readConfig() throws ServletException {
        // コンフィグファイルのロード
        FeatConfig ret = null;
        try {
            FileLocator locator = getConfigFileLocation();
            FileLocator appRoot = new ServletFileLocator("/", getServletContext());
            ret = ConfigReader.parseFeatConfig(appRoot, locator);
            ret.validate();
            log.info(rm.getString("config yomikomi kanryou", locator.toString(), null, null));
        }
        catch (FeatException ex) {
            log.fatal("ActionServletの初期化中にエラー発生", ex);
            throw new ServletException(ex);
        }
        catch (URISyntaxException ex) {
            throw new ServletException(ex);
        }
        return ret;
    }

    private FileLocator getConfigFileLocation() throws ServletException {
        if ( configLocation == null ) {
            // サーブレットのパラメータからコンフィグファイルの場所を特定する
            String configFilePath = getServletConfig().getInitParameter("config");
            if (configFilePath == null) {
                configFilePath = "/WEB-INF/feat-config.xml";
            }
            /*String dir = StringUtils.substringBeforeLast(configFilePath, "/");
            String file = StringUtils.substringAfterLast(configFilePath, "/");*/
            try {
                configLocation = new ServletFileLocator(configFilePath, getServletContext());
            }
            catch (URISyntaxException ex) {
                throw new ServletException(ex);
            }
        }
        return configLocation;
    }

    /**
     * 設定ファイルが最後にロードした時刻より新しくなっていたらtrueを返す。
     * @return boolean
     */
    private boolean checkConfig() throws FeatException, ServletException {
        if ( System.currentTimeMillis() - checktime > 10000L ) {
            try {
                // feat-config.xmlが更新されたか
                FileLocator locator = getConfigFileLocation();
                long lastUpdate = ContextAccessor.getConfigLastUpdateTime(getServletContext());
                if ( lastUpdate < locator.getLastModified() )
                    return true;

                // 各Featureファイルが更新されたか
                FeatConfig conf = ContextAccessor.getFeatConfig(getServletContext());
                Iterator it = conf.getFeatureConfigs();
                while( it.hasNext() ) {
                    FeatureConfig fc = (FeatureConfig)it.next();
                    if ( lastUpdate < fc.getConfigFileLocation().getLastModified() )
                        return true;
                }
            }
            catch(IOException ex) {}
            finally {
                checktime = System.currentTimeMillis();
            }
        }
        return false;
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
            ServletException, IOException {
        process(req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
            ServletException, IOException {
        process(req, resp);
    }

    /**
     * GET, POSTメソッドの処理
     */
    private void process(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        try {
            // リクエストURIからFeature、コマンド名を取り出す
            String servletPath = req.getServletPath();
            String featureName = null;
            String commandName = null;
            if ( servletPath != null ) {
                String[] pathInfo = StringUtil.split(servletPath, "/");
                if ( pathInfo.length > 0 )
                    featureName = pathInfo[0];
                if ( pathInfo.length > 1 )
                    commandName = StringUtil.substringBefore(pathInfo[1], ".");
            }
            if ( featureName == null ) {
                log.warn("Feature名が指定されていない");
                resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }

            // ContextAccessorを作る
            ContextAccessor acc = null;
            try {
                acc = new ContextAccessor(
                        getServletContext(),
                        req, resp,
                        featureName, commandName);
            }
            catch (FeatException ex) {
                throw new HttpNotFoundException(ex.getMessage());
            }

            // 設定ファイルが新しくなっていたらリロード
            FeatConfig config = null;
            synchronized(ActionServlet.class) {
                if ( checkConfig() )
                    acc.setFeatConfig(readConfig());
                config = acc.getFeatConfig();
            }

            // Feature、コマンドの宣言を取り出す
            FeatureDeclaration featureDec = acc.getCurrentFeatureDeclaration();
            CommandDeclaration commandDec = acc.getCurrentCommandDeclaration();

            Response r = processor.process(acc);
            if ( r != null ) {
                long start = System.currentTimeMillis();
                r.output(acc);
                long elapseTime = System.currentTimeMillis() - start;
                log.info(acc.getCurrentFeatureDeclaration().getName()+"/"+r.getClass().getName()+"(response) "+elapseTime+"[ms]");
            }
            else
                throw new ResponseException("レスポンスがない");

        }
        catch (FileNotFoundException ex) {
            log.warn(ex.getMessage(), ex);
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        catch (HttpNotFoundException ex) {
            log.warn(ex.getMessage(), ex);
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        catch (NotHandledException ex) {
            Throwable cause = ex.getCause();
            log.error("アプリケーションで例外が発生", cause);
            throw new ServletException(ex.getCause());
        }
        catch (FeatException ex) {
            switch(ex.getLevel()) {
                case FeatException.LEVEL_WARN:
                    log.warn(ex.getMessage(), ex);
                	break;
            	case FeatException.LEVEL_ERROR:
                    log.error(ex.getMessage(), ex);
            		break;
            	case FeatException.LEVEL_FATAL:
                    log.fatal(ex.getMessage(), ex);
            		break;
            	default:
            	    log.info(ex.getMessage(), ex);
            }
            throw new ServletException(ex);
        }
    }

}
