import unittest
import sys
import os

from utils import execute, kill, run_file, run_barebone

def extend_commands(commands):
    # Initially we communicate with the compilation_manager to add the bytecodes
    pre =   [
                '3',
                '"upload"',
                '"test.o"',
                '"MD5_HASH"',
                'true',     # use NEW interface for constructors
                '"funcdef"', '"main"', '0', # Main function
            ]
    # We only have to define "main" for now
    post =  [
                'true',
                '"main"',
                'true',
                'false',
            ]
    return pre + commands + post

class TestConstructorsActionLanguageLinkable(unittest.TestCase):
    def test_constructors_simple(self):
        commands = [
                        '"output"',
                            '"const"', 'true',
                            'true',
                        '"return"',
                            'true',
                            '"const"', 'true',
                        'false',
                    ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["True"], None, link=["test.o"]))

    def test_constructors_if_else_true(self):
        commands = [
                        '"if"',                    # If
                        '"const"', 'true',    # Condition
                        '"output"',            # True-part
                            '"const"', 'true',
                            'false',            # Has next
                        'true',                 # Has else
                        '"output"',            # False-part
                            '"const"', 'false',
                            'false',            # Has next
                        'true',                 # Has next
                        '"return"',            # Return
                            'true',             # Has value
                            '"const"', "true",
                        'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["True"], None, link=["test.o"]))

    def test_constructors_if_else_false(self):
        commands = [
                        '"if"',                    # If
                        '"const"', 'false',   # Condition
                        '"output"',            # True-part
                            '"const"', 'true',
                            'false',            # Has next
                        'true',                 # Has else
                        '"output"',            # False-part
                            '"const"', 'false',
                            'false',            # Has next
                        'true',                 # Has next
                        '"return"',            # Return
                            'true',             # Has value
                            '"const"', "true",
                        'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["False"], None, link=["test.o"]))

    def test_constructors_if_true(self):
        commands = [
                        '"if"',                    # If
                        '"const"', 'true',    # Condition
                        '"output"',            # True-part
                            '"const"', 'true',
                            'false',            # Has next
                        'false',                # Has else
                        'true',                 # Has next
                        '"return"',            # Return
                            'true',             # Has value
                            '"const"', "true",
                    'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["True"], None, link=["test.o"]))

    def test_constructors_if_false(self):
        commands = [
                        '"if"',                    # If
                        '"const"', 'false',   # Condition
                        '"output"',            # True-part
                            '"const"', 'true',
                            'false',            # Has next
                        'false',                # Has else
                        'true',                 # Has next
                        '"return"',            # Return
                            'true',             # Has value
                            '"const"', "true",
                        'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, [None], None, timeout=True, link=["test.o"]))

    """
    def test_constructors_addition(self):
        commands = [('"output"',
                        ('"call"',
                            ('"deref"', '"primitives/integer_addition"'),
                            '2',
                            ('"const"', '1'),
                            ('"const"', '5'),
                            'false',
                        ),
                        'true',
                        ('"return"', 'true', '"const"', 'true'),
                    )
                ]
        self.assertTrue(run_barebone(flatten(commands), ["6"], 1))
    """

    def test_constructors_while_false(self):
        commands = [
                        '"while"',                 # While
                        '"const"', 'false',   # Condition
                        '"output"',            # True-part
                            '"const"', 'true',
                            'false',            # Has next
                        'true',                 # Has next
                        '"output"',            # Output false
                            '"const"', 'false',
                            'true',             # Has next
                            '"return"',        # Return
                                'true',         # Has value
                                '"const"', "true",
                        'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["False"], None, link=["test.o"]))

    def test_constructors_while_true(self):
        commands = [
                        '"while"',                 # While
                        '"const"', 'true',    # Condition
                        '"output"',            # True-part
                            '"const"', 'true',
                            'false',            # Has next
                        'true',                 # Has next
                        '"output"',            # False-part
                            '"const"', 'false',
                            'true',            # Has next
                        '"return"',            # Return
                            'true',             # Has value
                            '"const"', "true",
                        'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["True", "True", "True", "True"], None, link=["test.o"]))

    def test_constructors_declare_and_assign(self):
        commands = [
                    '"declare"',
                        1,
                        'true',
                    '"assign"',
                        '"resolve"', 1,
                        '"const"', '5',
                        'true',
                    '"output"',
                        '"access"', '"resolve"', 1,
                        'true',
                    '"return"',
                        'true',
                        '"const"', "true",
                    'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["5"], None, link=["test.o"]))

    """
    def test_constructors_output_input(self):
        commands = [
                     '"output"',
                        '"input"',
                        'true',
                    '"return"',
                        'true',
                        '"const"', "true",
                    'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands + ['123456'], ["123456"], None, link=["test.o"]))
    """

    def test_constructors_global_and_assign(self):
        commands = [
                    '"global"',
                        '"my_global"',
                        'true',
                    '"assign"',
                        '"resolve"', '"my_global"',
                        '"const"', '5',
                        'true',
                    '"output"',
                        '"access"', '"resolve"', '"my_global"',
                        'true',
                    '"return"',
                        'true',
                        '"const"', "true",
                    'false',
                ]
        commands = extend_commands(commands)
        self.assertTrue(run_barebone(commands, ["5"], None, link=["test.o"]))

    """
    def test_constructors_funcdecl(self):
        commands = ['"global"',
                        '"my_global"',
                        'true',
                    '"funcdef"', '"my_global"',
                        '2',
                        2,
                        3,
                        '"return"', 'true',
                            '"call"',
                                '"deref"', '"primitives/integer_addition"',
                                '2',
                                '"access"', '"resolve"', 2,
                                '"access"', '"resolve"', 3,
                                'false',
                        'true',
                    '"output"',
                        '"call"',
                            '"access"', '"resolve"', '"my_global"',
                            '2',
                            '"input"',
                            '"input"',
                            'false',
                        'true',
                    '"return"', 'true',
                        '"const"', 'true',
                ]
        self.assertTrue(run_barebone(flatten(commands) + ['2', '5'], ["7"], 1))

    def test_constructors_fibonacci(self):
        commands = ['"global"',
                        '"fibonacci"',
                        'true',
                    '"funcdef"', '"fibonacci"',
                        '1',
                        2,
                        '"if"',
                            '"call"',
                                '"deref"', '"primitives/integer_lte"',
                                '2',
                                '"access"', '"resolve"', 2,
                                '"const"', '2',
                                'false',
                            '"return"', 'true', '"const"', '1',
                            'true',
                            '"return"', 'true',
                                '"call"',
                                    '"deref"', '"primitives/integer_addition"',
                                    '2',
                                    '"call"',
                                        '"access"', '"resolve"', '"fibonacci"',
                                        '1',
                                        '"call"',
                                            '"deref"', '"primitives/integer_subtraction"',
                                            '2',
                                            '"access"', '"resolve"', 2,
                                            '"const"', '1',
                                            'false',
                                        'false',
                                    '"call"',
                                        '"access"', '"resolve"', '"fibonacci"',
                                        '1',
                                        '"call"',
                                            '"deref"', '"primitives/integer_subtraction"',
                                            '2',
                                            '"access"', '"resolve"', 2,
                                            '"const"', '2',
                                            'false',
                                        'false',
                                    'false',
                            'false',
                        'true',
                    '"while"',
                        '"const"', 'true',
                        '"output"',
                            '"call"',
                                '"access"', '"resolve"', '"fibonacci"',
                                '1',
                                '"input"',
                                'false',
                            'false',
                        'false',
                    ]
        self.assertTrue(run_barebone(flatten(commands) + ['1', '2', '3', '4', '5', '6', '7', '8'], ['1', '1', '2', '3', '5', '8', '13', '21'], 1))

    def test_constructors_continue(self):
        commands = ['"while"',
                        '"const"', 'true',
                        '"output"', '"const"', '1',
                            'true',
                            '"if"',
                                '"const"', 'true',
                                '"continue"',
                                'false',
                                'true',
                            '"output"', '"const"', '2',
                                'false',
                        'true',
                    '"output"', '"const"', '3',
                        'true',
                    '"return"', 'true',
                        '"const"', 'true',
                ]
        self.assertTrue(run_barebone(flatten(commands), ['1', '1', '1', '1', '1'], 1))
    def test_constructors_break(self):
        commands = ['"while"',
                        '"const"', 'true',
                        '"output"', '"const"', '1',
                            'true',
                            '"if"',
                                '"const"', 'true',
                                '"break"',
                                'false',
                                'true',
                            '"output"', '"const"', '2',
                                'false',
                        'true',
                    '"output"', '"const"', '3',
                        'true',
                    '"return"', 'true',
                        '"const"', 'true',
                ]
        self.assertTrue(run_barebone(flatten(commands), ['1', '3'], 1))
    """
