#include "Test.h"

#include <math.h>
#include <limits>

#include "Measure_geometry_impl.h"
#include "Point_3D_impl.h"
#include "Defs.h"
#include "Create.h"
#include "Model.h"
#include "Path.h"
#include "Project.h"

namespace MM
{

class Measure_geometry_test : public Test
{
public:
    Measure_geometry_test (const Text& suite_name) : Test (suite_name) { }

    void run()
    {
        demo_points ();                        count_tests();
//        test_boundary_conditions();     count_tests();
        demo_atoms  ();                        count_tests();
        demo_mass_centers  ();                 count_tests();
    }

    void demo_points ()
    {
        Measure_geometry_impl measure;
        Text result;

        measure.get_info (result);
        TEST ("No info.", result == "");

        Point_3D_impl point_0 (0, 0, 0);
        measure.set_space_position_type (0, point_0);
        measure.get_info (result);
        TEST ("No info.", result != "");

        // 0---1
        Point_3D_impl point_1 (1.587, 0, 0);
        measure.set_space_position_type (1, point_1);
        measure.get_info (result);
        TEST ("Info.", result != "");
        DOUBLES_EQUAL (1.587, measure.last_length().A(),    0.001);
        DOUBLES_EQUAL (3.000, measure.last_length().bohr(), 0.002);
        
        //     2
        //     I
        // 0---1
        Point_3D_impl point_2 (1.587, 1, 0);
        measure.set_space_position_type (2, point_2);
        measure.get_info (result);
        TEST ("Info.", result != "");
        DOUBLES_EQUAL (1.000, measure.last_length().A(),    0.001);
        DOUBLES_EQUAL (90.00, measure.last_angle().deg(),   0.001);

        //     2---3
        //     I
        // 0---1
        Point_3D_impl point_3 (2*1.587, 1, 0);
        measure.set_space_position_type (3, point_3);
        measure.get_info (result);
        TEST ("Info.", result != "");
        DOUBLES_EQUAL (1.587, measure.last_length().A(),            0.001);
        DOUBLES_EQUAL (180.0, measure.last_dihedral_angle().deg(),  0.001);

        //     2<3
        //     I
        // 0---1
        point_3.set_x (1.587);
        point_3.set_z (1);
        measure.set_space_position_type (3, point_3);
        measure.get_info (result);
        DOUBLES_EQUAL (90.0, measure.last_dihedral_angle().deg(),  0.001);

        //     2
        //   / I
        // 03--1
        point_3.set_x (0);
        point_3.set_y (0);
        point_3.set_z (0);
        measure.set_space_position_type (3, point_3);
        measure.get_info (result);
        DOUBLES_EQUAL (sqrt(1.587*1.587+1), measure.last_length().A(),  0.001);
        DOUBLES_EQUAL (0.000, measure.last_dihedral_angle().deg(),      0.001);
    }

    void test_boundary_conditions()
    {
        using namespace std;
        Measure_geometry_impl measure;
        Text result;

        Point_3D_impl point_0 (0, 0, 0);
        Point_3D_impl point_1 (0, 0, 0);
        Point_3D_impl point_2 (0, 0, 0);
        Point_3D_impl point_3 (0, 0, 0);

        measure.set_space_position_type (0, point_0);
        measure.set_space_position_type (1, point_1);
        measure.set_space_position_type (2, point_2);
        measure.set_space_position_type (3, point_3);

        measure.get_info (result);

        //fix
        DOUBLES_EQUAL (0.000, measure.last_length().A(),            0.001);
        DOUBLES_EQUAL (0.000, measure.last_angle().rad(),           0.001);
        DOUBLES_EQUAL (0.000, measure.last_dihedral_angle().deg(),  0.001);

        /*DOUBLES_EQUAL (numeric_limits<double>::quiet_NaN(), 
                       measure.last_angle().rad(),           0.001);

        DOUBLES_EQUAL (numeric_limits<double>::quiet_NaN(), 
                       measure.last_dihedral_angle().deg(),  0.001);//*/
    }

    void demo_atoms ()
    {
        Own<Model> model (prototype <Model>(). clone ());
        model().load (Path::tests() + "CONH-0.hin");
        Project::singleton ().adopt (&model());

        Measure_geometry_impl measure;
        measure.set_atom_type (0, 1); // O
        measure.set_atom_type (1, 0); // C
        measure.set_atom_type (2, 2); // N
        measure.set_atom_type (3, 3); // H

        Text result;
        measure.get_info (result);

        DOUBLES_EQUAL (0., measure.last_dihedral_angle().deg(),  0.001);

        // Change angle
        measure.set_active (0, false);
        Unit const & unit = measure.parameter ();
        //TEST ("", unit.current_type() == "rad");
        unit.set_current_type ("rad");
        DOUBLES_EQUAL (2.094394, unit.value(), 0.00001);
        unit.set_current_type ("deg");
        DOUBLES_EQUAL (120.0000, unit.value(), 0.0001);

        Own <Unit>  new_parameter (unit.clone());
        new_parameter().set_value (90);
        measure.set_parameter (new_parameter());
        TEST ("", unit.current_type() == "deg");
        DOUBLES_EQUAL (90.0000, unit.value(), 0.00001);

        Angle_unit new_angle;
        new_angle.set_deg (110);
        new_angle.set_current_type ("deg");
        measure.set_parameter (new_angle);
        DOUBLES_EQUAL (110.0000, unit.value(), 0.00001);

        Project::singleton ().orphan (model());
    }

    void demo_mass_centers ()
    {
        Own<Model> model (prototype <Model>(). clone ());
        model().load (Path::tests() + "mc-mc.hin");
        Project::singleton ().adopt (&model());

        Measure_geometry_impl measure;
        measure.set_mass_center_type (0, 0);
        measure.set_mass_center_type (1, 1);

        Text result;
        measure.get_info (result);

        DOUBLES_EQUAL (sqrt(2.), measure.last_length().A(),  0.001);

        Project::singleton ().orphan (model());
    }
};

Measure_geometry_test test_measure_geometry ("correctness");

}//MM
