import numpy as np
import pytest

from pandas._libs.byteswap import (
    read_double_with_byteswap,
    read_float_with_byteswap,
    read_uint16_with_byteswap,
    read_uint32_with_byteswap,
    read_uint64_with_byteswap,
)

import pandas._testing as tm


@pytest.mark.parametrize("read_offset", [0, 1, 7, 11])
@pytest.mark.parametrize(
    "int_type,number",
    [
        (np.uint16, 0),
        (np.uint16, 4660),
        (np.uint16, 2**16 - 1),
        (np.uint32, 0),
        (np.uint32, 3735928559),
        (np.uint32, 2**32 - 1),
        (np.uint64, 0),
        (np.uint64, 81985529216486895),
        (np.uint64, 2**64 - 1),
    ],
)
@pytest.mark.parametrize("should_byteswap", [True, False])
def test_int_byteswap(read_offset, number, int_type, should_byteswap):
    _test(number, int_type, read_offset, should_byteswap)


@pytest.mark.parametrize("read_offset", [0, 1, 7, 11])
@pytest.mark.parametrize(
    "number",
    [
        0.0,
        -0.0,
        1.0,
        -1.0,
        float("inf"),
        float("-inf"),
        float("nan"),
        1e-38,
        3.14159,
    ],
)
@pytest.mark.parametrize("float_type", [np.float32, np.float64])
@pytest.mark.parametrize("should_byteswap", [True, False])
def test_float_byteswap(read_offset, number, float_type, should_byteswap):
    _test(number, float_type, read_offset, should_byteswap)


def _test(number, number_type, read_offset, should_byteswap):
    number = number_type(number)
    data = np.random.default_rng(2).integers(0, 256, size=20, dtype="uint8")
    data[read_offset : read_offset + number.itemsize] = number[None].view("uint8")
    swap_func = {
        np.float32: read_float_with_byteswap,
        np.float64: read_double_with_byteswap,
        np.uint16: read_uint16_with_byteswap,
        np.uint32: read_uint32_with_byteswap,
        np.uint64: read_uint64_with_byteswap,
    }[type(number)]
    output_number = number_type(swap_func(bytes(data), read_offset, should_byteswap))
    if should_byteswap:
        tm.assert_equal(output_number, number.byteswap())
    else:
        tm.assert_equal(output_number, number)
