/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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 org.apache.coyote.http2;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.junit.Assert;
import org.junit.Test;

import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.valves.ErrorReportValve;
import org.apache.tomcat.util.res.StringManager;

public class TestHttp2InitialConnection extends Http2TestBase {

    private TestData testData;


    @Test
    public void testValidHostHeader() throws Exception {
        List<String> hostHeaders = new ArrayList<>(1);
        hostHeaders.add("localhost:8080");

        testData = new TestData(hostHeaders, 200);

        http2Connect();
    }


    @Test
    public void testMultipleHostHeaders() throws Exception {
        List<String> hostHeaders = new ArrayList<>(1);
        hostHeaders.add("localhost:8080");
        hostHeaders.add("localhost:8081");

        testData = new TestData(hostHeaders, 400);

        http2Connect();
    }


    @Test
    public void testNoHostHeader() throws Exception {
        List<String> hostHeaders = new ArrayList<>(1);

        testData = new TestData(hostHeaders, 400);
        http2Connect();
    }


    @Override
    protected void doHttpUpgrade(String connection, String upgrade, String settings, boolean validate)
            throws IOException {
        StringBuilder request = new StringBuilder();
        request.append("GET /simple HTTP/1.1\r\n");
        for (String hostHeader : testData.getHostHeaders()) {
            request.append("Host: ");
            request.append(hostHeader);
            request.append("\r\n");
        }
        // Connection
        request.append("Connection: ");
        request.append(connection);
        request.append("\r\n");
        // Upgrade
        request.append("Upgrade: ");
        request.append(upgrade);
        request.append("\r\n");
        // Settings
        request.append(settings);
        // Locale - Force the en Locale else the i18n on the error page changes
        // the size of the response body and that triggers a failure as the test
        // checks the exact response length
        request.append("Accept-Language: en\r\n");
        // Request terminator
        request.append("\r\n");

        byte[] upgradeRequest = request.toString().getBytes(StandardCharsets.ISO_8859_1);
        os.write(upgradeRequest);
        os.flush();

        if (validate) {
            Assert.assertTrue("Failed to read HTTP Upgrade response", readHttpUpgradeResponse());
        }
    }


    @Override
    protected String getResponseBodyFrameTrace(int streamId, String body) {
        if (testData.getExpectedStatus() == 200) {
            return super.getResponseBodyFrameTrace(streamId, body);
        } else if (testData.getExpectedStatus() == 400) {
            /*
             * Need to be careful here. The test wants the exact content length in bytes. This will vary depending on
             * where the test is run due to: - The length of the version string that appears once in the error page -
             * The status header uses a UTF-8 EN dash. When running in an IDE the UTF-8 properties files will be used
             * directly rather than after native2ascii conversion.
             *
             * Note: The status header appears twice in the error page.
             */
            int serverInfoLength = ServerInfo.getServerInfo().getBytes().length;
            StringManager sm = StringManager.getManager(ErrorReportValve.class.getPackage().getName(), Locale.ENGLISH);
            String reason = sm.getString("http." + testData.getExpectedStatus() + ".reason");
            int descriptionLength = sm.getString("http." + testData.getExpectedStatus() + ".desc")
                    .getBytes(StandardCharsets.UTF_8).length;
            int statusHeaderLength = sm
                    .getString("errorReportValve.statusHeader", String.valueOf(testData.getExpectedStatus()), reason)
                    .getBytes(StandardCharsets.UTF_8).length;
            int typeLabelLength = sm.getString("errorReportValve.type").getBytes(StandardCharsets.UTF_8).length;
            int statusReportLabelLength = sm.getString("errorReportValve.statusReport")
                    .getBytes(StandardCharsets.UTF_8).length;
            int descriptionLabelLength = sm.getString("errorReportValve.description")
                    .getBytes(StandardCharsets.UTF_8).length;
            // 196 bytes is the static length of the pure HTML code from the ErrorReportValve
            int len = 196 + org.apache.catalina.util.TomcatCSS.TOMCAT_CSS.getBytes(StandardCharsets.UTF_8).length +
                    typeLabelLength + statusReportLabelLength + descriptionLabelLength + descriptionLength +
                    serverInfoLength + statusHeaderLength * 2;
            String contentLength = String.valueOf(len);
            return getResponseBodyFrameTrace(streamId, testData.getExpectedStatus(), "text/html;charset=utf-8", "en",
                    contentLength, contentLength);
        } else {
            Assert.fail();
            // To keep the IDE happy
            return null;
        }
    }


    private static class TestData {
        private final List<String> hostHeaders;
        private final int expectedStatus;

        TestData(List<String> hostHeaders, int expectedStatus) {
            this.hostHeaders = hostHeaders;
            this.expectedStatus = expectedStatus;
        }

        public List<String> getHostHeaders() {
            return hostHeaders;
        }

        public int getExpectedStatus() {
            return expectedStatus;
        }
    }
}
