/*
 * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

/*
 * $Id$
 */

package com.sun.ts.tests.servlet.common.client;

import com.sun.ts.tests.common.webclient.BaseUrlClient;
import com.sun.ts.tests.common.webclient.http.HttpRequest;
import com.sun.ts.tests.common.webclient.WebTestCase;
import com.sun.ts.tests.servlet.common.util.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;

/**
 * Base client for Servlet tests.
 */

public abstract class AbstractUrlClient extends BaseUrlClient {
    /**
     * The logger for this class.
     */
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * apitest property name
     */
    protected static final String APITEST = "apitest";
    /**
     * no servlet name
     */
    protected static final String DONOTUSEServletName = "NoServletName";

    private InetAddress[] _addrs = null;

    private String _servlet = null;

    /**
     * Constructor.
     */
    protected AbstractUrlClient() {
        // Construct a default context root based on the class name of
        // the concrete subclass of this class.
        String cname = this.getClass().getName();
        String prefix = "com.sun.ts.tests.";
        if (cname.startsWith(prefix))
            cname = cname.substring(prefix.length());
        String suffix = ".URLClient";
        if (cname.endsWith(suffix))
            cname = cname.substring(0, cname.length() - suffix.length());
        cname = cname.replace('.', '_');
        cname = "/" + cname + "_web";
        setContextRoot(cname);
    }

    protected void setTestProperties(WebTestCase testCase) {
        setStandardProperties(TEST_PROPS.getProperty(STANDARD), testCase);
        setApiTestProperties(TEST_PROPS.getProperty(APITEST), testCase);
        super.setTestProperties(testCase);
    }

    /**
     * Sets the request, testname, and a search string for test passed. A search is also added for test failure. If found,
     * the test will fail.
     *
     * @param testValue - a logical test identifier
     * @param testCase - the current test case
     */
    private void setApiTestProperties(String testValue, WebTestCase testCase) {
        if (testValue == null) {
            return;
        }

        // An API test consists of a request with a request parameter of
        // testname, a search string of Test PASSED, and a logical test name.

        // set the testname
        _testName = testValue;

        // set the request
        StringBuilder sb = new StringBuilder(50);
        if ((_servlet != null) && (TEST_PROPS.getProperty(DONOTUSEServletName) == null)) {
            sb.append(GET).append(_contextRoot).append(SL);
            sb.append(_servlet).append("?testname=").append(testValue);
            sb.append(HTTP11);
        } else {
            sb.append(GET).append(_contextRoot).append(SL);
            sb.append(testValue).append(HTTP10);
        }
        logger.debug("REQUEST LINE: {}", sb);

        HttpRequest req = new HttpRequest(sb.toString(), _hostname, _port);
        testCase.setRequest(req);

        if ((TEST_PROPS.getProperty(SEARCH_STRING) == null) || ((TEST_PROPS.getProperty(SEARCH_STRING)).equals(""))) {
            testCase.setResponseSearchString(Data.PASSED);
            testCase.setUnexpectedResponseSearchString(Data.FAILED);
        }

    }

    /**
     * Consists of a test name, a request, and a goldenfile.
     * 
     * @param testValue - a logical test identifier
     * @param testCase - the current test case
     */
    private void setStandardProperties(String testValue, WebTestCase testCase) {

        if (testValue == null) {
            return;
        }
        // A standard test sets consists of a testname
        // a request, and a goldenfile. The URI is not used
        // in this case since the JSP's are assumed to be located
        // at the top of the contextRoot
        StringBuffer sb = new StringBuffer(50);

        // set the testname
        _testName = testValue;

        // set the request
        // sb.append(GET).append(_contextRoot).append(SL);
        // sb.append(testValue).append(JSP_SUFFIX).append(HTTP10);
        // setRequest(sb.toString());
        // HttpRequest req = new HttpRequest(sb.toString(), _hostname, _port);
        // testCase.setRequest(req);

        if (_servlet != null) {
            sb.append(GET).append(_contextRoot).append(SL);
            sb.append(_servlet).append("?testname=").append(testValue);
            sb.append(HTTP11);
        } else {
            sb.append(GET).append(_contextRoot).append(SL);
            sb.append(testValue).append(HTTP10);
        }
        logger.debug("REQUEST LINE: {}", sb);
        HttpRequest req = new HttpRequest(sb.toString(), _hostname, _port);
        testCase.setRequest(req);
    }

