/*
 * $Revision: 1.6 $ $Date: 2006/04/27 13:51:13 $
 * Copyright (C) 2004-2006 SUGIMOTO Ken-ichi
 */

package feat2;

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

import javax.servlet.ServletContext;
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 feat2.config.ConfigReader;
import feat2.config.ConfigurationException;
import feat2.config.FeatConfig;
import feat2.config.FeatureConfig;
import feat2.config.FileLocator;
import feat2.config.ServletFileLocator;

public class ActionServlet extends HttpServlet {

    private static final long serialVersionUID = "$Id: ActionServlet.java,v 1.6 2006/04/27 13:51:13 sugimotokenichi Exp $".hashCode();
    protected static Log log = LogFactory.getLog(ActionServlet.class);

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


    public ActionServlet() {
        checktime = 0L;
    }


    public void init() throws ServletException {

        Util.setFeatConfig(getServletContext(), readConfig());
        processor = new Processor();

    }


    private FeatConfig readConfig() throws ServletException {

        FeatConfig ret = null;
        try {

            FileResourceManager rm = FileResourceManager.getInstance();
            FileLocator locator = getConfigFileLocation();
            FileLocator appRoot = new ServletFileLocator("/", getServletContext());
            ret = ConfigReader.parseFeatConfig(appRoot, locator);

            FeatErrors errs = new FeatErrors(rm);

            boolean valid = ret.validate(errs);
            FeatError[] errors = errs.getErrors(FeatError.ERROR);

            if ( !valid || errors.length > 0 ) {

                log.fatal("設定ファイルの記述にエラーがあります");

                for (int i = 0; i < errors.length; i++) {
                    log.fatal(errors[i].getMessage());
                }

                throw new ServletException("設定ファイルの記述にエラーがあります");

            }

            log.info(rm.getString("config yomikomi kanryou", locator.toString(), null, null));

        }

        catch (FeatException ex) {
            log.fatal("ActionServletの初期化中にエラー発生", ex);
            throw new ServletException(ex);
        }

        catch (ConfigurationException ex) {
            log.fatal("設定ファイルの記述にエラーがあります", 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";
            }

            try {

                configLocation = new ServletFileLocator(configFilePath, getServletContext());

            }
            catch (URISyntaxException ex) {
                throw new ServletException(ex);
            }

        }

        return configLocation;

    }


    /**
     * 設定ファイルが最後にロードした時刻より新しくなっていたらtrueを返す。
     * @return boolean
     */
    private boolean checkConfig() throws ServletException {

        if ( System.currentTimeMillis() - checktime > 1000L ) {

            try {

                // feat-config.xmlが更新されたか

                FileLocator locator = getConfigFileLocation();

                long lastUpdate = Util.getConfigLastUpdateTime(getServletContext());
                if ( lastUpdate < locator.getLastModified() )
                    return true;

                // 各Featureファイルが更新されたか

                FeatConfig conf = Util.getFeatConfig(getServletContext());

                Iterator it = conf.getFeatures();
                while( it.hasNext() ) {

                    FeatureConfig fc = (FeatureConfig)it.next();
                    if ( lastUpdate < fc.getConfigFileLocator().getLastModified() )
                        return true;

                }

            }

            catch(IOException ex) {
                log.warn("", 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 {

            ServletContext sctx = getServletContext();

            // 設定ファイルが新しくなっていたらリロード

            FeatConfig config = null;
            synchronized(this) {

                if ( checkConfig() )
                    Util.setFeatConfig(sctx, readConfig());

                config = Util.getFeatConfig(sctx);

            }

            // ActionContextを作る

            CommandContext cctx = new CommandContext(config, sctx, req, resp);;

            // 実行

            processor.process(cctx);




            // レスポンス
            /*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.sendError(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);
        }
    }

}
