# Copyright (c) 2013-2019, Manfred Moitzi
# License: MIT-License
import pytest

from ezdxf.queryparser import EntityQueryParser, ParseException, InfixBoolQuery


class TestEntityQueryParserWithoutAttributes:
    def test_without_wildcards(self):
        result = EntityQueryParser.parseString("LINE", parseAll=True)
        name = result.EntityQuery[0]
        assert "LINE" == name

    def test_two_entity_names(self):
        result = EntityQueryParser.parseString("LINE CIRCLE", parseAll=True)
        assert "LINE" == result.EntityQuery[0]
        assert "CIRCLE" == result.EntityQuery[1]

    def test_star_wildcard(self):
        result = EntityQueryParser.parseString("*", parseAll=True)
        name = result.EntityQuery[0]
        assert "*" == name

    def test_wrong_star_wildcard(self):
        with pytest.raises(ParseException):
            EntityQueryParser.parseString("LIN*", parseAll=True)

    def test_star_wildcard_2(self):
        result = EntityQueryParser.parseString("* !LINE", parseAll=True)
        assert result.EntityQuery[0] == "*"
        assert result.EntityQuery[1] == "!LINE"

    def test_star_wildcard_3(self):
        with pytest.raises(ParseException):
            EntityQueryParser.parseString("!LINE *", parseAll=True)

    def test_star_wildcard_4(self):
        with pytest.raises(ParseException):
            EntityQueryParser.parseString("* LINE", parseAll=True)


class TestEntityQueryParserWithAttributes:
    def test_empty_attribute_list_not_allowed(self):
        with pytest.raises(ParseException):
            EntityQueryParser.parseString("LINE[]", parseAll=True)

    def test_one_attribute(self):
        result = EntityQueryParser.parseString(
            'LINE[layer=="0"]', parseAll=True
        )
        assert "LINE" == result.EntityQuery[0]
        assert ("layer", "==", "0") == tuple(result.AttribQuery)

    def test_double_quoted_attributes(self):
        result = EntityQueryParser.parseString(
            'LINE[layer=="0"]', parseAll=True
        )
        assert "LINE" == result.EntityQuery[0]
        assert ("layer", "==", "0") == tuple(result.AttribQuery)

    def test_single_quoted_attributes(self):
        result = EntityQueryParser.parseString(
            "LINE[layer=='0']", parseAll=True
        )
        assert "LINE" == result.EntityQuery[0]
        assert ("layer", "==", "0") == tuple(result.AttribQuery)

    def test_attribute_name_with_underscore(self):
        result = EntityQueryParser.parseString(
            "HATCH[solid_fill==0]", parseAll=True
        )
        assert "HATCH" == result.EntityQuery[0]
        assert ("solid_fill", "==", 0) == tuple(result.AttribQuery)

    def test_star_with_one_attribute(self):
        result = EntityQueryParser.parseString('*[layer=="0"]', parseAll=True)
        assert "*" == result.EntityQuery[0]
        assert 3 == len(result.AttribQuery)
        assert ("layer", "==", "0") == tuple(result.AttribQuery)

    def test_relation_lt(self):
        result = EntityQueryParser.parseString('*[layer<"0"]', parseAll=True)
        assert ("layer", "<", "0") == tuple(result.AttribQuery)

    def test_relation_le(self):
        result = EntityQueryParser.parseString('*[layer<="0"]', parseAll=True)
        assert ("layer", "<=", "0") == tuple(result.AttribQuery)

    def test_relation_eq(self):
        result = EntityQueryParser.parseString('*[layer=="0"]', parseAll=True)
        assert ("layer", "==", "0") == tuple(result.AttribQuery)

    def test_relation_ne(self):
        result = EntityQueryParser.parseString('*[layer!="0"]', parseAll=True)
        assert ("layer", "!=", "0") == tuple(result.AttribQuery)

    def test_relation_ge(self):
        result = EntityQueryParser.parseString('*[layer>="0"]', parseAll=True)
        assert ("layer", ">=", "0") == tuple(result.AttribQuery)

    def test_relation_gt(self):
        result = EntityQueryParser.parseString('*[layer>="0"]', parseAll=True)
        assert ("layer", ">=", "0") == tuple(result.AttribQuery)

    def test_regex_match(self):
        result = EntityQueryParser.parseString('*[layer?"0"]', parseAll=True)
        assert ("layer", "?", "0") == tuple(result.AttribQuery)

    def test_not_regex_match(self):
        result = EntityQueryParser.parseString('*[layer!?"0"]', parseAll=True)
        assert ("layer", "!?", "0") == tuple(result.AttribQuery)

    def test_appended_ignore_case_option(self):
        result = EntityQueryParser.parseString(
            '*[layer=="IgnoreCase"]i', parseAll=True
        )
        assert "i" == result.AttribQueryOptions


class TestInfixBoolQuery:
    def test_not_operation(self):
        result = InfixBoolQuery.parseString("!a!=1", parseAll=True)
        op, relation = result.AttribQuery
        assert "!" == op
        assert ("a", "!=", 1) == tuple(relation)

    def test_and_operation(self):
        result = InfixBoolQuery.parseString("a != 1 & b != 2", parseAll=True)
        rel1, op, rel2 = result.AttribQuery
        assert ("a", "!=", 1) == tuple(rel1)
        assert "&" == op
        assert ("b", "!=", 2) == tuple(rel2)

    def test_or_operation(self):
        result = InfixBoolQuery.parseString("a != 1 | b != 2", parseAll=True)
        rel1, op, rel2 = result.AttribQuery
        assert ("a", "!=", 1) == tuple(rel1)
        assert "|" == op
        assert ("b", "!=", 2) == tuple(rel2)

    def test_not_operation_with_brackets(self):
        result = InfixBoolQuery.parseString("!(a!=1)", parseAll=True)
        op, relation = result.AttribQuery
        assert "!" == op
        assert ("a", "!=", 1) == tuple(relation)

    def test_operation_with_brackets(self):
        result = InfixBoolQuery.parseString(
            "(a != 1) & (b != 2)", parseAll=True
        )
        rel1, op, rel2 = result.AttribQuery
        assert ("a", "!=", 1) == tuple(rel1)
        assert "&" == op
        assert ("b", "!=", 2) == tuple(rel2)

    def test_operation_with_nested_brackets(self):
        result = InfixBoolQuery.parseString(
            "((a != 1) & (b != 2))", parseAll=True
        )
        rel1, op, rel2 = result.AttribQuery
        assert ("a", "!=", 1) == tuple(rel1)
        assert "&" == op
        assert ("b", "!=", 2) == tuple(rel2)
