/*******************************************************************************
 * Copyright (c) 2016-2019 École Polytechnique de Montréal
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/

package org.eclipse.tracecompass.tmf.analysis.xml.core.tests.model;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.pattern.stateprovider.XmlPatternAnalysis;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.segment.TmfXmlPatternSegment;
import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
import org.eclipse.tracecompass.tmf.analysis.xml.core.tests.common.TmfXmlTestFiles;
import org.eclipse.tracecompass.tmf.analysis.xml.core.tests.module.XmlUtilsTest;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.junit.Test;

import com.google.common.collect.ImmutableMap;

/**
 * Test the XML pattern segment
 *
 * @author Jean-Christian Kouame
 * @author Geneviève Bastien
 */
public class XmlSegmentTest {

    private static final @NonNull String TEST_TRACE5 = "test_traces/testTrace5.xml";
    private static final @NonNull String TEST_TRACE2 = "test_traces/testTrace2.xml";

    // Data for segment tests
    private static final @NonNull String NAME_FIELD_1 = "field1";
    private static final @NonNull String NAME_FIELD_2 = "field2";
    private static final @NonNull String NAME_FIELD_3 = "field3";
    private static final long SEGMENT_START = 1L;
    private static final long SEGMENT_TIME2 = 3L;
    private static final long SEGMENT_TIME3 = 5L;
    private static final long SEGMENT_TIME4 = 7L;
    /**
     * The content for the segment TEST_2
     */
    private static final @NonNull Map<@NonNull String, @NonNull Object> SEGMENT_CONTENT_1 = ImmutableMap
            .of(NAME_FIELD_1, 100l,
                    NAME_FIELD_2, "test",
                    NAME_FIELD_3, 1);
    private static final @NonNull Map<@NonNull String, @NonNull Object> SEGMENT_CONTENT_2 = ImmutableMap
            .of(NAME_FIELD_1, 200l,
                    NAME_FIELD_2, "test",
                    NAME_FIELD_3, 1);

    /** The 4 segments of the segment content test */
    private static final @NonNull TmfXmlPatternSegment SEGMENT_1 = new TmfXmlPatternSegment(SEGMENT_START, SEGMENT_START, "test1", Collections.emptyMap());
    private static final @NonNull TmfXmlPatternSegment SEGMENT_2 = new TmfXmlPatternSegment(SEGMENT_START, SEGMENT_TIME2, "test2", SEGMENT_CONTENT_1);
    private static final @NonNull TmfXmlPatternSegment SEGMENT_3 = new TmfXmlPatternSegment(SEGMENT_START, SEGMENT_TIME3, "test1", Collections.emptyMap());
    private static final @NonNull TmfXmlPatternSegment SEGMENT_4 = new TmfXmlPatternSegment(SEGMENT_START, SEGMENT_TIME4, "test2", SEGMENT_CONTENT_2);

    /** Segment generated by the mapping test */
    private static final @NonNull TmfXmlPatternSegment MAPPED_SEGMENT = new TmfXmlPatternSegment(SEGMENT_START, SEGMENT_START, "open", Collections.emptyMap());

    private static @NonNull ITmfTrace getTrace(String traceFile) {
        return XmlUtilsTest.initializeTrace(traceFile);
    }

    private static void deleteSupplementaryFiles(@NonNull ITmfTrace trace) {
        /*
         * Delete the supplementary files at the end of the benchmarks
         */
        File suppDir = new File(TmfTraceManager.getSupplementaryFileDir(trace));
        for (File file : suppDir.listFiles()) {
            file.delete();
        }
    }

    private @NonNull static XmlPatternAnalysis initializeModule(@NonNull ITmfTrace trace, TmfXmlTestFiles xmlTestFile) throws TmfAnalysisException {
        XmlPatternAnalysis module = XmlUtilsTest.initializePatternModule(xmlTestFile);

        module.setTrace(trace);

        module.schedule();
        module.waitForCompletion();

        return module;
    }

