"""Unit tests for pyatv.dmap.parser."""

import unittest
import plistlib

from pyatv import exceptions
from pyatv.dmap import (tags, parser)

TEST_TAGS = {
    'uuu8': parser.DmapTag(tags.read_uint, 'uint8'),
    'uu16': parser.DmapTag(tags.read_uint, 'uint16'),
    'uu32': parser.DmapTag(tags.read_uint, 'uint32'),
    'uu64': parser.DmapTag(tags.read_uint, 'uint64'),
    'bola': parser.DmapTag(tags.read_bool, 'bool'),
    'bolb': parser.DmapTag(tags.read_bool, 'bool'),
    'stra': parser.DmapTag(tags.read_str, 'string'),
    'strb': parser.DmapTag(tags.read_str, 'string'),
    'cona': parser.DmapTag('container', 'container'),
    'conb': parser.DmapTag('container', 'container 2'),
    'igno': parser.DmapTag(tags.read_ignore, 'ignore'),
    'plst': parser.DmapTag(tags.read_bplist, 'bplist'),
}


def lookup_tag(name):
    return TEST_TAGS[name]


class ParserTest(unittest.TestCase):

    def test_parse_uint_of_various_lengths(self):
        in_data = tags.uint8_tag('uuu8', 12) + \
                  tags.uint16_tag('uu16', 37888) + \
                  tags.uint32_tag('uu32', 305419896) + \
                  tags.uint64_tag('uu64', 8982983289232)
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(4, len(parsed))
        self.assertEqual(12, parser.first(parsed, 'uuu8'))
        self.assertEqual(37888, parser.first(parsed, 'uu16'))
        self.assertEqual(305419896, parser.first(parsed, 'uu32'))
        self.assertEqual(8982983289232, parser.first(parsed, 'uu64'))

    def test_parse_bool(self):
        in_data = tags.bool_tag('bola', True) + \
                  tags.bool_tag('bolb', False)
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(2, len(parsed))
        self.assertTrue(parser.first(parsed, 'bola'))
        self.assertFalse(parser.first(parsed, 'bolb'))

    def test_parse_strings(self):
        in_data = tags.string_tag('stra', '') + \
                  tags.string_tag('strb', 'test string')
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(2, len(parsed))
        self.assertEqual('', parser.first(parsed, 'stra'))
        self.assertEqual('test string', parser.first(parsed, 'strb'))

    def test_parse_binary_plist(self):
        data = {"key": "value"}
        in_data = tags.raw_tag(
            'plst', plistlib.dumps(data, fmt=plistlib.FMT_BINARY))
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(1, len(parsed))
        self.assertEqual(data, parser.first(parsed, 'plst'))

    def test_parse_value_in_container(self):
        in_data = tags.container_tag('cona',
                                     tags.uint8_tag('uuu8', 36) +
                                     tags.uint16_tag('uu16', 13000))
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(1, len(parsed))
        inner = parser.first(parsed, 'cona')
        self.assertEqual(2, len(inner))
        self.assertEqual(36, parser.first(inner, 'uuu8'))
        self.assertEqual(13000, parser.first(inner, 'uu16'))

    def test_extract_simplified_container(self):
        elem = tags.uint8_tag('uuu8', 12)
        inner = tags.container_tag('conb', elem)
        in_data = tags.container_tag('cona', inner)
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(12, parser.first(parsed, 'cona', 'conb', 'uuu8'))

    def test_ignore_value(self):
        elem = tags.uint8_tag('igno', 44)
        parsed = parser.parse(elem, lookup_tag)
        self.assertEqual(parser.first(parsed, 'igno'), None)

    def test_simple_pprint(self):
        elem = tags.uint8_tag('uuu8', 12)
        inner = tags.container_tag('conb', elem)
        in_data = tags.container_tag('cona', inner)
        parsed = parser.parse(in_data, lookup_tag)
        self.assertEqual(parser.pprint(parsed, lookup_tag),
                         'cona: [container, container]\n' +
                         '  conb: [container, container 2]\n' +
                         '    uuu8: 12 [uint, uint8]\n')

    def test_print_invalid_input_raises_exception(self):
        with self.assertRaises(exceptions.InvalidDmapDataError):
            parser.pprint('bad data', lookup_tag)
