//
//  ========================================================================
//  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.test.support;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.junit.Assert;
import org.junit.Ignore;

/**
 * Allows for setting up a Jetty server for testing based on XML configuration files.
 */
@Ignore
public class TestableJettyServer
{
    private List<URL> _xmlConfigurations;
    private final Map<String,String> _properties = new HashMap<String, String>();
    private Server _server;
    private int _serverPort;
    private String _scheme = HttpScheme.HTTP.asString();

    /* Popular Directories */
    private File baseDir;
    private File testResourcesDir;

    public TestableJettyServer() throws IOException
    {
        _xmlConfigurations = new ArrayList<URL>();
        Properties properties = new Properties();

        /* Establish Popular Directories */
        String baseDirPath = System.getProperty("basedir");
        if (baseDirPath == null)
        {
            baseDirPath = System.getProperty("user.dir",".");
        }
        baseDir = new File(baseDirPath);
        properties.setProperty("test.basedir",baseDir.getAbsolutePath());

        testResourcesDir = new File(baseDirPath,"src/test/resources".replace('/',File.separatorChar));
        properties.setProperty("test.resourcesdir",testResourcesDir.getAbsolutePath());

        File testDocRoot = new File(testResourcesDir,"docroots");
        properties.setProperty("test.docroot.base",testDocRoot.getAbsolutePath());

        File targetDir = new File(baseDir,"target");
        properties.setProperty("test.targetdir",targetDir.getAbsolutePath());

        File webappsDir = new File(targetDir,"webapps");
        properties.setProperty("test.webapps",webappsDir.getAbsolutePath());

        // Write out configuration for use by ConfigurationManager.
        File testConfig = new File(targetDir,"testable-jetty-server-config.properties");
        FileOutputStream out = new FileOutputStream(testConfig);
        properties.store(out,"Generated by " + TestableJettyServer.class.getName());
        
        for (Object key:properties.keySet())
            _properties.put(String.valueOf(key),String.valueOf(properties.get(key)));
    }

    public void addConfiguration(URL xmlConfig)
    {
        _xmlConfigurations.add(xmlConfig);
    }

    public void addConfiguration(File xmlConfigFile) throws MalformedURLException
    {
        _xmlConfigurations.add(xmlConfigFile.toURI().toURL());
    }

    public void addConfiguration(String testConfigName) throws MalformedURLException
    {
        addConfiguration(new File(testResourcesDir,testConfigName));
    }

    public void setProperty(String key, String value)
    {
        _properties.put(key,value);
    }

    public void load() throws Exception
    {
        XmlConfiguration last = null;
        Object[] obj = new Object[this._xmlConfigurations.size()];

        // Configure everything
        for (int i = 0; i < this._xmlConfigurations.size(); i++)
        {
            URL configURL = this._xmlConfigurations.get(i);
            XmlConfiguration configuration = new XmlConfiguration(configURL);
            if (last != null)
            {
                configuration.getIdMap().putAll(last.getIdMap());
            }
            configuration.getProperties().putAll(_properties);
            obj[i] = configuration.configure();
            last = configuration;
        }

        // Test for Server Instance.
        Server foundServer = null;
        int serverCount = 0;
        for (int i = 0; i < this._xmlConfigurations.size(); i++)
        {
            if (obj[i] instanceof Server)
            {
                if (obj[i].equals(foundServer))
                {
                    // Identical server instance found
                    break;
                }
                foundServer = (Server)obj[i];
                serverCount++;
            }
        }

        if (serverCount <= 0)
        {
            throw new Exception("Load failed to configure a " + Server.class.getName());
        }

        Assert.assertEquals("Server load count",1,serverCount);

        this._server = foundServer;
        this._server.setStopTimeout(1000);
        
    }

    public String getScheme()
    {
        return _scheme;
    }

    public void setScheme(String scheme)
    {
        this._scheme = scheme;
    }

    public void start() throws Exception
    {
        Assert.assertNotNull("Server should not be null (failed load?)",_server);

        _server.start();

        // Find the active server port.
        this._serverPort = (-1);
        Connector connectors[] = _server.getConnectors();
        for (int i = 0; i < connectors.length; i++)
        {
            NetworkConnector connector = (NetworkConnector)connectors[i];
            if (connector.getLocalPort() > 0)
            {
                this._serverPort = connector.getLocalPort();
                break;
            }
        }

        Assert.assertTrue("Server Port is between 1 and 65535. Actually <" + _serverPort + ">",(1 <= this._serverPort) && (this._serverPort <= 65535));
    }

    public int getServerPort()
    {
        return _serverPort;
    }

    public void stop() throws Exception
    {
        _server.stop();
    }

    public URI getServerURI() throws UnknownHostException
    {
        StringBuffer uri = new StringBuffer();
        uri.append(this._scheme).append("://");
        uri.append(InetAddress.getLocalHost().getHostAddress());
        uri.append(":").append(this._serverPort);
        return URI.create(uri.toString());
    }
}
