import unittest

import paramak
import pytest


class TestToroidalFieldCoilCoatHanger(unittest.TestCase):
    def setUp(self):
        self.test_shape = paramak.ToroidalFieldCoilCoatHanger(
            horizontal_start_point=(200, 500),
            horizontal_length=400,
            vertical_mid_point=(700, 0),
            vertical_length=500,
            thickness=50,
            distance=30,
            number_of_coils=1,
            with_inner_leg=True,
        )

    def test_default_parameters(self):
        """Checks that the default parameters of a ToroidalFieldCoilCoatHanger
        are correct."""

        assert self.test_shape.rotation_angle == 360
        assert self.test_shape.with_inner_leg

    def test_points_calculation(self):
        """Checks that the points used to construct the
        ToroidalFieldCoilCoatHanger are calculated correctly from the parameters
        given."""

        assert self.test_shape.points == [
            (200, 500),
            (600, 500),
            (700, 250.0),
            (700, -250.0),
            (600, -500),
            (200, -500),
            (200, -550),
            (600, -550),
            (646.423834544263, -518.5695338177052),
            (746.423834544263, -268.5695338177052),
            (750, -250.0),
            (750, 250.0),
            (746.423834544263, 268.5695338177052),
            (646.423834544263, 518.5695338177052),
            (600, 550),
            (200, 550),
        ]

    def test_processed_points_calculation(self):
        """Checks that the processed_points used to construct the
        ToroidalFieldCoilCoatHanger are calculated correctly from the parameters
        given."""

        assert self.test_shape.processed_points == [
            (200, 500, "straight"),
            (600, 500, "straight"),
            (700, 250.0, "straight"),
            (700, -250.0, "straight"),
            (600, -500, "straight"),
            (200, -500, "straight"),
            (200, -550, "straight"),
            (600, -550, "straight"),
            (646.423834544263, -518.5695338177052, "straight"),
            (746.423834544263, -268.5695338177052, "straight"),
            (750, -250.0, "straight"),
            (750, 250.0, "straight"),
            (746.423834544263, 268.5695338177052, "straight"),
            (646.423834544263, 518.5695338177052, "straight"),
            (600, 550, "straight"),
            (200, 550, "straight"),
            (200, 500, "straight"),
        ]

    def test_creation_with_inner_leg(self):
        """Creates a tf coil with inner leg using the ToroidalFieldCoilCoatHanger
        parametric component and checks that a cadquery solid is created."""

        assert self.test_shape.solid is not None
        assert self.test_shape.volume() > 1000
        assert self.test_shape.inner_leg_connection_points is not None

        test_inner_leg = paramak.ExtrudeStraightShape(
            points=self.test_shape.inner_leg_connection_points, distance=30
        )
        assert test_inner_leg.solid is not None

    def test_creation_with_inner_leg_with_overlap(self):
        """Creates tf coils with overlapping inner legs using the ToroidalFieldCoilCoatHanger
        parametric component and checks that a cadquery solid is created correctly."""

        self.test_shape.horizontal_start_point = (0, 500)
        self.test_shape.number_of_coils = 8
        assert self.test_shape.solid is not None

        with_inner_leg_volume = self.test_shape.volume()

        self.test_shape.with_inner_leg = False
        without_inner_leg_volume = self.test_shape.volume()

        assert self.test_shape.solid is not None

        test_inner_leg = paramak.ExtrudeStraightShape(
            points=[(0, 500), (50, 500), (50, -500), (0, -500)],
            distance=30,
            azimuth_placement_angle=[0, 45, 90, 135, 180, 225, 270, 315],
        )
        inner_leg_volume = test_inner_leg.volume()

        assert with_inner_leg_volume == pytest.approx(
            without_inner_leg_volume + inner_leg_volume
        )

    def test_creation_no_inner_leg(self):
        """Creates a tf coil with no inner leg using the ToroidalFieldCoilRectangle
        parametric component and checks that a cadquery solid is created."""

        test_volume = self.test_shape.volume()

        test_inner_leg = paramak.ExtrudeStraightShape(
            points=self.test_shape.inner_leg_connection_points, distance=30
        )
        inner_leg_volume = test_inner_leg.volume()

        self.test_shape.with_inner_leg = False

        assert self.test_shape.solid is not None
        assert self.test_shape.volume() == pytest.approx(test_volume - inner_leg_volume)

    def test_absolute_volume(self):
        """Creates a tf coil using the ToroidalFieldCoilCoatHanger parametric
        component and checks that the volume is correc."""

        assert self.test_shape.volume() == pytest.approx(
            (400 * 50 * 30 * 2)
            + ((50 * 50 * 30 / 2) * 2)
            + (50 * 500 * 30)
            + (
                ((150 * 250 * 30) - (((100 * 250) / 2) * 30) - (((100 * 250) / 2) * 30))
                * 2
            )
            + (50 * 1000 * 30),
            rel=0.1,
        )

        self.test_shape.with_inner_leg = False
        assert self.test_shape.volume() == pytest.approx(
            (400 * 50 * 30 * 2)
            + ((50 * 50 * 30 / 2) * 2)
            + (50 * 500 * 30)
            + (
                ((150 * 250 * 30) - (((100 * 250) / 2) * 30) - (((100 * 250) / 2) * 30))
                * 2
            ),
            rel=0.1,
        )

        self.test_shape.with_inner_leg = True
        self.test_shape.number_of_coils = 8
        assert self.test_shape.volume() == pytest.approx(
            (
                (400 * 50 * 30 * 2)
                + ((50 * 50 * 30 / 2) * 2)
                + (50 * 500 * 30)
                + (
                    (
                        (150 * 250 * 30)
                        - (((100 * 250) / 2) * 30)
                        - (((100 * 250) / 2) * 30)
                    )
                    * 2
                )
                + (50 * 1000 * 30)
            )
            * 8,
            rel=0.1,
        )

        self.test_shape.with_inner_leg = False
        assert self.test_shape.volume() == pytest.approx(
            (
                (400 * 50 * 30 * 2)
                + ((50 * 50 * 30 / 2) * 2)
                + (50 * 500 * 30)
                + (
                    (
                        (150 * 250 * 30)
                        - (((100 * 250) / 2) * 30)
                        - (((100 * 250) / 2) * 30)
                    )
                    * 2
                )
            )
            * 8,
            rel=0.1,
        )

    def test_absolute_area(self):
        """Creates a tf coil using the ToroidalFieldCoilCoatHanger parametric
        component and checks that the areas of the faces are correct."""

        assert self.test_shape.area == pytest.approx(
            (
                (
                    (400 * 50 * 2)
                    + (50 * 50 * 0.5 * 2)
                    + (((150 * 250) - (100 * 250 * 0.5) - (100 * 250 * 0.5)) * 2)
                    + (500 * 50)
                )
                * 2
            )
            + ((50 * 30) * 4)
            + ((400 * 30) * 4)
            + ((500 * 30) * 2)
            + ((((50 ** 2 + 50 ** 2) ** 0.5) * 30) * 2)
            + ((((100 ** 2 + 250 ** 2) ** 0.5) * 30) * 4)
            + ((50 * 1000) * 2)
            + ((1000 * 30) * 2),
            rel=0.1,
        )
        assert len(self.test_shape.areas) == 24

        assert self.test_shape.areas.count(pytest.approx(50 * 30)) == 4
        assert self.test_shape.areas.count(pytest.approx(400 * 30)) == 4
        assert self.test_shape.areas.count(pytest.approx(500 * 30)) == 2
        assert (
            self.test_shape.areas.count(
                pytest.approx(((100 ** 2 + 250 ** 2) ** 0.5) * 30)
            )
            == 4
        )
        assert self.test_shape.areas.count(pytest.approx(50 * 1000)) == 2
        assert self.test_shape.areas.count(pytest.approx(1000 * 30)) == 2

        self.test_shape.with_inner_leg = False
        assert self.test_shape.area == pytest.approx(
            (
                (
                    (400 * 50 * 2)
                    + (50 * 50 * 0.5 * 2)
                    + (((150 * 250) - (100 * 250 * 0.5) - (100 * 250 * 0.5)) * 2)
                    + (500 * 50)
                )
                * 2
            )
            + ((50 * 30) * 2)
            + ((400 * 30) * 4)
            + ((500 * 30) * 2)
            + ((((50 ** 2 + 50 ** 2) ** 0.5) * 30) * 2)
            + ((((100 ** 2 + 250 ** 2) ** 0.5) * 30) * 4),
            rel=0.1,
        )

    def test_rotation_angle(self):
        """Creates a tf coil with a rotation_angle < 360 degrees and checks
        that the correct cut is performed and the volume is correct."""

        self.test_shape.number_of_coils = 8

        self.test_shape.rotation_angle = 360
        self.test_shape.workplane = "XZ"
        test_volume = self.test_shape.volume()
        self.test_shape.rotation_angle = 180
        assert self.test_shape.volume() == pytest.approx(test_volume * 0.5, rel=0.01)

        self.test_shape.rotation_angle = 360
        self.test_shape.workplane = "YZ"
        test_volume = self.test_shape.volume()
        self.test_shape.rotation_angle = 180
        assert self.test_shape.volume() == pytest.approx(test_volume * 0.5, rel=0.01)

        self.test_shape.rotation_angle = 360
        self.test_shape.workplane = "XY"
        self.test_shape.rotation_axis = "Y"
        test_volume = self.test_shape.volume()
        self.test_shape.rotation_angle = 180
        assert self.test_shape.volume() == pytest.approx(test_volume * 0.5)
