//   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.httpmonitor;

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

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.logging.Level;

import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.After;
import org.junit.Before;

import de.ugoe.cs.autoquest.eventcore.Event;
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.logdata.HttpExchange;
import de.ugoe.cs.autoquest.plugin.http.logdata.Method;
import de.ugoe.cs.util.console.TextConsole;

/**
 *
 */
public abstract class AbstractTC {
    
    /**
     * 
     */
    protected static final TextConsole CONSOLE = new TextConsole(Level.FINEST);
    
    /**
     * 
     */
    protected final static String LOG_FILE_DIR = "target/tmp/logfiles/";
    
    /**
     * 
     */
    protected static final int DUMMY_SERVER_PORT = 19098;
    
    /**
     * 
     */
    protected static final int PROXY_PORT = 19099;
    
    /**
     * 
     */
    protected static final int MONITOR_PORT = 19100;
    
    /**
     * the servlet of the dummy web server responding to requests
     */
    private DummyServlet dummyServlet;
    
    /** */
    private static Tomcat tomcat = new Tomcat();
    
    /**
     * 
     */
    static String readStreamContentToString(InputStream stream) throws IOException {
        return readToString(new BufferedReader(new InputStreamReader(stream)));
    }
    
    /**
     * 
     */
    static String readToString(BufferedReader reader) throws IOException {
        StringBuffer message = new StringBuffer();
        
        String line = reader.readLine();
        while (line != null) {
            System.err.println(line);
            if (message.length() > 0) {
                message.append('\n');
            }
            message.append(line);
            line = reader.readLine();
        }
        
        return message.toString();
    }

    /**
     * 
     */
    @Before
    public void setUp() throws Exception {
        // setup a simple HTTP server
        dummyServlet = new DummyServlet();
        
        /*dummyServer = new Server(DUMMY_SERVER_PORT);
        
        ServletContextHandler root =
            new ServletContextHandler(dummyServer, "/dummyServer", ServletContextHandler.SESSIONS);

        
        root.addServlet(new ServletHolder(dummyServlet), "/*");
       
        dummyServer.start();*/
        
        File tomcatDir = new File("target/test/tomcat");
        File webappRootDir = new File(tomcatDir, "webapp");
        
        tomcat = new Tomcat();
        tomcat.setPort(DUMMY_SERVER_PORT);
        tomcat.setBaseDir(tomcatDir.getAbsolutePath());

        File warFile = new File(webappRootDir, "dummyService.war");
        tomcat.addWebapp("/dummyWebapp", warFile.getAbsolutePath());
        System.out.println("configuring Dummy Service from " + warFile.getAbsolutePath());
        
        File servletRootDir = new File(tomcatDir, "servlet");
        servletRootDir.mkdirs();
        Context ctx = tomcat.addContext("/", servletRootDir.getAbsolutePath());
        Tomcat.addServlet(ctx, "dummyServlet", dummyServlet);
        ctx.addServletMapping("/dummyServlet", "dummyServlet");
        
        tomcat.start();
        
        setUpHook();
    }

    /**
     *
     */
    protected abstract void setUpHook() throws Exception;

    /**
     *
     */
    @After
    public void tearDown() throws Exception {
        // give all started servers a second to finalize their internal work after the test
        // execution. This prevents some unnecessary exceptions due to requests already processed
        // by the test case but not finalized on server side.
        Thread.sleep(2000);
        tearDownHook();

        if (tomcat != null) {
            try {
                tomcat.stop();
                tomcat.getServer().await();
                tomcat.destroy();
            }
            finally {
                tomcat = null;
            }
        }
        
        deleteFiles(new File(LOG_FILE_DIR));
        
        // wait a little before the next server starts
        Thread.sleep(2000);
    }

    /**
     *
     */
    protected abstract void tearDownHook() throws Exception;

    /**
     *
     */
    protected String sendDummyMessage(String type, String query, String message, String respMsg)
        throws Exception
    {
        dummyServlet.setResponse(respMsg);
        
        System.err.println("sending message: " + message);
        DefaultHttpClient httpclient = new DefaultHttpClient();
        HttpRequestBase httpRequest = null;
        
        String uri = "http://localhost:" + PROXY_PORT + "/dummyServlet";
        
        if (query != null) {
            uri += "?" + query;
        }
        
        if ("POST".equals(type)) {
            httpRequest = new HttpPost(uri);
            HttpEntity entity = new StringEntity(message, ContentType.TEXT_PLAIN);
            ((HttpPost) httpRequest).setEntity(entity);
        }
        else if ("GET".equals(type)) {
            httpRequest = new HttpGet(uri);
        }
        
        try {
            HttpResponse response = httpclient.execute(httpRequest);
            
            assertEquals(message, dummyServlet.getRequest());
            assertEquals(query, dummyServlet.getQuery());
            System.err.println(response.getStatusLine());
            String responseStr = readStreamContentToString(response.getEntity().getContent());
            System.err.println("received response: " + responseStr);
            return responseStr;
        }
        finally {
            httpRequest.releaseConnection();
        }
    }
    
    /**
     *
     */
    protected void assertEvent(Event  event,
                               String method,
                               String query,
                               String message,
                               String response)
    {
        assertNotNull(event);
        assertNotNull(event.getType());
        assertNotNull(event.getTarget());
        
        assertTrue(event.getType() instanceof HTTPEventType);
        assertTrue(event.getTarget() instanceof HTTPTarget);
        
        assertExchange(((HTTPEventType) event.getType()).getExchange(),
                       method, query, message, response);
    }
    
    /**
     *
     */
    protected void assertExchange(HttpExchange exchange,
                                  String       method,
                                  String       query,
                                  String       message,
                                  String       response)
    {
        assertEquals(Method.fromValue(method), exchange.getRequest().getMethod());
        
        if (query != null) {
            assertEquals(query, exchange.getRequest().getQuery());
        }
        else if (exchange.getRequest().getQuery() != null) {
            System.err.println(exchange.getRequest().getQuery());
            fail("unexpected query");
        }
        
        if (message != null) {
            assertEquals(message, exchange.getRequest().getContent().getData());
        }
        else if (exchange.getRequest().getContent() != null) {
            System.err.println(exchange.getRequest().getContent().getData());
        }
        
        if (response != null) {
            assertEquals(response, exchange.getResponse().getContent().getData());
        }
        else if (exchange.getResponse().getContent() != null) {
            System.err.println(exchange.getResponse().getContent().getData());
        }
        
        assertNotNull(exchange.getRequest().getOrderingId());
        assertNotNull(exchange.getResponse().getOrderingId());
        assertTrue(exchange.getRequest().getOrderingId() < exchange.getResponse().getOrderingId());
    }

    /**
     *
     */
    protected 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
            }
        }
    }

}
