/*
 
    Copyright IBM Corp. 2012, 2016
    This file is part of Anomaly Detection Engine for Linux Logs (ADE).

    ADE is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ADE is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ADE.  If not, see <http://www.gnu.org/licenses/>.
 
*/
package org.openmainframe.ade.ext.output;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;

import org.openmainframe.ade.Ade;
import org.openmainframe.ade.data.IAnalyzedInterval;
import org.openmainframe.ade.data.IAnalyzedMessageSummary;
import org.openmainframe.ade.data.ISource;
import org.openmainframe.ade.exceptions.AdeException;
import org.openmainframe.ade.exceptions.AdeFlowException;
import org.openmainframe.ade.exceptions.AdeInternalException;
import org.openmainframe.ade.impl.PropertyAnnotation.Property;
import org.openmainframe.ade.impl.flow.factory.jaxb.FramingFlowType;
import org.openmainframe.ade.impl.utils.DateTimeUtils;
import org.openmainframe.ade.output.AnalyzedIntervalOutputer;

public class ExtAnalyzedIntervaFast18lXmlStorer extends AnalyzedIntervalOutputer {

    private File m_outFile = null;
    private PrintStream m_outStream;
    @Property(key = "outputTimeZone", help = "Time zone used to output analysed intervals", required = false)
    private TimeZone m_outputTimeZone = DateTimeUtils.GMT_TIMEZONE;
    private GregorianCalendar m_gc;
    private static DatatypeFactory s_dataTypeFactory = null;
    private ISource m_source;
    private int m_offset;
    private FramingFlowType m_framingFlowType;

    @Override
    public void setupSourceAndFlowType(ISource source,
            FramingFlowType framingFlowType) throws AdeException {
        m_source = source;
        if (s_dataTypeFactory == null) {
            try {
                s_dataTypeFactory = DatatypeFactory.newInstance();
            } catch (DatatypeConfigurationException e) {
                throw new AdeInternalException("Failed to instantiate data factory for calendar", e);
            }
        }

        // FIXME this is not thread safe
        m_gc = new GregorianCalendar(m_outputTimeZone);
        m_framingFlowType = framingFlowType;
    }

    @Override
    public void beginOfStream() throws AdeException, AdeFlowException {
        // TODO Auto-generated method stub

    }

    @Override
    public void incomingObject(IAnalyzedInterval analyzedInterval) throws AdeException,
            AdeFlowException {
        openFile(analyzedInterval);

        m_offset = 0;
        printHeader();
        printOpenInterval();
        m_offset += 4;
        printIntervalInfo(analyzedInterval);
        for (IAnalyzedMessageSummary ams : analyzedInterval.getAnalyzedMessages()) {
            printMessage(ams);
        }
        m_offset -= 4;
        printCloseInterval();
        printFooter();
        closeFile();
    }

    protected void openFile(IAnalyzedInterval analyzedInterval)
            throws AdeException {
        /* Get the XML Filename.  The filename in the OutputFilenameGenerator is for 3X XML file. 
         * We need to convert the filename into 1.8 XML file.  */
        m_outFile = getInterval18XMLFile(analyzedInterval);

        if (m_verbose) {
            System.out.println("saving xml in " + m_outFile.getAbsolutePath());
        }

        try {
            File parentdir = m_outFile.getParentFile();
            parentdir.mkdirs();

            m_outStream = new PrintStream(m_outFile, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void closeFile() {
        m_outStream.close();
    }

    private void printHeader() {
        out("<?xml version='1.0' encoding='UTF-8' ?>");
        out("<?xml-stylesheet href='./xslt/AdeCoreInterval.xsl' type='text/xsl' ?>");
    }

    private void printOpenInterval() {
        out("<interval xsi:noNamespaceSchemaLocation=\"xslt/AdeCoreInterval.xsd\" xmlns=\"http://www.example.org/AdeCoreInterval\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");

    }

    @SuppressWarnings("deprecation")
    private void printIntervalInfo(IAnalyzedInterval ai) throws AdeException {
        out("<sys_id>" + m_source + "</sys_id>");
        m_gc.setTimeInMillis(ai.getInterval().getIntervalStartTime());
        out("<start_time>" + s_dataTypeFactory.newXMLGregorianCalendar(m_gc) + "</start_time>");
        m_gc.setTimeInMillis(ai.getInterval().getIntervalEndTime());
        out("<end_time>" + s_dataTypeFactory.newXMLGregorianCalendar(m_gc) + "</end_time>");
        out("<anomaly_score>" + ai.getScore() + "</anomaly_score>");
        out("<model_internal_id>" + ai.getModelInternalId() + "</model_internal_id>");
        out("<ade_version>" + ai.getAdeVersion().toInt() + "</ade_version>");
    }

    private void printCloseInterval() {
        out("</interval>");
    }

    private void printFooter() {
        // nothing to put here for now, AFAICT
    }

    private void printMessage(IAnalyzedMessageSummary ams) throws AdeException {
        out("<interval_message msg_id=\"" + ams.getMessageId() + "\">");
        m_offset += 4;
        out("<num_instances>" + ams.getNumberOfAppearances() + "</num_instances>");
        out("<bernoulli>" + ams.getStatistics().getDoubleStat("FullBernoulliClusterAwareScore.main") + "</bernoulli>");
        out("<cluster_id>" + ams.getStatistics().getStringStat("ClusteringContextScore.clusterId") + "</cluster_id>");
        out("<poisson>" + ams.getStatistics().getDoubleStat("LogNormalScore.main") + "</poisson>");
        out("<anomaly>" + ams.getStatistics().getDoubleStat("anomaly") + "</anomaly>");
        out("<cluster_status>" + ams.getStatistics().getStringStat("ClusteringContextScore.status") + "</cluster_status>");
        out("<critical_words>" + ams.getStatistics().getDoubleStat("CriticalWordsScorer.mail") + "</critical_words>");
        out("<text_sum>" + ams.getTextSummary() + "</text_sum>");
        out("<text_smp>" + ams.getTextSample() + "</text_smp>");
        printTimeLine(ams);
        m_offset -= 4;
        out("</interval_message>");

    }

    protected void printTimeLine(IAnalyzedMessageSummary ams) {
        out("<time_vec>");
        m_offset += 4;
        for (int i = 0; i < ams.getTimeLine().length; ++i) {
            out("<occ>" + ams.getTimeLine()[i] + "</occ>");
        }
        m_offset -= 4;
        out("</time_vec>");
    }

    private void out(String string) {
        m_outStream.println(String.format("%s" + m_offset + "s%s", "", string));
    }

    @Override
    public void endOfStream() throws AdeException, AdeFlowException {
        // TODO Auto-generated method stub

    }

    /**
     * Return the Interval XML Filename 
     * @return
     * @throws AdeException 
     */
    private File getInterval18XMLFile(IAnalyzedInterval analyzedInterval) throws AdeException {
        ExtOutputFilenameGenerator outputFilenameGenerator = (ExtOutputFilenameGenerator) Ade.getAde().getConfigProperties().getOutputFilenameGenerator();

        return outputFilenameGenerator.getIntervalXmlV1File(analyzedInterval, m_framingFlowType);
    }

}