# python-cc1101 - Python Library to Transmit RF Signals via CC1101 Transceivers
#
# Copyright (C) 2021 Fabian Peter Hammerle <fabian@hammerle.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

import pathlib
import re
import unittest.mock

import pytest

import cc1101._gpio

# pylint: disable=protected-access


@pytest.mark.parametrize(
    ("chip_selector", "chip_selector_formatted"),
    (
        (0, "/dev/gpiochip0"),
        (1, "/dev/gpiochip1"),
        ("/dev/gpiochip0", "/dev/gpiochip0"),
        (pathlib.Path("/dev/gpiochip1"), "/dev/gpiochip1"),
    ),
)
def test_get_line_permission_error(chip_selector, chip_selector_formatted):
    with unittest.mock.patch(
        "gpiod.chip",
        side_effect=PermissionError("[Errno 13] Permission denied: '/dev/gpiochip0'"),
    ):
        with pytest.raises(
            PermissionError,
            match=r"^Failed to access GPIO chip {}\.".format(
                re.escape(chip_selector_formatted)
            ),
        ):
            cc1101._gpio.get_line(chip_selector=chip_selector, line_name="GPIO24")


def test_get_line_file_not_found():
    with unittest.mock.patch(
        "gpiod.chip",
        side_effect=FileNotFoundError(
            "[Errno 2] No such file or directory: 'cannot open GPIO device /dev/gpiochip21'"
        ),
    ):
        with pytest.raises(
            FileNotFoundError, match=r"^Failed to find GPIO chip /dev/gpiochip21\."
        ):
            cc1101._gpio.get_line(chip_selector=21, line_name="GPIO24")


def test_get_line_cannot_open():
    with unittest.mock.patch(
        "gpiod.chip",
        side_effect=OSError("[Errno 0] Success: 'cannot open GPIO device 42'"),
    ):
        with pytest.raises(
            FileNotFoundError, match=r"^Failed to find GPIO chip /dev/gpiochip42\."
        ):
            cc1101._gpio.get_line(chip_selector=42, line_name="GPIO24")


def test_get_line_type_error():
    with unittest.mock.patch(
        "gpiod.chip",
        side_effect=TypeError("iter() returned non-iterator of type 'NoneType'"),
    ):
        with pytest.raises(
            FileNotFoundError, match=r"^Failed to find GPIO chip /dev/gpiochip815\."
        ):
            cc1101._gpio.get_line(chip_selector="/dev/gpiochip815", line_name="GPIO24")


class _LineMock:

    # pylint: disable=too-few-public-methods

    def __init__(self, holding: bool):
        self._holding = holding

    @property
    def name(self) -> str:
        if not self._holding:
            raise RuntimeError("object not holding a GPIO line handle")
        return "dummy"


@pytest.mark.parametrize("chip_selector", ("/dev/gpiochip0",))
@pytest.mark.parametrize("line_name", ("GPIO24", "GPIO25"))
def test_get_line(chip_selector, line_name):
    with unittest.mock.patch("gpiod.chip") as chip_mock:
        chip_mock().find_line.return_value = _LineMock(holding=True)
        chip_mock.reset_mock()
        assert isinstance(
            cc1101._gpio.get_line(chip_selector=chip_selector, line_name=line_name),
            _LineMock,
        )
        chip_mock.assert_called_once_with(chip_selector)
        chip_mock().find_line.assert_called_once_with(name=line_name)


@pytest.mark.parametrize("chip_selector", ("/dev/gpiochip0",))
@pytest.mark.parametrize("line_name", ("GPIO24", "GPIO25"))
def test_get_line_unknown_line(chip_selector, line_name):
    with unittest.mock.patch("gpiod.chip") as chip_mock:
        chip_mock().find_line.return_value = _LineMock(holding=False)
        with pytest.raises(
            ValueError,
            match=r"Failed to find GPIO line with name {}\.".format(
                re.escape(repr(line_name))
            ),
        ):
            cc1101._gpio.get_line(chip_selector=chip_selector, line_name=line_name)
