"""
Test of rotation/positioning of simple cubic particle.
Original particle is compared with the one obtained.
"""

import unittest
import PyFuTestInfrastructure as infrastruct
from bornagain import *


class RotationsCubeTest(unittest.TestCase):
    """
    Test of rotations and translations of simple cube in three layers system
    """

    def get_sample(self,
                   formfactor,
                   rot=None,
                   pos=None,
                   layout_rot=None,
                   layout_pos=None,
                   add_to="Vacuum"):
        mParticle = RefractiveMaterial("Particle", 6e-4, 2e-8)
        mMiddle = RefractiveMaterial("MidleLayer", 5e-5, 2e-8)
        mSubstrate = RefractiveMaterial("Substrate", 6e-6, 2e-8)

        particle = Particle(mParticle, formfactor)
        if rot:
            particle.rotate(rot)
        if pos:
            particle.translate(pos)

        if layout_rot:
            particle.rotate(layout_rot)
        if layout_pos:
            particle.translate(layout_pos)

        layout = ParticleLayout()
        layout.addParticle(particle)

        vacuum_layer = Layer(Vacuum())
        middle_layer = Layer(mMiddle, 50)
        substrate = Layer(mSubstrate)

        if add_to == "Vacuum":
            vacuum_layer.addLayout(layout)
        else:
            middle_layer.addLayout(layout)

        sample = MultiLayer()
        sample.addLayer(vacuum_layer)
        sample.addLayer(middle_layer)
        sample.addLayer(substrate)
        return sample

    def get_result(self, data, add_to="Vacuum"):
        ff = data[0]
        rot = data[1]
        pos = data[2]
        layout_rot = data[3]
        layout_pos = data[4]
        sample = self.get_sample(ff, rot, pos, layout_rot, layout_pos, add_to)
        # simulation = self.get_simulation(sample)
        simulation = infrastruct.get_simulation_MiniGISAS(sample)
        return simulation.simulate()

    def get_difference(self, reference_data, test_data, add_to="Vacuum"):
        intensity = self.get_result(test_data, add_to)
        return meanRelativeDifference(reference_data, intensity)

    def testRotationZ(self):
        """
        Cube is Z-rotated either through setRotation method or through particle layout.
        The result is compared with unrotated cube.
        """
        box = Box(10, 10, 10)

        data_to_test = [
            # ff  rot                     pos                    layout_rot              layout_pos
            (box, None, None, None, None),  # reference
            (box, RotationZ(90. * deg), None, None, None),  # rotating particle
            (box, RotationZ(-90. * deg), None, None, None),
            (box, RotationZ(180. * deg), None, None, None),
            (box, None, None, RotationZ(90. * deg),
             None),  # rotating through layout
            (box, RotationZ(45. * deg), None, RotationZ(45. * deg),
             None),  # cumulative rotation
        ]

        reference_data = self.get_result(data_to_test[0])

        isSuccess = True
        for i in range(1, len(data_to_test)):
            diff = self.get_difference(reference_data, data_to_test[i])
            print("{0}    #{1}   diff {2:.2e}".format(
                self.testRotationZ.__name__, i, diff))
            if diff > 1e-10:
                isSuccess = False

        self.assertTrue(isSuccess)

    def testRotationY(self):
        """
        Cube is Y-rotated either through setRotation method or through particle layout.
        Additional translation is applied if necessary.
        The result is compared with unrotated cube.
        """
        box = Box(10, 10, 10)

        data_to_test = [
            # ff rot pos layout_rot layout_pos
            (box, None, None, None, None),  # reference
            (box, RotationY(90. * deg), R3(0, 0, 5), None,
             None),  # rotating and translating
            (box, None, None, RotationY(90. * deg),
             R3(0, 0, 5)),  # rotating and translating
            (box, RotationY(90. * deg), None, None,
             R3(0, 0, 5)),  # rotating and translating
            (box, RotationY(45. * deg), R3(0, 0, 0), RotationY(45. * deg),
             R3(0, 0, 5)),  # rotating and translating
        ]

        reference_data = self.get_result(data_to_test[0])

        isSuccess = True
        for i in range(1, len(data_to_test)):
            diff = self.get_difference(reference_data, data_to_test[i])
            print("{0}    #{1}   diff {2:.2e}".format(
                self.testRotationY.__name__, i, diff))
            if diff > 1e-10:
                isSuccess = False

        self.assertTrue(isSuccess)

    def testRotationX(self):
        """
        Cube is Z-rotated either through setRotation method or through particle layout.
        Additional translation is applied if necessary.
        The result is compared with unrotated cube.
        """
        box = Box(10, 10, 10)

        data_to_test = [
            # ff rot pos layout_rot layout_pos
            (box, None, None, None, None),  # reference
            (box, RotationX(90. * deg), R3(0, 0, 5), None,
             None),  # rotating and translating
            (box, None, None, RotationX(90. * deg),
             R3(0, 0, 5)),  # rotating and translating
            (box, RotationX(90. * deg), None, None,
             R3(0, 0, 5)),  # rotating and translating
            (box, RotationX(45. * deg), R3(0, 0, 0), RotationX(45. * deg),
             R3(0, 0, 5)),  # rotating and translating
        ]

        reference_data = self.get_result(data_to_test[0])

        isSuccess = True
        for i in range(1, len(data_to_test)):
            diff = self.get_difference(reference_data, data_to_test[i])
            print("{0}    #{1}   diff {2:.2e}".format(
                self.testRotationX.__name__, i, diff))
            if diff > 1e-10:
                isSuccess = False

        self.assertTrue(isSuccess)

    def testRotationsInMiddleLayer(self):
        """
        """
        box = Box(10, 10, 10)

        data_to_test = [
            # ff rot pos layout_rot layout_pos
            (box, None, R3(0, 0, -25), None, None),  # reference
            (box, RotationX(90. * deg), R3(0, 0, -20), None,
             None),  # rotating and translating
        ]

        reference_data = self.get_result(data_to_test[0], "add_to_middle")

        isSuccess = True
        for i in range(1, len(data_to_test)):
            diff = self.get_difference(reference_data, data_to_test[i],
                                       "add_to_middle")
            print("{0}    #{1}   diff {2:.2e}".format(
                self.testRotationX.__name__, i, diff))
            if diff > 1e-10:
                isSuccess = False

        self.assertTrue(isSuccess)


if __name__ == '__main__':
    unittest.main()
