/*
 * 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.config.actions;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.integrator.chart.ChartDataManager;
import com.queplix.core.integrator.security.AccessRightsManager;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.NoSuchUserException;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eqlext.jxb.gr.Chart;
import com.queplix.core.modules.eqlext.jxb.gr.types.ChartVisibiltyType;
import com.queplix.core.modules.jeo.JEObjectHandler;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocal;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocalHome;
import com.queplix.core.modules.jeo.gen.ChartObject;
import com.queplix.core.modules.jeo.gen.ChartObjectHandler;
import com.queplix.core.modules.services.Action;
import com.queplix.core.modules.services.ServiceStartupManager;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.xml.XMLBinding;
import com.queplix.core.utils.xml.XMLFactory;
import com.queplix.core.utils.xml.XMLHelper;
import com.queplix.core.utils.xml.XMLWrapper;

/**
 * <p>Action which parses config xml file(s) and fills System Charts definitions</p>
 *
 * @author Michael Trofimov
 */
public class SystemChartsInstallationAction extends Action {

    private static class SingletonHolder {
        static XMLWrapper XML_WRAPPER = XMLFactory.getXMLWrapper();
        static XMLBinding XML_BINDING = XMLFactory.getXMLBinding();
    }

    /**
     * No javadoc
     */
    @Override
    public Serializable perform() {
        String[] chartDirs = (String[]) getContext().getParameter(
                ServiceStartupManager.SYSTEM_CHARTS_XML_DIR_PARAM);
        if (chartDirs == null) {
            throw new NullPointerException("Parameter "
                    + ServiceStartupManager .SYSTEM_CHARTS_XML_DIR_PARAM
                            + " is NULL");
        }

        try {
            INFO("PROCESS STARTED...");
            long time = System.currentTimeMillis();

            Map<String, Chart> charts = parseSystemCharts(chartDirs);
            INFO("parse system charts configs - ok");

            deploySystemCharts(charts);
            INFO("deploy system charts configs - ok");

            INFO("Process last(s): "
                    + (System.currentTimeMillis() - time) / 1000);
            INFO("PROCESS DONE !!!");

        } catch (GenericSystemException ex) {
            ERROR(ex);
            throw ex;

        } catch (Exception ex) {
            ERROR(ex);
            throw new GenericSystemException(
                    "Unknown exception: " + ex.getMessage(), ex);
        }

        return null;
    }

    /**
     * Get FocusConfigManager EJB reference
     *
     * @return FocusConfigManager remote interface
     */
    private JEOManagerLocal getJEOManager() {
        return (JEOManagerLocal) getContext().getCOM().getLocalObject(
                JNDINames.JEOManager, JEOManagerLocalHome.class);
    }

    private void deploySystemCharts(Map<String, Chart> charts) {
        ChartDataManager.clearChartsCache();

        JEOManagerLocal jeoManager = getJEOManager();
        LogonSession ls = AccessRightsManager.getSystemLogonSession();

        List<Chart> chartIndexes = new ArrayList<Chart>(charts.values());

        try {
            List chartHnds = ChartObjectHandler.selectSystemCharts(jeoManager, ls);

            // updates existing charts
            if (chartHnds != null) {
                for (Iterator it = chartHnds.iterator(); it.hasNext();) {
                    JEObjectHandler chartHnd = (JEObjectHandler) it.next();
                    ChartObject chartObject = (ChartObject) chartHnd.getJEObject();
        
                    Chart chart = charts.get(chartObject.getName());
                    if (chart != null) {
                        updateChartObject(chartHnd, chart, chartIndexes);
                        // removes updated chart object from the collection
                        charts.remove(chartObject.getName());
                    } else {
                        // removes non-existing chart
                        chartHnd.remove();
                    }
                    chartHnd.commit();
                }
            }
    
            // adds new charts
            for (Chart chart : charts.values()) {
                JEObjectHandler chartHnd = jeoManager.create(
                        ls, ChartObjectHandler.class);
                updateChartObject(chartHnd, chart, chartIndexes);
                chartHnd.commit();
            }

        } catch (EQLException e) {
            throw new GenericSystemException(
                    "EQLException: " + e.getMessage(), e);
        } catch (NoSuchUserException e) {
            throw new GenericSystemException(
                    "NoSuchUserException: " + e.getMessage(), e);
        }
    }

    private void updateChartObject(
            JEObjectHandler jeoHandler, Chart chart, List<Chart> chartIndexes)
                    throws NoSuchUserException {

        ChartObject chartObject = (ChartObject) jeoHandler.getJEObject();
        chartObject.setName(chart.getName());
        chartObject.setSystem_chart(1);
        chartObject.setPublic_chart(
                chart.getVisibilty().equals(ChartVisibiltyType.PUBLIC) ? 1 : 0);
        chartObject.setChart_position(chartIndexes.indexOf(chart));
        chartObject.setOwner_id(AccessRightsManager.getAdminUserId());
        chartObject.setBody(XMLHelper.writeObject(chart));
    }

    private static final String CONFIG_FILES_EXTENSION = "xml";

    private Map<String, Chart> parseSystemCharts(String[] chartDirs) {
        Map<String, Chart> charts = new LinkedHashMap<String, Chart>();

        for (String chartDir : chartDirs) {
            File chartDirFile = new File(chartDir);
            if(!chartDirFile.exists() || !chartDirFile.isDirectory()) {
                ERROR("Bad system charts config dir: "
                        + chartDirFile.getAbsolutePath());
            } else {
                // get list of files
                File[] files = chartDirFile.listFiles(
                        new FilenameFilter() {
                            public boolean accept(File dir, String name) {
                                String ext = CONFIG_FILES_EXTENSION;
                                return name.substring(
                                        name.length() - ext.length()).equals(
                                        ext);
                            }
                        });
                try {
                    // cycle read focus config XML files
                    for(File file : files) {
                        INFO("  read from " + file.getCanonicalPath());
                        Chart chart = parseSystemChart(file);

                        String fileName = file.getName();
                        fileName = fileName.substring(
                                0, fileName.length() - CONFIG_FILES_EXTENSION.length() - 1);
                        String chartName = chart.getVisibilty().equals(ChartVisibiltyType.PRIVATE)
                                ? fileName : chart.getChartParams().getTitle();
                        chart.setName(chartName);

                        charts.put(chartName, chart);
                    }
                } catch (IOException ex) {
                    ERROR(ex);
                    throw new GenericSystemException(
                            "IO exception. Can't process parsing system charts: "
                                    + ex.getMessage(), ex);
                }
            }
        }
        
        return charts;
    }

    private Chart parseSystemChart(File chartFile) {
        XMLWrapper xmlWrapper = SingletonHolder.XML_WRAPPER;
        XMLBinding xmlBinding = SingletonHolder.XML_BINDING;

        Document document = xmlWrapper.getDocument(chartFile, false);
        return (Chart) xmlBinding.xmlToJava(Chart.class, document);
	}
}
