/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.tests.http

import io.ktor.http.*
import kotlin.test.*

class RangesTest {
    @Test
    fun testParseClosed() {
        assertEquals(RangesSpecifier(RangeUnits.Bytes, listOf(ContentRange.Bounded(0, 499))),
                parseRangesSpecifier("bytes=0-499"))
    }

    @Test
    fun testParseClosedSecond() {
        assertEquals(RangesSpecifier(RangeUnits.Bytes, listOf(ContentRange.Bounded(500, 999))),
                parseRangesSpecifier("bytes=500-999"))
    }

    @Test
    fun testParseFinalBytes() {
        assertEquals(RangesSpecifier(RangeUnits.Bytes, listOf(ContentRange.Suffix(500))),
                parseRangesSpecifier("bytes=-500"))
    }

    @Test
    fun testParseFirstAndLastByteOnly() {
        assertEquals(RangesSpecifier(RangeUnits.Bytes, listOf(
                ContentRange.Bounded(0, 0),
                ContentRange.Suffix(1)
        )),
                parseRangesSpecifier("bytes=0-0,-1"))
    }

    @Test
    fun testParseBytesFrom() {
        assertEquals(RangesSpecifier(RangeUnits.Bytes, listOf(ContentRange.TailFrom(9500))),
                parseRangesSpecifier("bytes=9500-"))
    }

    @Test
    fun testParseEmpty() {
        assertNull(parseRangesSpecifier(""))
    }

    @Test
    fun testParseWrongUnit() {
        assertNull(parseRangesSpecifier("miles=10-100"))
    }

    @Test
    fun testParseWrongNoValues() {
        assertNull(parseRangesSpecifier("bytes=-"))
    }

    @Test
    fun testParseWrongNegativeEnd() {
        assertNull(parseRangesSpecifier("bytes=--1"))
        assertNull(parseRangesSpecifier("bytes=1--1"))
    }

    @Test
    fun testParseStartGreaterThanEnd() {
        assertNull(parseRangesSpecifier("bytes=10-1"))
    }

    @Test
    fun testParseWrongNoUnit() {
        assertNull(parseRangesSpecifier("=10-1"))
        assertNull(parseRangesSpecifier("10-1"))
    }

    @Test
    fun testParseWrongNegativeStart() {
        assertNull(parseRangesSpecifier("bytes=-10-1"))
    }

    @Test
    fun testResolveRanges() {
        val ranges = listOf(ContentRange.Bounded(0, 10),
                ContentRange.TailFrom(11),
                ContentRange.Suffix(5))

        assertEquals(
                listOf(0..10,
                        11..14,
                        10..14),
                ranges.toLongRanges(15)
        )
    }

    @Test
    fun testMergeRanges() {
        val ranges = longRanges(0 .. 10, 12 .. 13)
        assertEquals(longRanges(0 .. 10, 12 .. 13), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeRangesReverse() {
        val ranges = longRanges(12 .. 13, 0 .. 10)
        assertEquals(longRanges(12 .. 13, 0 .. 10), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeRangesJustOneByte() {
        val ranges = longRanges(500 .. 600, 601 .. 999)
        assertEquals(longRanges(500 .. 999), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeRangesIntersection() {
        val ranges = longRanges(500 .. 700, 601 .. 999)
        assertEquals(longRanges(500 .. 999), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeRangesIntersectionOneInsideAnother() {
        val ranges = longRanges(0 .. 100, 10 .. 50)

        assertEquals(longRanges(0 .. 100), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeRangesSameStart() {
        val ranges = longRanges(0 .. 10, 0 .. 50)

        assertEquals(longRanges(0 .. 50), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeRangesMultiple() {
        val ranges = longRanges(100 .. 200, 10 .. 15, 80 .. 99)
        assertEquals(longRanges(80..200, 10..15), ranges.mergeRangesKeepOrder())
    }

    @Test
    fun testMergeToSingle() {
        assertEquals(0L..100L, RangesSpecifier(RangeUnits.Bytes, listOf(
                ContentRange.Bounded(0, 0),
                ContentRange.Bounded(90, 100),
                ContentRange.Bounded(5, 7)
        )).mergeToSingle(1000))
    }

    @Test
    fun testMergeRangeSpecifier() {
        assertEquals(listOf(0L..99L), RangesSpecifier(RangeUnits.Bytes, listOf(
                ContentRange.Bounded(0, 0),
                ContentRange.Bounded(1, 90),
                ContentRange.Suffix(10)
        )).merge(100))

        assertEquals(listOf(0L..99L), RangesSpecifier(RangeUnits.Bytes, listOf(
                ContentRange.Bounded(0, 0),
                ContentRange.Bounded(1, 90),
                ContentRange.TailFrom(90)
        )).merge(100))
    }

    @Test
    fun testRenderRanges() {
        assertEquals("bytes=0-0,1-,-1", RangesSpecifier(RangeUnits.Bytes, listOf(
                ContentRange.Bounded(0, 0),
                ContentRange.TailFrom(1),
                ContentRange.Suffix(1)
        )).toString())
    }

    @Test
    fun testPartialContentResponseRender() {
        assertEquals("bytes 0-0/1", contentRangeHeaderValue(0L..0L, 1L, RangeUnits.Bytes))
    }

    @Test
    fun testPartialContentResponseRenderUnknownSize() {
        assertEquals("bytes 0-0/*", contentRangeHeaderValue(0L..0L, null, RangeUnits.Bytes))
    }

    @Test
    fun testPartialContentResponseRenderUnsatisfiableRange() {
        assertEquals("bytes */1", contentRangeHeaderValue(null, 1L, RangeUnits.Bytes))
    }

    @Test
    fun testPartialContentResponseRenderUnsatisfiableRangeUnknownSize() {
        assertEquals("bytes */*", contentRangeHeaderValue(null, null, RangeUnits.Bytes))
    }

    private fun assertEquals(expected: List<IntRange>, actual: List<LongRange>) {
        assertEquals(expected.map { it.toLong() }, actual)
    }

    private fun longRanges(vararg ranges: IntRange): List<LongRange> = ranges.map { it.toLong() }

    private fun IntRange.toLong() = start.toLong()..endInclusive.toLong()
}
