import unittest

from Aspidites._vendor.contracts.docstring_parsing import DocStringInfo, Arg, number_of_spaces
from Aspidites._vendor.contracts.interface import add_prefix


examples = {"""
        Provides a RGB representation of the values by interpolating the range
        [min(value),max(value)] into the colorspace [min_color, max_color].

        :param value: The field to represent.
        :type  value: HxW array

        :param max_value: If specified, everything *above* is clipped.
        :type max_value: float
        :param min_value: If specified, everything *below* is clipped.
        :type min_value: float

        :param min_color:  Color to give to the minimum values.


        :return: A RGB image.
        :rtype: HxWx3 uint8

        :return: gray

    """: DocStringInfo(docstring='\n        Provides a RGB representation of the values by interpolating the range\n'
                         '        [min(value),max(value)] into the colorspace [min_color, max_color].\n',
  params={
        'value': Arg('The field to represent.', 'HxW array'),
        'max_value': Arg('If specified, everything *above* is clipped.', 'float'),
        'min_value': Arg('If specified, everything *below* is clipped.', 'float'),
        'min_color': Arg('Color to give to the minimum values.', None),
  },
  returns=[Arg('A RGB image.', "HxWx3 uint8"), Arg('gray', None)]
)
}


class DocStringTest(unittest.TestCase):

    def test_parsing(self):
        for string in examples:
            parsed = DocStringInfo.parse(string)
            "%s" % parsed
            "%r" % parsed
            result = examples[string]
            self.assertEqual(result, parsed)

    def test_number_of_spaces(self):
        self.assertEqual(number_of_spaces(''), 0)
        self.assertEqual(number_of_spaces(' '), 1)
        self.assertEqual(number_of_spaces('  '), 2)
        self.assertEqual(number_of_spaces('11'), 0)
        self.assertEqual(number_of_spaces(' 223'), 1)
        self.assertEqual(number_of_spaces('  4343'), 2)

    def test_reparsing(self):
        for string, result in examples.items(): #@UnusedVariable
            parsed = DocStringInfo.parse(string)
            converted = "%s" % parsed
            reparsed = DocStringInfo.parse(converted)

            msg = ('First string:\n%s\nParsed as:\n%s\n' %
                (add_prefix(string, '|'), add_prefix('%r' % parsed, '|')))

            msg += ('Converted:\n%s\nReparsed as:\n%s\n' %
                (add_prefix(converted, '|'), add_prefix('%r' % reparsed, '|')))

            self.assertEqual(parsed, reparsed, msg=msg)

    def test_inline_params(self):
        def test_inline_parsing(docstring, expected_type="type",
                                expected_desc="desc"):
            info = DocStringInfo.parse(docstring)
            self.assertTrue("name" in info.params)
            self.assertEqual(info.params["name"].type, expected_type)
            self.assertEqual(info.params["name"].desc, expected_desc)

        # Proper syntax
        test_inline_parsing(":param type name: desc")
        test_inline_parsing(":param name: desc", None)
        test_inline_parsing(":param name:", None, None)

        # Weird syntax for people who like to break things.
        test_inline_parsing(" : param type name : desc ")
        test_inline_parsing(" : param  name : desc ", None)
        test_inline_parsing(" : param  name : ", None, None)
        test_inline_parsing(" : param type , > 0  name : ", "type , > 0", None)

    def test_inline_returns(self):
        def test_inline_parsing(docstring, expected_type="type",
                                expected_desc="desc"):
            info = DocStringInfo.parse(docstring)
            self.assertTrue(len(info.returns) > 0)
            self.assertEqual(info.returns[0].type, expected_type)
            self.assertEqual(info.returns[0].desc, expected_desc)

        # Proper syntax
        test_inline_parsing(":returns type: desc")
        test_inline_parsing(":returns: desc", None)
        test_inline_parsing(":returns:", None, None)

        # Weird syntax for people who like to break things.
        test_inline_parsing(" : returns type : desc ")
        test_inline_parsing(" : returns : desc ", None)
        test_inline_parsing(" : returns : ", None, None)
        test_inline_parsing(" : returns type , > 0 : ", "type , > 0", None)
