/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.modules.eqlext.www;

import com.queplix.core.error.IncorrectParameterException;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.WebLoginManager;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eqlext.jxb.gr.Report;
import com.queplix.core.modules.eqlext.utils.ReportBuilder;
import com.queplix.core.modules.web.www.ZipFilter;
import com.queplix.core.utils.www.AbstractServlet;
import com.queplix.core.utils.www.ServletHelper;
import com.queplix.core.utils.xml.XMLHelper;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Get report servlet.
 * <p/>
 * <p>Request to store report data on the server<p>
 * <strong>USAGE</strong>: <pre>POST /getReport/request/&lt;processID&gt;</pre>
 * <p><strong>Parameters</strong>:
 * <li><b>processID</b> - unique process ID</li>
 * </p>
 * <br><br>
 * <p/>
 * <p>Request to cancel execution of report operation on the server<p>
 * <strong>USAGE</strong>: <pre>POST /getReport/cancel/&lt;processID&gt;</pre>
 * <p><strong>Parameters</strong>:
 * <li><b>processID</b> - unique process ID</li>
 * </p>
 * <br><br>
 * <p/>
 * <p>Request to get status (progress) of report operation on the server<p>
 * <strong>USAGE</strong>: <pre>POST /getReport/status/&lt;processID&gt;</pre>
 * <p><strong>Parameters</strong>:
 * <li><b>processID</b> - unique process ID</li>
 * </p>
 * <br><br>
 * <p/>
 * <p>Get report as HTML document<p>
 * <strong>USAGE</strong>: <pre>GET /getReport/response/html/&lt;processID&gt;?
 * transletName=&lt;transletName&gt;</pre>
 * <p><strong>Parameters</strong>:
 * <li><b>processID</b> - unique process ID</li>
 * <li><b>transletName</b> - translet name</li>
 * </p>
 * <br><br>
 * <p/>
 * <p>Get report as Excel document<p>
 * <strong>USAGE</strong>: <pre>GET /getReport/response/excel/&lt;processID&gt;?
 * transletName=&lt;transletName&gt;</pre>
 * <p><strong>Parameters</strong>:
 * <li><b>processID</b> - unique process ID</li>
 * <li><b>transletName</b> - translet name</li>
 * </p>
 * <br><br>
 * <p/>
 * <p>Get report as Word document<p>
 * <strong>USAGE</strong>: <pre>GET /getReport/response/word/&lt;processID&gt;?
 * transletName=&lt;transletName&gt;</pre>
 * <p><strong>Parameters</strong>:
 * <li><b>processID</b> - unique process ID</li>
 * <li><b>transletName</b> - translet name</li>
 * </p>
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:45 $
 */

