import unittest
from parameterized import parameterized
from poc.util.fplutil import Utils
from poc.fplinterpreter import FplInterpreter
from tatsu.exceptions import FailedToken
from tatsu.exceptions import FailedParse
from tatsu.exceptions import FailedPattern
import os

"""
Tests of FPL related (parser) errors.
Note: FPL parser is autogenerated through the TatSu package. Therefore, we do not test any FPL syntax errors.
However, we make a positive test to ensure that all that is allowed by the FPL syntax definition can be parsed without
errors. 
"""


class FplSyntaxTests(unittest.TestCase):
    path = None
    path_to_grammar = None
    util = None

    @classmethod
    def setUpClass(cls):
        cls.path = os.path.normpath(os.path.abspath(__file__))
        if os.path.isfile(cls.path):
            cls.path = os.path.dirname(cls.path)
        cls.path_to_grammar = os.path.join(cls.path, "../../../grammar")
        cls.path_to_usecases = os.path.join(cls.path)
        cls.util = Utils()
        cls.fpl_parser = cls.util.get_parser(cls.path_to_grammar + "/fpl_tatsu_format.ebnf")

    @parameterized.expand([
        "test_syntax_type_class_inheritance",
        "test_syntax_type_in_image_of_functional_terms",
        "test_syntax_type_var_declarations_in_block",
        "test_syntax_type_var_declarations_in_signature",
        "test_syntax_type_of_properties",
    ])
    def test_parser(self, use_case):
        interpreter = FplInterpreter(self.fpl_parser)
        code = self.util.get_file_content(self.path_to_usecases + "/" + use_case + ".fpl")
        if interpreter.is_in_verbose_mode():
            try:
                interpreter.syntax_transform(use_case, code)
            except FailedToken as ex:
                return
            self.assertFalse(True)
        else:
            interpreter.syntax_transform(use_case, code)
            # exactly no error was found
            # (unfortunately, TatSu parsers do not support error recovery and stop after the first error)
            if len(interpreter.get_errors()) > 0:
                print(interpreter.get_errors()[0])
            self.assertEqual(0, len(interpreter.get_errors()))

    @parameterized.expand([
        "test_syntax_type_faulty_calls_001",
        "test_syntax_type_faulty_calls_002",
        "test_syntax_type_faulty_calls_003",
        "test_syntax_type_faulty_calls_004",
        "test_syntax_type_faulty_calls_005",
        "test_syntax_type_faulty_calls_006",
        "test_syntax_type_faulty_calls_007",
        "test_syntax_type_faulty_calls_008",
        "test_syntax_type_faulty_calls_009",
        "test_syntax_type_faulty_calls_010",
        "test_syntax_type_faulty_calls_011",
        "test_syntax_type_faulty_calls_012",
        "test_syntax_type_faulty_calls_013",
        "test_syntax_type_faulty_calls_014",
        "test_syntax_type_faulty_calls_015",
        "test_syntax_type_faulty_calls_016",
        "test_syntax_type_faulty_calls_017",
        "test_syntax_type_faulty_calls_018",
        "test_syntax_type_faulty_calls_019",
        "test_syntax_type_faulty_calls_020",
        "test_syntax_type_faulty_calls_021",
        "test_syntax_type_faulty_calls_022",
        "test_syntax_type_faulty_calls_023",
        "test_syntax_type_faulty_calls_024",
        "test_syntax_type_faulty_calls_025",
        "test_syntax_type_faulty_calls_026",
        "test_syntax_type_faulty_calls_027",
        "test_syntax_type_faulty_calls_028",
        "test_syntax_type_faulty_calls_029",
    ])
    def test_fail_parser_calls(self, use_case):
        # the above tests should fail syntactically,
        # because the they contain a "call" using anonymous types instead of variables
        interpreter = FplInterpreter(self.fpl_parser)
        code = self.util.get_file_content(self.path_to_usecases + "/" + use_case + ".fpl")
        if interpreter.is_in_verbose_mode():
            try:
                interpreter.syntax_transform(use_case, code)
            except FailedToken as ex:
                return
            except FailedParse as ex:
                return
            except FailedPattern as ex:
                return
            self.assertFalse(True)
        else:
            interpreter.syntax_transform(use_case, code)
            # exactly one parse error was found
            self.assertEqual(1, len(interpreter.get_errors()))
            self.assertIn("FplParserError", str(interpreter.get_errors()[0]))

    @parameterized.expand([
        "test_syntax_type_var_declarations_in_signature_fail_001",
        "test_syntax_type_var_declarations_in_signature_fail_002",
        "test_syntax_type_var_declarations_in_signature_fail_003",
        "test_syntax_type_var_declarations_in_signature_fail_004",
        "test_syntax_type_var_declarations_in_signature_fail_005",
        "test_syntax_type_var_declarations_in_signature_fail_006",
        "test_syntax_type_var_declarations_in_signature_fail_007",
        "test_syntax_type_var_declarations_in_signature_fail_008",
        "test_syntax_type_var_declarations_in_signature_fail_009",
        "test_syntax_type_var_declarations_in_signature_fail_010",
        "test_syntax_type_var_declarations_in_signature_fail_011",
        "test_syntax_type_var_declarations_in_signature_fail_012",
        "test_syntax_type_var_declarations_in_signature_fail_013",
        "test_syntax_type_var_declarations_in_signature_fail_014",
        "test_syntax_type_var_declarations_in_signature_fail_015",
        "test_syntax_type_var_declarations_in_signature_fail_016",
        "test_syntax_type_var_declarations_in_signature_fail_017",
        "test_syntax_type_var_declarations_in_signature_fail_018",
        "test_syntax_type_var_declarations_in_signature_fail_019",
        "test_syntax_type_var_declarations_in_signature_fail_020",
        "test_syntax_type_var_declarations_in_signature_fail_021",
        "test_syntax_type_var_declarations_in_signature_fail_022",
        "test_syntax_type_var_declarations_in_signature_fail_023",
        "test_syntax_type_var_declarations_in_signature_fail_024",
        "test_syntax_type_var_declarations_in_signature_fail_025",
        "test_syntax_type_var_declarations_in_signature_fail_026",
        "test_syntax_type_var_declarations_in_signature_fail_027",
        "test_syntax_type_var_declarations_in_signature_fail_028",
        "test_syntax_type_var_declarations_in_signature_fail_029",
        "test_syntax_type_var_declarations_in_signature_fail_030",
        "test_syntax_type_var_declarations_in_signature_fail_031",
    ])
    def test_fail_parser_signature(self, use_case):
        # the above tests should fail syntactically,
        # because the they contain a signature containing some anonymous types instead of declared variables
        interpreter = FplInterpreter(self.fpl_parser)
        code = self.util.get_file_content(self.path_to_usecases + "/" + use_case + ".fpl")
        if interpreter.is_in_verbose_mode():
            try:
                interpreter.syntax_transform(use_case, code)
            except FailedToken as ex:
                return
            except FailedParse as ex:
                return
            except FailedPattern as ex:
                return
            self.assertFalse(True)
        else:
            interpreter.syntax_transform(use_case, code)
            # exactly one parse error was found
            self.assertEqual(1, len(interpreter.get_errors()))
            self.assertIn("FplParserError", str(interpreter.get_errors()[0]))

    @parameterized.expand([
        "test_syntax_type_of_properties_fail_001",
        "test_syntax_type_of_properties_fail_002",
        "test_syntax_type_of_properties_fail_003",
        "test_syntax_type_of_properties_fail_004",
        "test_syntax_type_of_properties_fail_005",
        "test_syntax_type_of_properties_fail_006",
        "test_syntax_type_of_properties_fail_007",
        "test_syntax_type_of_properties_fail_008",
        "test_syntax_type_of_properties_fail_009",
        "test_syntax_type_of_properties_fail_010",
    ])
    def test_fail_parser_property_type(self, use_case):
        # the above tests should fail syntactically,
        # because the they contain a property type that cannot be disambiguated with its signature
        interpreter = FplInterpreter(self.fpl_parser)
        code = self.util.get_file_content(self.path_to_usecases + "/" + use_case + ".fpl")
        if interpreter.is_in_verbose_mode():
            try:
                interpreter.syntax_transform(use_case, code)
            except FailedToken as ex:
                return
            except FailedParse as ex:
                return
            except FailedPattern as ex:
                return

            self.assertFalse(True)
        else:
            interpreter.syntax_transform(use_case, code)
            # exactly one parse error was found
            self.assertEqual(1, len(interpreter.get_errors()))
            self.assertIn("FplParserError", str(interpreter.get_errors()[0]))

    @parameterized.expand([
        "test_syntax_type_class_inheritance_fail_001",
        "test_syntax_type_class_inheritance_fail_002",
        "test_syntax_type_class_inheritance_fail_003",
        "test_syntax_type_class_inheritance_fail_004",
        "test_syntax_type_class_inheritance_fail_005",
        "test_syntax_type_class_inheritance_fail_006",
        "test_syntax_type_class_inheritance_fail_007",
        "test_syntax_type_class_inheritance_fail_008",
        "test_syntax_type_class_inheritance_fail_009",
        "test_syntax_type_class_inheritance_fail_010",
        "test_syntax_type_class_inheritance_fail_011",
    ])
    def test_fail_parser_inheritance_type(self, use_case):
        # the above tests should fail syntactically,
        # because the they contain a property type that cannot be disambiguated with its signature
        interpreter = FplInterpreter(self.fpl_parser)
        code = self.util.get_file_content(self.path_to_usecases + "/" + use_case + ".fpl")
        if interpreter.is_in_verbose_mode():
            try:
                interpreter.syntax_transform(use_case, code)
            except FailedToken as ex:
                return
            except FailedParse as ex:
                return
            except FailedPattern as ex:
                return

            self.assertFalse(True)
        else:
            interpreter.syntax_transform(use_case, code)
            # exactly one parse error was found
            self.assertEqual(1, len(interpreter.get_errors()))
            self.assertIn("FplParserError", str(interpreter.get_errors()[0]))
