import pytest
import numpy as np
from sympy import symbols

from volpy import (
    CartesianCoordinate,
    CoordinateSystem,
    Line2D,
    Triangle,
    TriangularMesh,
    Survey,
)

"""
Test Line2D correctly represents a line. Cases below were calculated manually.
"""
test_cases = (('point_A', 'point_B', 'input_x', 'output_y'),
[
    (CartesianCoordinate(3, 5, 8),
     CartesianCoordinate(4, 2, 4),
     9.0,
     -13), # Assert that f(9)=-13 for the line AB

    (CartesianCoordinate(3, 5, 8),
     CartesianCoordinate(3, 2, 4),
     163.4,
     None),

    (CartesianCoordinate(19.34, 3.12, 8),
     CartesianCoordinate(0.5, -8.12, 4),
     18,
     2.328),
])

@pytest.mark.parametrize(*test_cases)
def test_2dLine(point_A, point_B, input_x, output_y):
    line = Line2D(point_A, point_B)
    line_equation = line.get_line_equation()
    if line_equation is None:
        output_y_calculated = None
        assert output_y == output_y_calculated
    else:
        x = symbols('x')
        output_y_calculated = float(line_equation.subs(x, input_x))
        assert output_y == pytest.approx(output_y_calculated, abs=0.01)

"""
Test the subtraction of Cartesian Coordinates
"""
test_cases = (('point_A', 'point_B', 'expected'),
[
    (CartesianCoordinate(1, 5, 8),
     CartesianCoordinate(6, 2, 10),
     np.array([5, -3, 2])),

    (CartesianCoordinate(8.5, -13, 5),
     CartesianCoordinate(1.72, 70.5, 10.8),
     np.array([-6.78, 83.5, 5.8])),
])

@pytest.mark.parametrize(*test_cases)
def test_point_subtraction(point_A, point_B, expected):
    vector_AB = point_B - point_A
    assert np.allclose(vector_AB, expected)


"""
Test plane equation is correctly defined.
"""
test_cases = (
    ('point_A', 'point_B', 'point_C', 'input_y', 'input_x', 'output_z'),
    [
    (CartesianCoordinate(3, 0, 8),
    CartesianCoordinate(5, 9, 1),
    CartesianCoordinate(10, 4, 7),
    32,
    18,
    -14.164)
    ]
)

@pytest.mark.parametrize(*test_cases)
def test_plane_equation(point_A, point_B, point_C, input_y, input_x, output_z):
    triangle = Triangle(point_A, point_B, point_C)
    plane = triangle.get_plane_equation()
    x, y = symbols('x y')
    output_z_calculated = float(plane.subs([(x, input_x), (y, input_y)]))
    assert output_z_calculated == pytest.approx(output_z, abs=0.01)

"""
Test volume of a triangle is calculated as expected.
"""
test_cases = (
    ('point_A', 'point_B', 'point_C', 'expected_volume'),
    [
        (CartesianCoordinate(3, 0, 8),
         CartesianCoordinate(5, 9, 1),
         CartesianCoordinate(10, 4, 7),
         146.67),

        (CartesianCoordinate(5, 9, 1),
         CartesianCoordinate(3, 0, 8),
         CartesianCoordinate(10, 4, 7),
         146.67),

        (CartesianCoordinate(10, 4, 7),
         CartesianCoordinate(3, 0, 8),
         CartesianCoordinate(5, 9, 1),
         146.67),

        # Create test case for a prism.
         (CartesianCoordinate(5, 0, 20),
         CartesianCoordinate(0, 10, 20),
         CartesianCoordinate(0, 0, 20),
         500.00),

         (CartesianCoordinate(15, 10, 20),
         CartesianCoordinate(5, 5, 20),
         CartesianCoordinate(10,15, 20),
         750.00)

    ]
)

@pytest.mark.parametrize(*test_cases)
def test_volume_calculation(point_A, point_B, point_C, expected_volume):
    triangle = Triangle(point_A, point_B, point_C)
    volume = triangle.get_volume()
    assert volume == pytest.approx(expected_volume, abs=0.05)


"""Test sorting of Cartesian Coordinates as a function of their x coordinate"""
test_cases = (
    ('point_A', 'point_B'),
    [
        (CartesianCoordinate(8, 12, 5), CartesianCoordinate(10, 9, 2))
    ]
)

@pytest.mark.parametrize(*test_cases)
def test_cartesian_sorting(point_A, point_B):
    array = [point_B, point_A]
    array.sort()
    print(array)
    assert array[0] == point_A
    assert array[1] == point_B

"""Test Mesh Volume"""
def test_mesh_volume():
    source = '../volpy/sample_data/survey_delaunay_Cartesian.csv'
    survey = Survey(source,
                    'sample',
                    coordinate_system=CoordinateSystem.CARTESIAN)
    mesh = TriangularMesh(survey.data)
    expected_volume = 168.0-(13.0*4.0*1.0)
    assert mesh.get_volume() == pytest.approx(expected_volume, rel=0.01)

"""Test Cut and Fill Volumes"""
# RESUME HERE
def test_cut_volume():
    assert 1 == 1

def test_fill_volume():
    assert 1 == 1
