import gzip
import http.server
import threading
import unittest

import wfdb.io._url


class TestNetFiles(unittest.TestCase):
    """
    Test accessing remote files.
    """
    def test_requests(self):
        """
        Test reading a remote file using various APIs.

        This tests that we can create a file object using
        wfdb.io._url.openurl(), and tests that the object implements
        the standard Python API functions for a file of the
        appropriate type.

        Parameters
        ----------
        N/A

        Returns
        -------
        N/A

        """

        text_data = """
        BERNARDO: Who's there?
        FRANCISCO: Nay, answer me: stand, and unfold yourself.
        BERNARDO: Long live the king!
        FRANCISCO: Bernardo?
        BERNARDO: He.
        FRANCISCO: You come most carefully upon your hour.
        BERNARDO: 'Tis now struck twelve; get thee to bed, Francisco.
        """
        binary_data = text_data.encode()
        file_content = {'/foo.txt': binary_data}

        # Test all possible combinations of:
        #  - whether or not the server supports compression
        #  - whether or not the server supports random access
        #  - chosen buffering policy
        for allow_gzip in (False, True):
            for allow_range in (False, True):
                with DummyHTTPServer(file_content=file_content,
                                     allow_gzip=allow_gzip,
                                     allow_range=allow_range) as server:
                    url = server.url('/foo.txt')
                    for buffering in (-2, -1, 0, 20):
                        self._test_text(url, text_data, buffering)
                        self._test_binary(url, binary_data, buffering)

    def _test_text(self, url, content, buffering):
        """
        Test reading a URL using text-mode file APIs.

        Parameters
        ----------
        url : str
            URL of the remote resource.
        content : str
            Expected content of the resource.
        buffering : int
            Buffering policy for openurl().

        Returns
        -------
        N/A

        """
        # read(-1), readable(), seekable()
        with wfdb.io._url.openurl(url, 'r', buffering=buffering) as tf:
            self.assertTrue(tf.readable())
            self.assertTrue(tf.seekable())
            self.assertEqual(tf.read(), content)
            self.assertEqual(tf.read(), '')

        # read(10)
        with wfdb.io._url.openurl(url, 'r', buffering=buffering) as tf:
            result = ''
            while True:
                chunk = tf.read(10)
                result += chunk
                if len(chunk) < 10:
                    break
            self.assertEqual(result, content)

        # readline(), seek(), tell()
        with wfdb.io._url.openurl(url, 'r', buffering=buffering) as tf:
            result = ''
            while True:
                rpos = tf.tell()
                tf.seek(0)
                tf.seek(rpos)
                chunk = tf.readline()
                result += chunk
                if len(chunk) == 0:
                    break
            self.assertEqual(result, content)

    def _test_binary(self, url, content, buffering):
        """
        Test reading a URL using binary-mode file APIs.

        Parameters
        ----------
        url : str
            URL of the remote resource.
        content : bytes
            Expected content of the resource.
        buffering : int
            Buffering policy for openurl().

        Returns
        -------
        N/A

        """
        # read(-1), readable(), seekable()
        with wfdb.io._url.openurl(url, 'rb', buffering=buffering) as bf:
            self.assertTrue(bf.readable())
            self.assertTrue(bf.seekable())
            self.assertEqual(bf.read(), content)
            self.assertEqual(bf.read(), b'')
            self.assertEqual(bf.tell(), len(content))

        # read(10)
        with wfdb.io._url.openurl(url, 'rb', buffering=buffering) as bf:
            result = b''
            while True:
                chunk = bf.read(10)
                result += chunk
                if len(chunk) < 10:
                    break
            self.assertEqual(result, content)
            self.assertEqual(bf.tell(), len(content))

        # readline()
        with wfdb.io._url.openurl(url, 'rb', buffering=buffering) as bf:
            result = b''
            while True:
                chunk = bf.readline()
                result += chunk
                if len(chunk) == 0:
                    break
            self.assertEqual(result, content)
            self.assertEqual(bf.tell(), len(content))

        # read1(10), seek(), tell()
        with wfdb.io._url.openurl(url, 'rb', buffering=buffering) as bf:
            bf.seek(0, 2)
            self.assertEqual(bf.tell(), len(content))
            bf.seek(0)
            result = b''
            while True:
                rpos = bf.tell()
                bf.seek(0)
                bf.seek(rpos)
                chunk = bf.read1(10)
                result += chunk
                if len(chunk) == 0:
                    break
            self.assertEqual(result, content)
            self.assertEqual(bf.tell(), len(content))

        # readinto(bytearray(10))
        with wfdb.io._url.openurl(url, 'rb', buffering=buffering) as bf:
            result = b''
            chunk = bytearray(10)
            while True:
                count = bf.readinto(chunk)
                result += chunk[:count]
                if count < 10:
                    break
            self.assertEqual(result, content)
            self.assertEqual(bf.tell(), len(content))

        # readinto1(bytearray(10))
        with wfdb.io._url.openurl(url, 'rb', buffering=buffering) as bf:
            result = b''