//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
//
//   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 de.ugoe.cs.autoquest.http;

import static org.junit.Assert.*;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang.SerializationUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.plugin.http.HTTPLogParser;
import de.ugoe.cs.autoquest.plugin.http.HTTPUtils;
import de.ugoe.cs.autoquest.plugin.http.eventcore.HTTPEventType;
import de.ugoe.cs.autoquest.plugin.http.eventcore.HTTPTarget;
import de.ugoe.cs.autoquest.plugin.http.eventcore.SOAPEventType;
import de.ugoe.cs.autoquest.plugin.http.logdata.ObjectFactory;
import de.ugoe.cs.autoquest.plugin.http.logdata.Session;
import de.ugoe.cs.util.console.TextConsole;

/**
 * Test for the new HTMLLogParser
 * 
 * @author Fabian Glaser
 * 
 */
public class HTTPLogParserTest {

    /**
     * 
     */
    private final static String LOG_FILE_DIR = "target/tmp/logfiles/";

    /**
     *
     */
    @Before
    public void setUp() {
        new TextConsole(Level.FINEST);
    }

    /**
     *
     */
    @After
    public void tearDown() throws Exception {
        deleteFiles(new File(LOG_FILE_DIR));
    }

    /**
     * Tests the parseFile method with a given trace file.
     * 
     */
    @Test
    public void testParseFile_1() throws Exception {
        for (int i = 0; i < 20; i++) {
            File logFile = new File(LOG_FILE_DIR, "session" + i + ".xml");

            Session session = HTTPTestUtils.createRandomSession(100);
            writeToFile(session, logFile);

            System.out.println("calling parser");
            HTTPLogParser parser = new HTTPLogParser();
            parser.parseFile(logFile);

            System.out.println("evaluating events");
            Collection<List<Event>> events = parser.getSequences();

            assertNotNull(events);

            if (session.getHttpExchange().size() > 0) {
                assertEquals(1, events.size());

                Iterator<List<Event>> iterator = events.iterator();
                assertNotNull(iterator);
                List<Event> sequence = iterator.next();
                assertEquals(session.getHttpExchange().size(), sequence.size());
                assertFalse(iterator.hasNext());

                System.out.println("{");
                System.out.println("  {");
                for (int j = 0; j < sequence.size(); j++) {
                    System.out.print("    ");
                    System.out.print(sequence.get(j));
                    System.out.println(",");

                    assertNotNull(sequence.get(j));
                    assertNotNull(sequence.get(j).getType());
                    assertTrue(sequence.get(j).getType() instanceof HTTPEventType);

                    assertNotNull(sequence.get(j).getTarget());
                    assertTrue(sequence.get(j).getTarget() instanceof HTTPTarget);

                    HTTPTestUtils.assertExchangeEquals(session.getHttpExchange().get(j),
                                                       ((HTTPEventType) sequence.get(j).getType())
                                                           .getExchange());

                    assertEquals(HTTPUtils.toString(session.getHttpExchange().get(j).getReceiver()),
                                 ((HTTPTarget) sequence.get(j).getTarget()).getStringIdentifier());
                }
                System.out.println("  }");
                System.out.println("}");
                System.out.println("\n\n");
            }
        }
    }

    /**
     * Tests the parseFile method with a given trace file.
     * 
     */
    @Test
    public void testParseFile_2() throws Exception {
        HTTPLogParser parser = new HTTPLogParser();
        parser.parseFile(new File(ClassLoader.getSystemResource("httpmonitor_testtrace_1.xml")
            .getFile()));
        Collection<List<Event>> events = parser.getSequences();

        assertNotNull(events);
        assertEquals(1, events.size());

        Iterator<List<Event>> iterator = events.iterator();
        assertNotNull(iterator);
        assertEquals(876, iterator.next().size());
        assertFalse(iterator.hasNext());

        List<Event> soapEvents = new LinkedList<Event>();
        System.out.println("{");
        for (List<Event> session : events) {
            System.out.println("  {");
            for (Event event : session) {
                System.out.print("    ");
                System.out.print(event);
                System.out.println(",");

                if (event.getType() instanceof SOAPEventType) {
                    assertNotNull(((SOAPEventType) event.getType()).getCalledMethod());
                    soapEvents.add(event);
                }
            }
            System.out.println("  }");
        }
        System.out.println("}");
        System.out.println("\n\n");

        assertEquals(870, soapEvents.size());
    }

    /**
     * Tests the parseFile method with a given trace file.
     * 
     */
    @Test
    public void testParseFile_3() throws Exception {
        // tests parsing with properties
        HTTPLogParser parser =
            new HTTPLogParser(new File(ClassLoader
                .getSystemResource("testParseFile_3_properties.txt").getFile()));
        parser.parseFile(new File(ClassLoader.getSystemResource("testParseFile_3_logfile.log")
            .getFile()));
        Collection<List<Event>> events = parser.getSequences();

        assertNotNull(events);
        assertEquals(1, events.size());

        Iterator<List<Event>> iterator = events.iterator();
        assertNotNull(iterator);
        assertEquals(153, iterator.next().size());
        assertFalse(iterator.hasNext());

        List<Event> soapEvents = new LinkedList<Event>();
        System.out.println("{");
        for (List<Event> session : events) {
            System.out.println("  {");
            for (Event event : session) {
                System.out.print("    ");
                System.out.print(event);
                System.out.println(",");

                if (event.getType() instanceof SOAPEventType) {
                    assertNotNull(((SOAPEventType) event.getType()).getCalledMethod());
                    soapEvents.add(event);
                }
            }
            System.out.println("  }");
        }
        System.out.println("}");
        System.out.println("\n\n");

        assertEquals(147, soapEvents.size());
    }

    /**
     */
    @Test
    public void testSerializability_1() throws Exception {
        HTTPLogParser parser = new HTTPLogParser();
        parser.parseFile(new File(ClassLoader.getSystemResource("httpmonitor_testtrace_1.xml")
            .getFile()));
        Collection<List<Event>> events = parser.getSequences();

        assertNotNull(events);
        assertEquals(1, events.size());

        Iterator<List<Event>> iterator = events.iterator();
        assertNotNull(iterator);
        assertEquals(876, iterator.next().size());
        assertFalse(iterator.hasNext());

        for (List<Event> session : events) {
            for (Event event : session) {
                byte[] serializedEvent = SerializationUtils.serialize(event);
                assertEquals(event, SerializationUtils.deserialize(serializedEvent));
            }
        }
    }

    /**
     *
     */
    private void writeToFile(Session session, File logFile) throws Exception {
        System.out.println("writing session to " + logFile);

        logFile.getParentFile().mkdirs();

        JAXBContext jaxbContext = JAXBContext.newInstance(Session.class.getPackage().getName());
        Marshaller marshaller = jaxbContext.createMarshaller();
        StreamResult result = new StreamResult(new FileOutputStream(logFile));
        marshaller.marshal(new ObjectFactory().createSession(session), result);

        result.getOutputStream().close();
    }

    /**
     *
     */
    private void deleteFiles(File file) {
        if (file.exists()) {
            if (file.isDirectory()) {
                for (File child : file.listFiles()) {
                    deleteFiles(child);
                }
            }

            try {
                file.delete();
            }
            catch (Exception e) {
                // ignore and delete as much as possible
            }
        }
    }
}
