//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.http3.qpack.util;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Objects;

import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http3.qpack.Instruction;
import org.eclipse.jetty.http3.qpack.QpackEncoder;
import org.eclipse.jetty.http3.qpack.QpackException;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matcher;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class QpackTestUtil
{
    public static ByteBuffer toBuffer(Instruction... instructions)
    {
        ByteBufferPool bufferPool = ByteBufferPool.NON_POOLING;
        ByteBufferPool.Accumulator accumulator = new ByteBufferPool.Accumulator();
        for (Instruction instruction : instructions)
        {
            instruction.encode(bufferPool, accumulator);
        }
        ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(accumulator.getTotalLength()));
        BufferUtil.clearToFill(combinedBuffer);
        for (ByteBuffer buffer : accumulator.getByteBuffers())
        {
            combinedBuffer.put(buffer);
        }
        BufferUtil.flipToFlush(combinedBuffer, 0);
        return combinedBuffer;
    }

    public static Matcher<String> equalsHex(String expectedString)
    {
        expectedString = expectedString.replaceAll("\\s+", "");
        return org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase(expectedString);
    }

    public static ByteBuffer toBuffer(List<Instruction> instructions)
    {
        ByteBufferPool bufferPool = ByteBufferPool.NON_POOLING;
        ByteBufferPool.Accumulator accumulator = new ByteBufferPool.Accumulator();
        instructions.forEach(i -> i.encode(bufferPool, accumulator));
        assertThat(accumulator.getSize(), is(instructions.size()));
        ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(accumulator.getTotalLength()), false);
        BufferUtil.clearToFill(combinedBuffer);
        accumulator.getByteBuffers().forEach(combinedBuffer::put);
        BufferUtil.flipToFlush(combinedBuffer, 0);
        return combinedBuffer;
    }

    public static ByteBuffer hexToBuffer(String hexString)
    {
        hexString = hexString.replaceAll("\\s+", "");
        return ByteBuffer.wrap(StringUtil.fromHexString(hexString));
    }

    public static String toHexString(Instruction instruction)
    {
        return BufferUtil.toHexString(toBuffer(List.of(instruction)));
    }

    public static ByteBuffer encode(QpackEncoder encoder, long streamId, MetaData metaData) throws QpackException
    {
        ByteBuffer buffer = BufferUtil.allocate(1024);
        BufferUtil.clearToFill(buffer);
        encoder.encode(buffer, streamId, metaData);
        BufferUtil.flipToFlush(buffer, 0);
        return buffer;
    }

    public static HttpFields.Mutable toHttpFields(HttpField field)
    {
        return HttpFields.build().add(field);
    }

    public static MetaData toMetaData(String name, String value)
    {
        return toMetaData(toHttpFields(new HttpField(name, value)));
    }

    public static MetaData toMetaData(String method, String path, String scheme)
    {
        return toMetaData(method, path, scheme, (HttpField)null);
    }

    public static MetaData toMetaData(String method, String path, String scheme, HttpField... fields)
    {
        HttpFields.Mutable httpFields = HttpFields.build();
        for (HttpField field : fields)
        {
            httpFields.add(field);
        }

        return toMetaData(method, path, scheme, httpFields);
    }

    public static MetaData toMetaData(String method, String path, String scheme, HttpFields.Mutable fields)
    {
        HttpURI.Immutable uri = HttpURI.from(scheme, path);
        return new MetaData.Request(method, uri, HttpVersion.HTTP_3, fields);
    }

    public static MetaData toMetaData(HttpFields fields)
    {
        return new MetaData(HttpVersion.HTTP_3, fields);
    }

    public static boolean compareMetaData(MetaData m1, MetaData m2)
    {
        if (!Objects.equals(m1.getHttpVersion(), m2.getHttpVersion()))
            return false;
        if (!Objects.equals(m1.getContentLength(), m2.getContentLength()))
            return false;
        if (!Objects.equals(m1.getHttpFields(), m2.getHttpFields()))
            return false;
        return m1.getTrailersSupplier() == null && m2.getTrailersSupplier() == null;
    }
}