    /**
     * Sets the name of the servlet to use when building a request for a single servlet API test.
     * 
     * @param servlet - the name of the servlet
     */
    protected void setServletName(String servlet) {
        _servlet = servlet;
    }

    /**
     * Gets the name of the servlet to use when building a request for a single servlet API test.
     *
     * @return the name of the servlet
     */
    protected String getServletName() {
        return _servlet;
    }

    /**
     * Returns a string containing the local interface information.
     *
     * @param returnAddresses if true, returns IP addresses; if false, returns hostnames
     * @return a string containing the local interface information
     */
    protected String getLocalInterfaceInfo(boolean returnAddresses) {
        String result = null;
        initInetAddress();
        if (_addrs.length != 0) {
            StringBuilder sb = new StringBuilder(32);
            if (!returnAddresses) {
                // localhost might not show up if aliased
                sb.append("localhost,");
            } else {
                // add 127.0.0.1
                sb.append("127.0.0.1,");
            }

            for (int i = 0; i < _addrs.length; i++) {
                if (returnAddresses) {
                    String ip = _addrs[i].getHostAddress();
                    if (!ip.equals("127.0.0.1")) {
                        if (ip.contains("%")) {
                            int scope_id = ip.indexOf("%");
                            ip = ip.substring(0, scope_id);
                        }
                        sb.append(ip);
                    }
                } else {
                    String host = _addrs[i].getCanonicalHostName();
                    if (!host.equals("localhost")) {
                        sb.append(host);
                    }
                }
                if (i + 1 != _addrs.length) {
                    sb.append(",");
                }
            }
            result = sb.toString();
            logger.trace("[AbstractUrlClient] Interface info: {}", result);
        }
        return result;
    }

    private void initInetAddress() {
        if (_addrs == null) {
            try {
                _addrs = InetAddress.getAllByName(InetAddress.getLocalHost().getCanonicalHostName());
            } catch (UnknownHostException uhe) {
                logger.info("[AbstractUrlClient][WARNING] Unable to obtain local host information.");
            }
        }
    }

    /**
     * Returns the request string.
     *
     * @param rq - the request string
     * @return the request string
     */
    protected String getRequest(String rq) {
        return rq;
    }

    /**
     * Builds a url string.
     * 
     * @param protocol - the protocol
     * @param hostname - the hostname
     * @param portnum - the port number
     * @param sContext - the context
     * @return the url string
     */
    protected String getURLString(String protocol, String hostname, int portnum, String sContext) {
        return protocol + "://" + hostname + ":" + portnum + "/" + sContext;
    }

    /**
     * Builds a URL.
     * 
     * @param protocol - the protocol
     * @param hostname - the hostname
     * @param portnum - the port number
     * @param sContext - the context
     * @return the url string
     * @throws MalformedURLException - if an error occurs
     */
    protected URL getURL(String protocol, String hostname, int portnum, String sContext) throws MalformedURLException {
        return new URL(protocol + "://" + hostname + ":" + portnum + "/" + sContext);
    }

    /**
     * Opens a URLConnection.
     * 
     * @param newURL - the URL
     * @return the URLConnection
     * @throws IOException - if an error occurs
     */
    public URLConnection getHttpsURLConnection(URL newURL) throws IOException {
        // open HttpsURLConnection using TSHttpsURLConnection
        URLConnection httpsURLConn = null;

        httpsURLConn = newURL.openConnection();
        if (httpsURLConn != null) {
            httpsURLConn.setDoInput(true);
            httpsURLConn.setDoOutput(true);
            httpsURLConn.setUseCaches(false);

        } else
            throw new IOException("Error opening httsURLConnection");

        return httpsURLConn;
    }

}
