"""
Test module for the calculator module.
"""
import pytest
from calculator.calculator import Calculator
from calculator.operators import Operator


@pytest.fixture(scope="module", name="setup")
def fixture_setup():
    """
    Setup the test suite, by instantiating the calculator and the operators.
    """
    plus = Operator('+', 1, lambda a, b: a + b)
    minus = Operator('-', 1, lambda a, b: a - b)
    times = Operator('*', 2, lambda a, b: a * b)
    divide = Operator('/', 2, lambda a, b: a / b)
    calculator = Calculator(
        operators={'+': plus, '-': minus, '*': times, '/': divide})
    yield plus, minus, times, divide, calculator


def test_tokenizer(setup):
    """
    Test the tokenizer.
    """
    plus, minus, times, divide, calc = setup
    assert calc.tokenize("1 + 2") == [1.0, plus, 2.0]
    assert calc.tokenize("1 + 2 * 3") == [1.0, plus, 2.0, times, 3.0]
    assert calc.tokenize(
        "1 + 2 * 3 / 4") == [1.0, plus, 2.0, times, 3.0, divide, 4.0]
    assert calc.tokenize(
        "1 + 2 * 3 / 4 - 5") == [1.0, plus, 2.0, times, 3.0, divide, 4.0, minus, 5.0]


def test_parser(setup):
    """
    Test the parser.
    """
    _, _, _, _, calc = setup
    assert repr(calc.parse(calc.tokenize("1 + 2"))) == '(1.0 + 2.0)'
    assert repr(calc.parse(calc.tokenize("1 + 2 * 3"))
                ) == '(1.0 + (2.0 * 3.0))'
    assert repr(calc.parse(calc.tokenize(
        "1 + 2 * 3 / 4"))) == '(1.0 + ((2.0 * 3.0) / 4.0))'
    assert repr(calc.parse(calc.tokenize(
        "1 + 2 * 3 / 4 - 5"))) == '((1.0 + ((2.0 * 3.0) / 4.0)) - 5.0)'


def test_evaluation(setup):
    """
    Test the evaluation.
    """
    _, _, _, _, calc = setup
    assert calc("1 + 2") == 3
    assert calc("1 + 2 * 3") == 7
    assert calc("1 + 2 * 3 / 4") == 2.5
    assert calc("1 + 2 * 3 / 4 - 5") == -2.5