public class GetReportServlet
        extends AbstractServlet {

    // ------------------------------------------------------- constants

    private static final String REPORT_BUILDER_CACHE_PARAM
            = "__reportBuilderCache";
    private static final String WORD_MIME_TYPE_PARAM = "wordMimeType";
    private static final String EXCEL_MIME_TYPE_PARAM = "excelMimeType";
    private static final String NOT_FOUND_URL_PARAM = "notFoundURL";

    private static final int HTML_REPORT = 0;
    private static final int EXPORTED_REPORT = 1;
    // ------------------------------------------------------- variables

    private String wordMimeType;
    private String excelMimeType;
    private String notFoundUrl;

    // ------------------------------------------------------- methods

    //
    // Init method
    //

    public void init(ServletConfig cfg)
            throws ServletException {

        super.init(cfg);

        wordMimeType = cfg.getInitParameter(WORD_MIME_TYPE_PARAM);
        if(wordMimeType == null) {
            throw new NullPointerException(
                    "Parameter '" + WORD_MIME_TYPE_PARAM + "' is NULL");
        }

        excelMimeType = cfg.getInitParameter(EXCEL_MIME_TYPE_PARAM);
        if(excelMimeType == null) {
            throw new NullPointerException(
                    "Parameter '" + EXCEL_MIME_TYPE_PARAM + "' is NULL");
        }

        notFoundUrl = cfg.getInitParameter(NOT_FOUND_URL_PARAM);
        if(notFoundUrl == null) {
            throw new NullPointerException(
                    "Parameter '" + NOT_FOUND_URL_PARAM + "' is NULL");
        }
    }

    //
    // Service method
    //
    public void service(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {

        String action = req.getPathInfo();
        if(action != null) {
            action = action.substring(1);
        } else {
            action = "";
        }

        // Get process ID
        Long processId = getId(req);
        if(processId == null) {
            throw new IncorrectParameterException("ProcessId");
        }

        // Perform action
        if(action.startsWith("request")) {
            doRequestAction(req, res, processId);
        } else if(action.startsWith("cancel")) {
            doCancelAction(req, res, processId);
        } else if(action.startsWith("status")) {
            doStatusAction(req, res, processId);
        } else if(action.startsWith("response/html")) {
            doHtmlAction(req, res, processId);
        } else if(action.startsWith("response/excel")) {
            doExcelAction(req, res, processId);
        } else if(action.startsWith("response/word")) {
            doWordAction(req, res, processId);
        } else {
            throw new IncorrectParameterException("action", action);
        }


    }

    // ----------------------------------------------------------------- actions

    //
    // Request to store report request in special cache action
    //

    private void doRequestAction(HttpServletRequest req,
                                 HttpServletResponse res,
                                 Long processId)
            throws ServletException,
            IOException {

        long time = System.currentTimeMillis();

        // Get logon session.
        LogonSession ls = WebLoginManager.getLogonSession(req);

        // Get Report object.
        Report report = readReport(processId, req);

        // Construct ReportBuilder object.
        ReportBuilder reportBuilder = new ReportBuilder(ls, processId, report);

        // Put it in cache.
        getReportBuilderCache(req.getSession()).putReportBuilder(reportBuilder);

        // Ok.
        logger.INFO("Cache report request process completed. Time (ms) = " +
                (System.currentTimeMillis() - time));
    }

    //
    // Request to cancel report operations
    //
    private void doCancelAction(HttpServletRequest req,
                                HttpServletResponse res,
                                Long processId)
            throws ServletException,
            IOException {

        long time = System.currentTimeMillis();

        // Get ReportBuilder cache.
        ReportBuilderCache cache = getReportBuilderCache(req.getSession());

        // Get ReportBuilder object from the cache.
        ReportBuilder reportBuilder = cache.getReportBuilder(processId);

        if(reportBuilder != null) {
            try {
                // Call cancel method.
                reportBuilder.cancel();

            } catch (EQLException ex) {
                logger.ERROR(ex);
                throw new ServletException(ex);

            } finally {
                // Remove ReportBuilder from the cache.
                cache.removeReportBuilder(processId);
            }
        }

        // Ok.
        logger.INFO("Cancel report process completed. Time (ms) = " +
                (System.currentTimeMillis() - time));
    }

    //
    // Request to get status of report operations
    //
    private void doStatusAction(HttpServletRequest req,
                                HttpServletResponse res,
                                Long processId)
            throws ServletException,
            IOException {

        // Get ReportBuilder object from the cache.
        ReportBuilder reportBuilder = getReportBuilderCache(req.getSession()).
                getReportBuilder(processId);

        if(reportBuilder != null) {
            int page = reportBuilder.getCurPage();
            int pages = reportBuilder.getCurReqSize();

            // Set headers.
            res.setContentType(ServletHelper.CONTENT_TYPE_TEXT);
            res.setHeader("Cache-Control", "no-cache");
            res.setHeader("Pragma", "no-cache");

            // calculate procent
            if(pages > 0) {
                int procent = (page * 100) / pages;
                res.getWriter().print(procent);
            } else {
                res.getWriter().print(-1);
            }
        }
    }

    //
    // Get report as Html file action
    //
    private void doHtmlAction(HttpServletRequest req,
                              HttpServletResponse res,
                              Long processId)
            throws ServletException,
            IOException {

        long time = System.currentTimeMillis();

        // Set MIME-type.
        res.setContentType(ServletHelper.CONTENT_TYPE_HTML);

        // Do print.
        doPrint(req, res, processId);

        // Ok.
        logger.INFO("Convert report to HTML process completed. Time (ms) = " +
                (System.currentTimeMillis() - time));
    }

    //
    // Get report as Excel file action
    //
    private void doExcelAction(HttpServletRequest req,
                               HttpServletResponse res,
                               Long processId)
            throws ServletException,
            IOException {

        long time = System.currentTimeMillis();

        // Set MIME-type.
        res.setContentType(excelMimeType);

        // Do print.
        doPrint(req, res, processId, EXPORTED_REPORT);

        // Ok.
        logger.INFO("Convert report to Excel process completed. Time (ms) = " +
                (System.currentTimeMillis() - time));
    }

    //
    // Get report as Word file action
    //
    private void doWordAction(HttpServletRequest req,
                              HttpServletResponse res,
                              Long processId)
            throws ServletException,
            IOException {

        long time = System.currentTimeMillis();

        // Set MIME-type.
        res.setContentType(wordMimeType);

        // Do print.
        doPrint(req, res, processId, EXPORTED_REPORT);

        // Ok.
        logger.INFO("Convert report to Word process completed. Time (ms) = " +
                (System.currentTimeMillis() - time));
    }

    // ----------------------------------------------------------------- private methods

    //
    // Print report.
    //

    private void doPrint(HttpServletRequest req,
                         HttpServletResponse res,
                         Long processId)
            throws ServletException, IOException {

        doPrint(req, res, processId, HTML_REPORT);
    }

    private void doPrint(HttpServletRequest req,
                         HttpServletResponse res,
                         Long processId,
                         int reportType)
            throws ServletException, IOException {

        // Login.
        LogonSession ls = WebLoginManager.getLogonSession(req);

        // Get translet name
        String transletName = ServletHelper.getParamAsString(req,
                "transletName");

        // Get ReportBuilder object from the cache.
        ReportBuilder reportBuilder = getReportBuilderCache(req.getSession()).
                getReportBuilder(processId);

        if(reportBuilder == null) {
            // Show "Not Found" screen.
            logger.ERROR(
                    "Cannot find ReportBuilder for processID: " + processId);
            reportNotFound(req, res, processId);

        } else {
            // Generate and print report.
            Map transletParams = null;
            if(reportType == EXPORTED_REPORT) {
                transletParams = new HashMap();
                transletParams.put((Object) "EXPORTED_REPORT",
                        (Object) new Integer(1));
            }

            try {
                boolean isGzip = ZipFilter.isGZipSupported(req);
                if(isGzip) {
                    res.setHeader("Content-Encoding",
                            ServletHelper.CONTENT_TYPE_GZIP);
                    reportBuilder.print(transletName, transletParams,
                            res.getOutputStream());
                } else {
                    reportBuilder.print(transletName, transletParams,
                            res.getWriter());
                }

            } catch (EQLException ex) {
                logger.ERROR(ex);
                throw new ServletException(ex);
            }
        }
    }

    //
    // Show "Not Found" screen.
    //
    protected void reportNotFound(HttpServletRequest req,
                                  HttpServletResponse res,
                                  Long processId)
            throws ServletException,
            IOException {
        res.setContentType(ServletHelper.CONTENT_TYPE_HTML);
        req.getRequestDispatcher(notFoundUrl).forward(req, res);

    }

    //
    // Deserialize Report object.
    //
    private Report readReport(Long processId, HttpServletRequest req)
            throws IOException {
        return (Report) XMLHelper.getParsedObject(Report.class,
                req.getInputStream());
    }

    //
    // Get numeric ID postfix
    //
    private Long getId(HttpServletRequest req)
            throws ServletException {

        Long id = null;
        String action = req.getPathInfo();
        if(action != null) {
            int pos = action.lastIndexOf("/");
            if(pos > 0 && pos < (action.length() - 1)) {
                String s = action.substring(pos + 1);
                try {
                    id = new Long(s);
                } catch (NumberFormatException ex) {
                    throw new ServletException("Bad ID: " + s);
                }
            }
        }
        return id;
    }

    //
    // Get report builder cache.
    //
    protected ReportBuilderCache getReportBuilderCache(HttpSession session) {
        ReportBuilderCache cache = (ReportBuilderCache)
                session.getAttribute(REPORT_BUILDER_CACHE_PARAM);
        if(cache == null) {
            cache = new ReportBuilderCache();
            session.setAttribute(REPORT_BUILDER_CACHE_PARAM, cache);
        }
        return cache;
    }

    /**
     * The method gets word mime type
     *
     * @return String
     */
    public String getWordMimeType() {
        return wordMimeType;
    } // getWordMimeType(): String

    /**
     * The method gets excel mime type
     *
     * @return String
     */
    public String getExcelMimeType() {
        return excelMimeType;
    } // getExcelMimeType(): String

    /**
     * The method gets getNotFoundURL string
     *
     * @return String
     */
    public String getNotFoundURL() {
        return notFoundUrl;
    } // getNotFoundURL(): String

    // ----------------------------------------------------------------- inner class

    /**
     * <p>Storage for <code>ReportBuilder</code> objects</p>
     *
     * @author [ALB] Baranov Andrey
     * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:45 $
     */
    public static class ReportBuilderCache
            implements Serializable, HttpSessionBindingListener {

        private Map cache = new HashMap();

        public ReportBuilder getReportBuilder(Long processId) {
            return (ReportBuilder) cache.get(processId);
        }

        public void putReportBuilder(ReportBuilder reportBuilder) {
            cache.put(reportBuilder.getProcessId(), reportBuilder);
        }

        public void removeReportBuilder(Long processId) {
            cache.remove(processId);
        }

        /**
         * Notifies the object that it is being bound to a HTTP session.
         *
         * @param event session-binding event
         */
        public void valueBound(HttpSessionBindingEvent event) {
        }

        /**
         * Notifies the object that it is being unbound from a HTTP session.
         *
         * @param event session-binding event
         */
        public void valueUnbound(HttpSessionBindingEvent event) {
            for(Iterator it = cache.keySet().iterator(); it.hasNext();) {
                Long processId = (Long) it.next();
                ReportBuilder reportBuilder = getReportBuilder(processId);

                // Cancel operation.
                try {
                    reportBuilder.cancel();
                } catch (Throwable t) {
                }
            }
        }
    }
}
