/*
 * 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.services.www;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.error.IncorrectParameterException;
import com.queplix.core.integrator.security.AccessRightsManager;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.modules.services.ServiceInitManager;
import com.queplix.core.modules.services.ServiceStartupManager;
import com.queplix.core.utils.FileHelper;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.SystemHelper;
import com.queplix.core.utils.www.AbstractServlet;
import com.queplix.core.utils.www.ServletHelper;

import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * QW Installation Tools front-end.
 *
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @version $Revision: 1.1 $ $Date: 2006/04/18 12:04:16 $
 */
public class ToolsServlet
        extends AbstractServlet
        implements SingleThreadModel {

    // ----------------------------------------------------- Constants

    // Available actions.
    private static final String SCHEDULER_ACTION = "scheduler";
    private static final String INSTALL_TOOL_ACTION = "installTool";
    private static final String AUTORUN_ACTION = "autorun";
    public static final String PORTAL_CONFIG_FILE = "portal-config.xml";

    // Request parameters.
    private static final String SCHEDULER_CMD_PARAM = "scheduler";
    private static final String SCHEDULER_PARAM_PARAM = "param";
    private static final String XMLMETA_DIR_PARAM = "xmlmeta";
    private static final String FORCE_UPDATE_PARAM = "force";

    // ----------------------------------------------------- Fields

    private LogonSession ls = null;
    private List<String> status = new ArrayList<String>();

    // ----------------------------------------------------- Servlet API methods

    /*
    * (non-Javadoc)
    *
    * @see HttpServlet#service(HttpServletRequest, HttpServletResponse)
    */

    public void service(HttpServletRequest request,
                        HttpServletResponse response)
            throws ServletException, IOException {

        // Check if the servlet is run from the localhost
        if(!request.getRemoteAddr().equalsIgnoreCase("127.0.0.1")) {
            throw new ServletException("Cannot use this servlet.");
        }

        long time = System.currentTimeMillis();
        logger.INFO("QueWeb tools front-end started.");

        // Set response headers.
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");

        // Login as administrator.
        ls = AccessRightsManager.getSystemLogonSession();

        status.clear();

        // Get the action to do.
        String action = request.getPathInfo();
        if(action == null) {
            displayStartPage(request, response);
            return;
        }
        action = action.substring(1);

        if(action.equalsIgnoreCase(AUTORUN_ACTION)) {
            runInstallTool(new ServiceInitManager().getXmlMetaPath(), false);
        } else if(action.equalsIgnoreCase(INSTALL_TOOL_ACTION)) {
            runInstallTool(request);
        } else if(action.equalsIgnoreCase(SCHEDULER_ACTION)) {
            runScheduler(request);
        } else {
            throw new IncorrectParameterException("action", action);
        }

        // Display response.
        displayStatus(request, response, action.equalsIgnoreCase(
                AUTORUN_ACTION));

        // Ok.
        logger.INFO("Request completed. Time (ms) = " +
                (System.currentTimeMillis() - time));

    }

    // ----------------------------------------------------- Action handlers
    private void runInstallTool(HttpServletRequest request)
            throws ServletException {
        String xmlMetaDirParam = ServletHelper.getParamAsString(request,
                XMLMETA_DIR_PARAM);
        String force = ServletHelper.getParamAsString(request,
                FORCE_UPDATE_PARAM, false);
        runInstallTool(xmlMetaDirParam, force != null);
    }

    private void runInstallTool(String xmlMetaDirParam, boolean force)
            throws ServletException {
        if(xmlMetaDirParam == null || StringHelper.isEmpty(xmlMetaDirParam)) {
            throw new GenericSystemException(
                    "Unspecified parameter '" + XMLMETA_DIR_PARAM + "'");
        }

        List<ServiceInitManager> sims = new ArrayList<ServiceInitManager>();

        // Input validation
        String[] xmlMetaDirs = StringHelper.split(xmlMetaDirParam,
                File.pathSeparator, false);
        List<String> metaDirs = new ArrayList<String>();
        for(String xmlMetaDir : xmlMetaDirs) {
            xmlMetaDir = xmlMetaDir.trim();
            if(StringHelper.isEmpty(xmlMetaDir)) {
                throw new IncorrectParameterException(
                        "Cannot process empty path");
            }

            File xmlMetaFile = new File(xmlMetaDir);
            if(!xmlMetaFile.exists()) {
                throw new IncorrectParameterException(
                        "Specified path " + xmlMetaFile + " doesn't exist");
            }

            if(!xmlMetaFile.isDirectory()) {
                throw new IncorrectParameterException("Specified path "
                        + xmlMetaFile + " must be a directory");
            }

            sims.add(new ServiceInitManager(xmlMetaDir));
            metaDirs.add(xmlMetaDir);
        }

        runEntityTool(sims.toArray(new ServiceInitManager[0]), force);
        runFocusTool(metaDirs.toArray(new String[0]), force);
        runSystemChartsTool(metaDirs.toArray(new String[0]), force);
        runPortalsTool(metaDirs.toArray(new String[0]), force);

        for(ServiceInitManager sim : sims) {
            runScriptTool(sim, force);
        }
        
        for(ServiceInitManager sim : sims) {
            runCustomTool(sim, force);
        }

        (new ServiceInitManager()).setXmlMetaPath(xmlMetaDirParam);

    }

    //
    // Runs the Form Installation Tool.
    //
    private void runFocusTool(String[] xmlMetaDirs, boolean force) {
        //we dont check if directory exists, because if this is only misspelling all permissions, roles and other settings will be reset.
        long dirsSize = 0;
        StringBuffer focusXmlDirs = new StringBuffer();
        String[] focusXmlDirsArray = new String[xmlMetaDirs.length];
        for(int i = 0; i < xmlMetaDirs.length; i++) {
            String focusDir = ServiceInitManager.getFocusXmlDir(xmlMetaDirs[i]);
            focusXmlDirsArray[i] = focusDir;
            dirsSize += FileHelper.calculateFileSize(focusDir);
            focusXmlDirs.append(focusDir).append(File.pathSeparator);
        }
        //we build single digest for concatinated focus dirs, otherwise it will not make sense.
        String newDigest = String.valueOf(dirsSize);
        String metaDirs = focusXmlDirs.toString();
        String oldDigest = ServiceInitManager.getFocusXmlDirDigest(ls.getUser(),
                metaDirs);

        if(oldDigest.equals(newDigest) && !force) {
            status.add("Form Installation Tool directories (" + metaDirs
                    + ") contents have not changed -- no action needed");
        } else {
            ServiceStartupManager.runFocusTool(ls, focusXmlDirsArray);
            ServiceInitManager.setFocusXmlDirDigest(newDigest, metaDirs,
                    ls.getUser());
            runContextMenuTool(xmlMetaDirs);
            status.add("Form Installation Tool completed (" + metaDirs + ")");
        }
    }

    //
    // Runs the Entity Installation Tool.
    //
    private void runEntityTool(ServiceInitManager[] sims, boolean force) {
        List<ServiceInitManager> simList = new ArrayList<ServiceInitManager>();
        Map<ServiceInitManager, String> digestMap
                = new HashMap<ServiceInitManager, String>();

        for(ServiceInitManager sim : sims) {
            File entityDir = new File(sim.getEntityXmlDir());
            if(!entityDir.exists()) {
                continue;
            }

            long dirSize = FileHelper.calculateFileSize(sim.getEntityXmlDir());
            String newDigest = String.valueOf(dirSize);
            String oldDigest = sim.getEntityXmlDirDigest();

            if(oldDigest.equals(newDigest) && !force) {
                status.add("Entity Installation Tool directory (" +
                        sim.getEntityXmlDir()
                        + ") contents have not changed -- no action needed");
            } else {
                simList.add(sim);
                digestMap.put(sim, newDigest);
            }

        }
        if(simList.size() > 0) {
            ServiceStartupManager.runEntityTool(ls, sims);
            for(ServiceInitManager sim : simList) {
                String newDigest = digestMap.get(sim);
                sim.setEntityXmlDirDigest(newDigest);
                status.add("Entity Installation Tool completed ("
                        + sim.getEntityXmlDir() + ")");
            }
        }
    }

    private void runContextMenuTool(String[] xmlMetaDirs) {
        List<String> contextMenuDirs = new ArrayList<String>();
        for(String xmlMetaDir : xmlMetaDirs) {
            contextMenuDirs.add(ServiceInitManager.getContextMenuXmlDir(
                    xmlMetaDir));
        }
        ServiceStartupManager.runContextMenuTool(ls,
                (String[]) contextMenuDirs.toArray(new String[0]));
    }


    private void runPortalsTool(String[] xmlMetaDirs, boolean force) {
        File portalConfigFile;
        for (String xmlMetaDir : xmlMetaDirs) {
            portalConfigFile = new File(xmlMetaDir + File.separator + 
                PORTAL_CONFIG_FILE);
            if (portalConfigFile.exists()) {
                long fileSize = portalConfigFile.length();
                String newDigest = String.valueOf(fileSize);
                String oldDigest = ServiceInitManager.
                    getPortalsConfigSize(ls.getUser(), portalConfigFile.getPath());
                if(oldDigest.equals(newDigest) && !force) {
                    status.add("Portals Config Installation Tool file (" + 
                               portalConfigFile.getPath() + ") contents have" +
                               " not changed -- no action needed");
                } else {
                    ServiceStartupManager.runPortalTools(ls, portalConfigFile);
                    status.add("Portals Config Installation Tool completed (" + 
                        portalConfigFile.getPath() + ")");
                    ServiceInitManager.setPortalsConfigDigest(newDigest, 
                        portalConfigFile.getPath(), ls.getUser());
                }
                return;
            }
        }
        status.add("Portals config file was not found - portals were not installed");
    }

    //
    // Runs the System Charts Installation Tool.
    //

    private void runSystemChartsTool(String[] xmlMetaDirs, boolean force) {
        //we dont check if directory exists, because if this is only misspelling all permissions, roles and other settings will be reset.
        long dirsSize = 0;
        StringBuffer chartsXmlDirs = new StringBuffer();
        String[] chartsXmlDirsArray = new String[xmlMetaDirs.length];
        for(int i = 0; i < xmlMetaDirs.length; i++) {
            String chartDir = ServiceInitManager.getSystemChartsXmlDir(xmlMetaDirs[i]);
            chartsXmlDirsArray[i] = chartDir;
            dirsSize += FileHelper.calculateFileSize(chartDir);
            chartsXmlDirs.append(chartDir).append(File.pathSeparator);
        }
        //we build single digest for concatinated focus dirs, otherwise it will not make sense.
        String newDigest = String.valueOf(dirsSize);
        String metaDirs = chartsXmlDirs.toString();
        String oldDigest = ServiceInitManager.getSystemChartsXmlDirDigest(
                ls.getUser(), metaDirs);

        if(oldDigest.equals(newDigest) && !force) {
            status.add("System Charts Installation Tool directories (" + metaDirs
                    + ") contents have not changed -- no action needed");
        } else {
            ServiceStartupManager.runSystemChartsTool(ls, chartsXmlDirsArray);
            ServiceInitManager.setSystemChartsXmlDirDigest(
                    newDigest, metaDirs, ls.getUser());
            status.add("System Charts Installation Tool completed (" + metaDirs + ")");
        }

        
    }

    //
    // Runs the Script Installation Tool.
    //
    private void runScriptTool(ServiceInitManager sim, boolean force) {
        File scriptFile = new File(sim.getScriptXmlFile());
        if(!scriptFile.exists()) {
            return;
        }

        long dirSize = FileHelper.calculateFileSize(sim.getScriptXmlFile());
        String newDigest = String.valueOf(dirSize);
        String oldDigest = sim.getScriptXmlFileDigest();

        if(oldDigest.equals(newDigest) && !force) {
            status.add("Script Installation Tool file contents (" +
                    sim.getScriptXmlFile()
                    + ") have not changed -- no action needed");
        } else {
            ServiceStartupManager.runScriptTool(ls, sim);
            sim.setScriptXmlFileDigest(newDigest);
            status.add("Script Installation Tool completed ("
                    + sim.getScriptXmlFile() + ")");
        }

    }

    //
    // Runs the Entity Customization Tool.
    //
    private void runCustomTool(ServiceInitManager sim, boolean force) {
        File customDir = new File(sim.getCustomXmlDir());
        if(!customDir.exists()) {
            return;
        }

        long dirSize = FileHelper.calculateFileSize(sim.getCustomXmlDir());
        String newDigest = String.valueOf(dirSize);
        String oldDigest = sim.getCustomXmlDirDigest();

        if(oldDigest.equals(newDigest) && !force) {
            status.add("Entity Customization Tool directory (" +
                    sim.getCustomXmlDir()
                    + ") contents have not changed -- no action needed");
        } else {
            ServiceStartupManager.runCustomTool(ls, sim);
            sim.setCustomXmlDirDigest(newDigest);
            status.add("Entity Customization Tool completed ("
                    + sim.getCustomXmlDir() + ")");
        }
    }

    //
    // Runs the Scheduler.
    //
    private void runScheduler(HttpServletRequest request)
            throws ServletException {

        // Get scheduler param.
        String cmd = ServletHelper.getParamAsString(request,
                SCHEDULER_CMD_PARAM);
        String param = ServletHelper.getParamAsString(request,
                SCHEDULER_PARAM_PARAM, "status");

        String autostart = ServletHelper.getParamAsString(request, "autostart",
                false);
        int lines = ServletHelper.getParamAsInt(request, "lines");
        int timeout = 60000;

        // Save scheduler cmd.
        ServiceInitManager sim = new ServiceInitManager();
        sim.setSchedulerCmd(cmd);
        sim.setSchedulerAutostart(autostart != null);

        // Run scheduler.
        if(param.equalsIgnoreCase("start")) {
            status.add(ServiceStartupManager.startScheduler(lines, timeout));
        } else if(param.equalsIgnoreCase("stop")) {
            status.add(ServiceStartupManager.stopScheduler(lines, timeout));
        } else if(param.equalsIgnoreCase("status")) {
            status.add(ServiceStartupManager.statusScheduler(lines, timeout));
        } else {
            throw new IncorrectParameterException(SCHEDULER_PARAM_PARAM, param);
        }
    }

    //
    // Displays the status window HTML.
    //
    private void displayStatus(HttpServletRequest request,
                               HttpServletResponse response, boolean autoStart)
            throws ServletException, IOException {

        // Set HTTP response headers.
        response.setContentType(ServletHelper.CONTENT_TYPE_HTML);

        // Print HTML.
        PrintWriter out = response.getWriter();
        if(!autoStart) {
            out.println("<html>");
            out.println("<head>");
            out.println("<title>QueWeb Installation Tools Status</title>");
            out.println(
                    "<link rel='STYLESHEET' type='text/css' href='/style/control.css'>");
            out.println(
                    "<link rel='STYLESHEET' type='text/css' href='/style/main.css'>");
            out.println("<script>");
            out.println("   function completeWindow() {");
            out.println("       try {");
            out.println("           resizeTo(400, 200);");
            out.println("       } catch(e) {}");
            out.println("   }");
            out.println("</script>");
            out.println("</head>");
            out.println(
                    "<body style='overflow-x:hidden;' margintop='0' marginleft='0' onload='completeWindow()'>");
            out.println("<pre>");
        }

        out.println(StringHelper.join((String[]) status.toArray(
                new String[status.size()]), "\n"));

        if(!autoStart) {
            out.println("</pre>");
            out.println("</body>");
            out.println("</html>");
        }
        out.flush();
    }

    protected void displayStartPage(HttpServletRequest request,
                                    HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        // Check permissions.
        if(SystemHelper.isProductionMode()) {
            // check availability in production mode
//            if ( ! request.getRemoteAddr().equalsIgnoreCase("127.0.0.1") ) {
//                throw new ServletException( "Cannot use this servlet in production mode." );
//            }
        }

        if(request.getParameter("forms") == null) {
            displayFrameSet(out);
        } else {
            dispayFormsFrame(out);
        }

        out.close();
    }

    private void displayFrameSet(PrintWriter out) {
        out.write("<html>\n");
        out.write("<head>\n");
        out.write("    <title>QueWeb Installation Tools Web Manager</title>\n");
        out.write(
                "    <link rel=\"STYLESHEET\" type=\"text/css\" href=\"/style/control.css\">\n");
        out.write(
                "    <link rel=\"STYLESHEET\" type=\"text/css\" href=\"/style/main.css\">\n");
        out.write("</head>\n");
        out.write("<frameset rows='80%,20%'>");
        out.write("    <frame src='tools?forms=true'></frame>");
        out.write("    <frame src='' name='results_frame'></frame>");
        out.write("</frameset>");
        out.write("</html>");
    }

    private void dispayFormsFrame(PrintWriter out) {
        ServiceInitManager sim = new ServiceInitManager();

        out.write("<html>\n");
        out.write("<head>\n");
        out.write("    <title>QueWeb Installation Tools Web Manager</title>\n");
        out.write(
                "    <link rel=\"STYLESHEET\" type=\"text/css\" href=\"/style/control.css\">\n");
        out.write(
                "    <link rel=\"STYLESHEET\" type=\"text/css\" href=\"/style/main.css\">\n");
        out.write("</head>\n");
        out.write(
                "<body style=\"overflow-x:hidden;\" margintop=\"0\" marginleft=\"0\">\n");
        out.write("<h2>QueWeb Installation Tools Web Manager</h2>\n");
        out.write("<br>\n");
        out.write("\n");
        out.write(
                "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"670\">\n");
        out.write("<tr>\n");
        out.write("<td>\n");
        out.write("    <!-- Installation Tool. -->\n");
        out.write("    <form action=\"tools/" + INSTALL_TOOL_ACTION
                + "\" method=\"POST\" autocomplete='on' target=\"results_frame\">\n");
        out.write("    <fieldset>\n");
        out.write("    <legend>Installation Tool</legend>\n");
        out.write(
                "    <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n");
        out.write("        <tr>\n");
        out.write(
                "            <td><img src=\"/pics/null.gif\" width=\"200\" height=\"0\"/></td>\n");
        out.write(
                "            <td><img src=\"/pics/null.gif\" width=\"400\" height=\"0\"/></td>\n");
        out.write(
                "            <td><img src=\"/pics/null.gif\" width=\"70\" height=\"0\"/></td>\n");
        out.write("        </tr>\n");
        out.write("        <tr>\n");
        out.write("            <td align=\"right\">\n");
        out.write(
                "                <label>XML Meta Directory(ies)</label>&nbsp;\n");
        out.write("            </td>\n");
        out.write("            <td>\n");
        out.write("                <input\n");
        out.write("                    name=\"" + XMLMETA_DIR_PARAM + "\"\n");
        out.write("                    type=\"text\"\n");
        out.write("                    style=\"width:100%\"\n");
        out.write("                    value=\"");

        out.print(sim.getXmlMetaPath());

        out.write("\">\n");
        out.write("                </input>\n");
        out.write("            </td>\n");
        out.write(
                "            <td rowspan=\"2\" valign=\"top\" align=\"right\">\n");
        out.write(
                "                <button type=\"submit\" name=\"entity-tool\" class=\"btnstdMenu\" style=\"width:60\">Run</button>\n");
        out.write("            </td>\n");
        out.write("        </tr>\n");
        out.write("        <tr>\n");
        out.write("            <td></td>\n");
        out.write("            <td>\n");
        out.write("            <label>Force update</label>&nbsp;\n");
        out.write("            <input name=\"" + FORCE_UPDATE_PARAM + "\"\n");
        out.write("                   type=\"checkbox\"\n");
        out.write("                   value=\"1\"\n");
        out.write("                    />\n");
        out.write("            </td>\n");
        out.write("        </tr>");
        out.write("    </table>\n");
        out.write("    </fieldset>\n");
        out.write("    </form>\n");
        out.write("    <!-- // Installation Tool. -->\n");
        out.write("\n");
/*
        out.write("    <!-- Scheduler. -->\n");
        out.write("    <form action=\"tools/scheduler\" method=\"POST\" autocomplete='on' target=\"results_frame\">\n");
        out.write("    <fieldset>\n");
        out.write("    <legend>Scheduler</legend>\n");
        out.write("    <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n");
        out.write("        <tr>\n");
        out.write("            <td><img src=\"/pics/null.gif\" width=\"150\" height=\"0\"/></td>\n");
        out.write("            <td><img src=\"/pics/null.gif\" width=\"400\" height=\"0\"/></td>\n");
        out.write("            <td><img src=\"/pics/null.gif\" width=\"70\" height=\"0\"/></td>\n");
        out.write("        </tr>\n");
        out.write("        <tr>\n");
        out.write("            <td align=\"right\">\n");
        out.write("                <label>Scheduler Launcher</label>&nbsp;\n");
        out.write("            </td>\n");
        out.write("            <td colspan=\"2\">\n");
        out.write("                <input\n");
        out.write("                    name=\"scheduler\"\n");
        out.write("                    type=\"text\"\n");
        out.write("                    style=\"width:100%\"\n");
        out.write("                    maxlength=\"240\"\n");
        out.write("                                        value=\"");
        out.print( sim.getSchedulerCmd() );
        out.write("\">\n");
        out.write("                </input>\n");
        out.write("            </td>\n");
        out.write("            <td>&nbsp;</td>\n");
        out.write("        </tr>\n");
        out.write("        <tr>\n");
        out.write("            <td valign=\"top\" colspan=\"2\" align=\"right\">\n");
        out.write("                <label>Autostart</label>&nbsp;\n");
        out.write("                                <input name=\"autostart\"\n");
        out.write("                                                type=\"checkbox\"\n");
        out.write("                                                value=\"1\"\n");
        
        if( sim.getSchedulerAutostart() ) {
            
            out.write("\n");
            out.write("                                                checked=\"1\"\n");
            
        }
        
        out.write("\n");
        out.write("                                />\n");
        out.write("                                &nbsp;\n");
        out.write("                                &nbsp;\n");
        out.write("                <label>Log lines</label>&nbsp;\n");
        out.write("                <input name=\"lines\" type=\"text\" maxlength=\"5\" value=\"100\" style=\"width:40\"/>\n");
        out.write("                                &nbsp;\n");
        out.write("                                &nbsp;\n");
        out.write("                <select name=\"param\">\n");
        out.write("                    <option value=\"start\">Start Scheduler</option>\n");
        out.write("                    <option value=\"stop\">Stop Scheduler</option>\n");
        out.write("                    <option value=\"status\">Check Scheduler status</option>\n");
        out.write("                </select>\n");
        out.write("                &nbsp;\n");
        out.write("                <button type=\"submit\" name=\"run\" class=\"btnstdMenu\" style=\"width:60\">Go!</button>\n");
        out.write("            </td>\n");
        out.write("        </tr>\n");
        out.write("    </table>\n");
        out.write("    </fieldset>\n");
        out.write("    </form>\n");
        out.write("    <!-- // Scheduler. -->\n");
        out.write("\n");
*/
        out.write("</td>\n");
        out.write("</tr>\n");
        out.write("</table>\n");
        out.write("\n");
        out.write("</body>\n");
        out.write("</html>\n");
    }

}