    /**
     * Test segment generated using a mapping group
     *
     * @throws TmfAnalysisException
     *             Exception thrown by analysis
     */
    @Test
    public void testMappingGroup() throws TmfAnalysisException {
        ITmfTrace trace = getTrace(TEST_TRACE5);
        XmlPatternAnalysis module = null;
        try {
            module = initializeModule(trace, TmfXmlTestFiles.CONSUMING_FSM_TEST);
            doTestMappingGroup(module);
        } finally {
            if (module != null) {
                module.dispose();
            }
            deleteSupplementaryFiles(trace);
            trace.dispose();
        }

    }

    private static void doTestMappingGroup(XmlPatternAnalysis module) {
        @Nullable
        ISegmentStore<@NonNull ISegment> ss = module.getSegmentStore();
        assertNotNull(ss);
        assertEquals("Segment store size", 1, ss.size());
        Object segment = ss.iterator().next();
        assertTrue(segment instanceof TmfXmlPatternSegment);
        XmlUtilsTest.testPatternSegmentData(MAPPED_SEGMENT, (TmfXmlPatternSegment) segment);
    }

    /**
     * Test segment generated using a mapping group
     *
     * @throws TmfAnalysisException
     *             Exception thrown by analysis
     */
    @Test
    public void testSegmentContent() throws TmfAnalysisException {
        ITmfTrace trace = getTrace(TEST_TRACE2);
        XmlPatternAnalysis module = null;
        try {
            module = initializeModule(trace, TmfXmlTestFiles.VALID_PATTERN_SEGMENT);
            doTestSegmentContent(module);

            // Close the segment store and open it again, it should read the
            // segment store from disk this time
            module.dispose();
            module = initializeModule(trace, TmfXmlTestFiles.VALID_PATTERN_SEGMENT);
            doTestSegmentContent(module);
        } finally {
            if (module != null) {
                module.dispose();
            }
            deleteSupplementaryFiles(trace);
            trace.dispose();
        }

    }

    private static void doTestSegmentContent(XmlPatternAnalysis module) {
        @Nullable
        ISegmentStore<@NonNull ISegment> ss = module.getSegmentStore();
        assertNotNull(ss);
        assertEquals("Segment store size", 4, ss.size());
        Iterator<@NonNull ISegment> iterator = ss.iterator();
        // first segment
        assertTrue(iterator.hasNext());
        ISegment segment1 = iterator.next();
        assertTrue(segment1 instanceof TmfXmlPatternSegment);
        XmlUtilsTest.testPatternSegmentData(SEGMENT_1, (TmfXmlPatternSegment) segment1);

        // second segment
        assertTrue(iterator.hasNext());
        ISegment segment2 = iterator.next();
        assertTrue(segment2 instanceof TmfXmlPatternSegment);
        XmlUtilsTest.testPatternSegmentData(SEGMENT_2, (TmfXmlPatternSegment) segment2);

        // third segment
        assertTrue(iterator.hasNext());
        ISegment segment3 = iterator.next();
        assertTrue(segment3 instanceof TmfXmlPatternSegment);
        XmlUtilsTest.testPatternSegmentData(SEGMENT_3, (TmfXmlPatternSegment) segment3);

        // fourth segment
        assertTrue(iterator.hasNext());
        ISegment segment4 = iterator.next();
        assertTrue(segment4 instanceof TmfXmlPatternSegment);
        XmlUtilsTest.testPatternSegmentData(SEGMENT_4, (TmfXmlPatternSegment) segment4);

        assertFalse(iterator.hasNext());

        // Compare segments
        // segment1 < segment3, comparison by string (end time)
        assertEquals(-1, segment1.compareTo(segment3));
        assertEquals(1, segment3.compareTo(segment1));

        // segment1 < segment2, comparison by name
        assertEquals(-1, segment1.compareTo(segment2));
        assertEquals(1, segment2.compareTo(segment1));
        assertEquals(0, segment2.compareTo(segment2));

        // segment2 < segment4, comparison by content
        assertEquals(-1, segment2.compareTo(segment4));
        assertEquals(1, segment4.compareTo(segment2));
    }

}
