"""Testcases for cssutils.css.CSSMediaRule"""

# flake8: noqa

import xml.dom
from . import test_cssrule
import cssutils
import pytest


class TestCSSMediaRule(test_cssrule.TestCSSRule):
    def _setup_rule(self):
        self.r = cssutils.css.CSSMediaRule()
        self.rRO = cssutils.css.CSSMediaRule(readonly=True)
        self.r_type = cssutils.css.CSSMediaRule.MEDIA_RULE
        self.r_typeString = 'MEDIA_RULE'
        # for tests
        self.stylerule = cssutils.css.CSSStyleRule()
        self.stylerule.cssText = 'a {}'

    def test_init(self):
        "CSSMediaRule.__init__()"
        super().test_init()

        r = cssutils.css.CSSMediaRule()
        assert isinstance(r.cssRules, cssutils.css.CSSRuleList)
        assert [] == r.cssRules
        assert '' == r.cssText
        assert isinstance(r.media, cssutils.stylesheets.MediaList)
        assert 'all' == r.media.mediaText
        assert r.name is None

        r = cssutils.css.CSSMediaRule(mediaText='print', name='name')
        assert isinstance(r.cssRules, cssutils.css.CSSRuleList)
        assert [] == r.cssRules
        assert '' == r.cssText
        assert isinstance(r.media, cssutils.stylesheets.MediaList)
        assert 'print' == r.media.mediaText
        assert 'name' == r.name

        # only possible to set @... similar name
        with pytest.raises(xml.dom.InvalidModificationErr):
            self.r._setAtkeyword('x')

    def test_iter(self):
        "CSSMediaRule.__iter__()"
        m = cssutils.css.CSSMediaRule()
        m.cssText = '''@media all { /*1*/a { left: 0} b{ top:0} }'''
        types = [
            cssutils.css.CSSRule.COMMENT,
            cssutils.css.CSSRule.STYLE_RULE,
            cssutils.css.CSSRule.STYLE_RULE,
        ]
        for i, rule in enumerate(m):
            assert rule == m.cssRules[i]
            assert rule.type == types[i]
            assert rule.parentRule == m

    def test_refs(self):
        """CSSStylesheet references"""
        s = cssutils.parseString('@media all {a {color: red}}')
        r = s.cssRules[0]
        rules = r.cssRules
        assert r.cssRules[0].parentStyleSheet == s
        assert rules[0].parentStyleSheet == s

        # set cssText
        r.cssText = '@media all {a {color: blue}}'
        # not anymore: self.assertEqual(rules, r.cssRules)

        # set cssRules
        r.cssRules = cssutils.parseString(
            '''
            /**/
            @x;
            b {}').cssRules'''
        ).cssRules
        # new object
        assert rules != r.cssRules
        for i, sr in enumerate(r.cssRules):
            assert sr.parentStyleSheet == s
            assert sr.parentRule == r

    def test_cssRules(self):
        "CSSMediaRule.cssRules"
        r = cssutils.css.CSSMediaRule()
        assert [] == r.cssRules
        sr = cssutils.css.CSSStyleRule()
        r.cssRules.append(sr)
        assert [sr] == r.cssRules
        ir = cssutils.css.CSSImportRule()
        with pytest.raises(xml.dom.HierarchyRequestErr):
            r.cssRules.append(ir)

        s = cssutils.parseString('@media all { /*1*/a {x:1} }')
        m = s.cssRules[0]
        assert 2 == m.cssRules.length
        del m.cssRules[0]
        assert 1 == m.cssRules.length
        m.cssRules.append('/*2*/')
        assert 2 == m.cssRules.length
        m.cssRules.extend(cssutils.parseString('/*3*/x {y:2}').cssRules)
        assert 4 == m.cssRules.length
        assert (
            '@media all {\n    a {\n        x: 1\n        }\n    /*2*/\n    /*3*/\n    x {\n        y: 2\n        }\n    }'
            == m.cssText
        )

        for rule in m.cssRules:
            assert rule.parentStyleSheet == s
            assert rule.parentRule == m

    def test_cssText(self):
        "CSSMediaRule.cssText"
        style = '''{
    a {
        color: red
        }
    }'''

        mls = {
            ' (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx) ': None,
            ' tv ': None,
            ' only tv ': None,
            ' not tv ': None,
            ' only tv and (color) ': None,
            ' only tv and(color)': ' only tv and (color) ',
            ' only tv and (color: red) ': None,
            ' only tv and (color: red) and (width: 100px) ': None,
            ' only tv and (color: red) and (width: 100px), tv ': None,
            ' only tv and (color: red) and (width: 100px), tv and (width: 20px) ': None,
            ' only tv and(color :red)and(  width :100px  )  ,tv and(width: 20px) ': ' only tv and (color: red) and (width: 100px), tv and (width: 20px) ',
            ' (color: red) and (width: 100px), (width: 20px) ': None,
            ' /*1*/ only /*2*/ tv /*3*/ and /*4*/ (/*5*/ width) /*5*/ /*6*/, (color) and (height) ': None,
            '(color)and(width),(height)': ' (color) and (width), (height) ',
        }
        tests = {}
        for b, a in list(mls.items()):
            if a is None:
                a = b
            tests['@media{}{}'.format(b, style)] = '@media{}{}'.format(a, style)

        self.do_equal_p(tests)
        self.do_equal_r(tests)

        tests = {
            '@media only tv{}': '',
            '@media not tv{}': '',
            '@media only tv and (color){}': '',
            '@media only tv and (color: red){}': '',
            '@media only tv and (color: red) and (width: 100px){}': '',
            '@media only tv and (color: red) and (width: 100px), tv{}': '',
            '@media only tv and (color: red) and (width: 100px), tv and (width: 20px){}': '',
            '@media (color: red) and (width: 100px), (width: 20px){}': '',
            '@media (width){}': '',
            '@media (width:10px){}': '',
            '@media (width), (color){}': '',
            '@media (width)  ,  (color),(height){}': '',
            '@media (width)  ,  (color) and (height){}': '',
            '@media (width) and (color){}': '',
            '@media all and (width){}': '',
            '@media all and (width:10px){}': '',
            '@media all and (width), (color){}': '',
            '@media all and (width)  ,  (color),(height){}': '',
            '@media all and (width)  ,  (color) and (height){}': '',
            '@media all and (width) and (color){}': '',
            '@media only tv and (width){}': '',
            '@media only tv and (width:10px){}': '',
            '@media only tv and (width), (color){}': '',
            '@media only tv and (width)  ,  (color),(height){}': '',
            '@media only tv and (width)  ,  (color) and (height){}': '',
            '@media only tv and (width) and (color){}': '',
            '@media only tv and (width) "name" {}': '',
            '@media only tv and (width:10px) "name" {}': '',
            '@media only tv and (width), (color){}': '',
            '@media only tv and (width)  ,  (color),(height){}': '',
            '@media only tv and (width)  ,  (color) and (height){}': '',
            '@media only tv and (width) and (color){}': '',
            '@media all "name"{}': '',
            '@media all {}': '',
            '@media/*x*/all{}': '',
            '@media all { a{ x: 1} }': '@media all {\n    a {\n        x: 1\n        }\n    }',
            '@media all "name" { a{ x: 1} }': '@media all "name" {\n    a {\n        x: 1\n        }\n    }',
            '@MEDIA all { a{x:1} }': '@media all {\n    a {\n        x: 1\n        }\n    }',
            '@\\media all { a{x:1} }': '@media all {\n    a {\n        x: 1\n        }\n    }',
            '@media all {@x some;a{color: red;}b{color: green;}}': '''@media all {
    @x some;
    a {
        color: red
        }
    b {
        color: green
        }
    }''',
            '@media all { @x{}}': '@media all {\n    @x {\n        }\n    }',
            '@media all "n" /**/ { @x{}}': '@media all "n" /**/ {\n    @x {\n        }\n    }',
            # comments
            '@media/*1*//*2*/all/*3*//*4*/{/*5*/a{x:1}}': '@media /*1*/ /*2*/ all /*3*/ /*4*/ {\n    /*5*/\n    a {\n        x: 1\n        }\n    }',
            '@media  /*1*/  /*2*/  all  /*3*/  /*4*/  {  /*5*/  a{ x: 1} }': '@media /*1*/ /*2*/ all /*3*/ /*4*/ {\n    /*5*/\n    a {\n        x: 1\n        }\n    }',
            # WS
            '@media\n\t\f all\n\t\f {\n\t\f a{ x: 1}\n\t\f }': '@media all {\n    a {\n        x: 1\n        }\n    }',
            # @page rule inside @media
            '@media all { @page { margin: 0; } }': '@media all {\n    @page {\n        margin: 0\n        }\n    }',
            # nested media rules
            '@media all { @media all { p { color: red; } } }': '@media all {\n    @media all {\n        p {\n            '
            'color: red\n            }\n        }\n    }',
        }
        self.do_equal_p(tests)
        self.do_equal_r(tests)

        tests = {
            '@media {}': xml.dom.SyntaxErr,
            '@media;': xml.dom.SyntaxErr,
            '@media/*only comment*/{}': xml.dom.SyntaxErr,
            '@media all;': xml.dom.SyntaxErr,
            '@media all "n";': xml.dom.SyntaxErr,
            '@media all; @x{}': xml.dom.SyntaxErr,
            '@media { a{ x: 1} }': xml.dom.SyntaxErr,
            '@media "name" { a{ x: 1} }': xml.dom.SyntaxErr,
            '@media "name" all { a{ x: 1} }': xml.dom.SyntaxErr,
            '@media all { @charset "x"; a{}}': xml.dom.HierarchyRequestErr,
            '@media all { @import "x"; a{}}': xml.dom.HierarchyRequestErr,
            '@media all { , }': xml.dom.SyntaxErr,
            '@media all {}EXTRA': xml.dom.SyntaxErr,
            '@media ({}': xml.dom.SyntaxErr,
            '@media (color{}': xml.dom.SyntaxErr,
            '@media (color:{}': xml.dom.SyntaxErr,
            '@media (color:red{}': xml.dom.SyntaxErr,
            '@media (:red){}': xml.dom.SyntaxErr,
            '@media (:){}': xml.dom.SyntaxErr,
            '@media color:red){}': xml.dom.SyntaxErr,
        }
        self.do_raise_p(tests)
        self.do_raise_r(tests)

        tests = {
            # extra stuff
            '@media all { x{} } a{}': xml.dom.SyntaxErr,
        }
        self.do_raise_r(tests)

        m = cssutils.css.CSSMediaRule()
        m.cssText = '''@media all {@x; /*1*/a{color: red;}}'''
        for r in m.cssRules:
            assert m == r.parentRule
            assert m.parentStyleSheet == r.parentStyleSheet

        cssutils.ser.prefs.useDefaults()

    def test_media(self):
        "CSSMediaRule.media"
        # see CSSImportRule.media

        # setting not allowed
        with pytest.raises(AttributeError):
            self.r.__setattr__('media', None)
        with pytest.raises(AttributeError):
            self.r.__setattr__('media', 0)

        # set mediaText instead
        self.r.media.mediaText = 'print'
        self.r.insertRule(self.stylerule)
        assert '' == self.r.cssText
        cssutils.ser.prefs.keepEmptyRules = True
        assert '@media print {\n    a {}\n    }' == self.r.cssText
        cssutils.ser.prefs.useDefaults()

    def test_name(self):
        "CSSMediaRule.name"
        r = cssutils.css.CSSMediaRule()
        r.cssText = '@media all "\\n\\"ame" {a{left: 0}}'

        assert '\\n"ame' == r.name
        r.name = "n"
        assert 'n' == r.name
        assert (
            '@media all "n" {\n    a {\n        left: 0\n        }\n    }' == r.cssText
        )
        r.name = '"'
        assert '"' == r.name
        assert (
            '@media all "\\"" {\n    a {\n        left: 0\n        }\n    }'
            == r.cssText
        )

        r.name = ''
        assert r.name is None
        assert '@media all {\n    a {\n        left: 0\n        }\n    }' == r.cssText

        r.name = None
        assert r.name is None
        assert '@media all {\n    a {\n        left: 0\n        }\n    }' == r.cssText

        with pytest.raises(xml.dom.SyntaxErr):
            r._setName(0)
        with pytest.raises(xml.dom.SyntaxErr):
            r._setName(123)

    def test_deleteRuleIndex(self):
        "CSSMediaRule.deleteRule(index)"
        # see CSSStyleSheet.deleteRule
        m = cssutils.css.CSSMediaRule()
        m.cssText = '''@media all {
            @a;
            /* x */
            @b;
            @c;
            @d;
        }'''
        assert 5 == m.cssRules.length
        with pytest.raises(xml.dom.IndexSizeErr):
            m.deleteRule(5)

        # end -1
        # check parentRule
        r = m.cssRules[-1]
        assert m == r.parentRule
        m.deleteRule(-1)
        assert r.parentRule is None

        assert 4 == m.cssRules.length
        assert (
            '@media all {\n    @a;\n    /* x */\n    @b;\n    @c;\n    }' == m.cssText
        )
        # beginning
        m.deleteRule(0)
        assert 3 == m.cssRules.length
        assert '@media all {\n    /* x */\n    @b;\n    @c;\n    }' == m.cssText
        # middle
        m.deleteRule(1)
        assert 2 == m.cssRules.length
        assert '@media all {\n    /* x */\n    @c;\n    }' == m.cssText
        # end
        m.deleteRule(1)
        assert 1 == m.cssRules.length
        assert '@media all {\n    /* x */\n    }' == m.cssText

    def test_deleteRule(self):
        "CSSMediaRule.deleteRule(rule)"
        m = cssutils.css.CSSMediaRule()
        m.cssText = '''@media all {
            a { color: red; }
            b { color: blue; }
            c { color: green; }
        }'''
        s1, s2, s3 = m.cssRules

        r = cssutils.css.CSSStyleRule()
        with pytest.raises(xml.dom.IndexSizeErr):
            m.deleteRule(r)

        assert 3 == m.cssRules.length
        m.deleteRule(s2)
        assert 2 == m.cssRules.length
        assert (
            m.cssText
            == '@media all {\n    a {\n        color: red\n        }\n    c {\n        color: green\n        }\n    }'
        )
        with pytest.raises(xml.dom.IndexSizeErr):
            m.deleteRule(s2)

    def test_add(self):
        "CSSMediaRule.add()"
        # see CSSStyleSheet.add
        r = cssutils.css.CSSMediaRule()
        stylerule1 = cssutils.css.CSSStyleRule()
        stylerule2 = cssutils.css.CSSStyleRule()
        r.add(stylerule1)
        r.add(stylerule2)
        assert r.cssRules[0] == stylerule1
        assert r.cssRules[1] == stylerule2

    def test_insertRule(self):
        "CSSMediaRule.insertRule"
        # see CSSStyleSheet.insertRule
        r = cssutils.css.CSSMediaRule()
        charsetrule = cssutils.css.CSSCharsetRule('ascii')
        importrule = cssutils.css.CSSImportRule('x')
        namespacerule = cssutils.css.CSSNamespaceRule()
        unknownrule = cssutils.css.CSSUnknownRule('@x;')
        stylerule = cssutils.css.CSSStyleRule('a')
        stylerule.cssText = 'a { x: 1}'
        comment1 = cssutils.css.CSSComment('/*1*/')
        comment2 = cssutils.css.CSSComment('/*2*/')

        # hierarchy
        with pytest.raises(xml.dom.HierarchyRequestErr):
            r.insertRule(charsetrule, 0)
        with pytest.raises(xml.dom.HierarchyRequestErr):
            r.insertRule(importrule, 0)
        with pytest.raises(xml.dom.HierarchyRequestErr):
            r.insertRule(namespacerule, 0)

        # start insert
        r.insertRule(stylerule, 0)
        assert r == stylerule.parentRule
        assert r.parentStyleSheet == stylerule.parentStyleSheet
        # before
        r.insertRule(comment1, 0)
        assert r == comment1.parentRule
        assert r.parentStyleSheet == stylerule.parentStyleSheet
        # end explicit
        r.insertRule(unknownrule, 2)
        assert r == unknownrule.parentRule
        assert r.parentStyleSheet == stylerule.parentStyleSheet
        # end implicit
        r.insertRule(comment2)
        assert r == comment2.parentRule
        assert r.parentStyleSheet == stylerule.parentStyleSheet
        assert (
            '@media all {\n    /*1*/\n    a {\n        x: 1\n        }\n    @x;\n    /*2*/\n    }'
            == r.cssText
        )

        # index
        with pytest.raises(xml.dom.IndexSizeErr):
            r.insertRule(stylerule, -1)
        with pytest.raises(xml.dom.IndexSizeErr):
            r.insertRule(stylerule, r.cssRules.length + 1)

    def test_InvalidModificationErr(self):
        "CSSMediaRule.cssText InvalidModificationErr"
        self._test_InvalidModificationErr('@media')

    def test_incomplete(self):
        "CSSMediaRule (incomplete)"
        tests = {
            '@media all { @unknown;': '@media all {\n    @unknown;\n    }',  # no }
            '@media all { a {x:"1"}': '@media all {\n    a {\n        x: "1"\n        }\n    }',  # no }
            '@media all { a {x:"1"': '@media all {\n    a {\n        x: "1"\n        }\n    }',  # no }}
            '@media all { a {x:"1': '@media all {\n    a {\n        x: "1"\n        }\n    }',  # no "}}
        }
        self.do_equal_p(tests)  # parse

    def test_reprANDstr(self):
        "CSSMediaRule.__repr__(), .__str__()"
        mediaText = 'tv, print'

        s = cssutils.css.CSSMediaRule(mediaText=mediaText)

        assert mediaText in str(s)

        s2 = eval(repr(s))
        assert isinstance(s2, s.__class__)
        assert mediaText == s2.media.mediaText
