{%- import "generator_macros.j2" as macros with context -%}
{{ macros.header()}}

{% macro tuplify(dominoes_list) -%}
    [
    {%- for v in dominoes_list %}
    ({{ v | join(', ') }}){{- "," if not loop.last }}
    {%- endfor %}
    ]
{%- endmacro %}

class {{ exercise | camel_case }}Test(unittest.TestCase):
    {% for case in cases -%}
    def test_{{ case["description"] | to_snake }}(self):
        input_dominoes = {{ tuplify(case["input"]["dominoes"]) }}
        output_chain = can_chain(input_dominoes)

        {%- if case["expected"] %}
        self.assert_correct_chain(input_dominoes, output_chain)
        {%- else %}
        self.refute_correct_chain(input_dominoes, output_chain)
        {%- endif %}
    {% endfor %}


    # Utility methods

    def normalize_dominoes(self, dominoes):
        return list(sorted(tuple(sorted(domino)) for domino in dominoes))

    def assert_same_dominoes(self, input_dominoes, output_chain):
        msg = ('Dominoes used in the output must be the same '
               'as the ones given in the input')
        input_normal = self.normalize_dominoes(input_dominoes)
        output_normal = self.normalize_dominoes(output_chain)
        self.assertEqual(input_normal, output_normal, msg)

    def assert_consecutive_dominoes_match(self, output_chain):
        for i in range(len(output_chain) - 1):
            msg = ("In chain {}, right end of domino {} ({}) "
                   "and left end of domino {} ({}) must match")
            msg = msg.format(output_chain,
                             i,
                             output_chain[i],
                             i + 1,
                             output_chain[i + 1])
            self.assertEqual(output_chain[i][1], output_chain[i + 1][0], msg)

    def assert_dominoes_at_ends_match(self, output_chain):
        msg = ("In chain {}, left end of first domino ({}) and "
               "right end of last domino ({}) must match")
        msg = msg.format(output_chain, output_chain[0], output_chain[-1])
        self.assertEqual(output_chain[0][0], output_chain[-1][1], msg)

    def assert_correct_chain(self, input_dominoes, output_chain):
        msg = 'There should be a chain for {}'.format(input_dominoes)
        self.assertIsNotNone(output_chain, msg)
        self.assert_same_dominoes(input_dominoes, output_chain)
        if not any(output_chain):
            return
        self.assert_consecutive_dominoes_match(output_chain)
        self.assert_dominoes_at_ends_match(output_chain)

    def refute_correct_chain(self, input_dominoes, output_chain):
        msg = 'There should be no valid chain for {}'.format(input_dominoes)
        self.assertIsNone(output_chain, msg)

{{ macros.footer() }}

