#include "Test.h"

#include "Joint.h"
#include "Arbitrary_atom_group.h"
#include "Atom_impl.h"

namespace MM
{

class Joint_test : public Test
{
    Joint *link;
public:
    Joint_test( const Text & suite_name ) : Test( suite_name ) { }
    
    void run()
    {
        test_chain_1   ();     count_tests();
        test_chain_2   ();     count_tests();
        test_chain_3   ();     count_tests();
    }

    void test_chain_1    ();
    void test_chain_2    ();
    void test_chain_3    ();
};

Joint_test test_joint ("correctness");

void Joint_test::
test_chain_1 ()
{
    // f3              f3           f3
    // |               |            |
    // f2 - f1  =>     f2 - f1  =>  f2 - f1
    // s3                           s1 - s2
    // |                                  |
    // s2 - s1    s1 - s2                s3
    //                  |
    //                 s3

    Atom_impl f1 ("H");     Atom_impl s1 ("H");
    Atom_impl f2 ("C");     Atom_impl s2 ("C");
    Atom_impl f3 ("C");     Atom_impl s3 ("C");

    Arbitrary_atom_group *f = new Arbitrary_atom_group;
    Arbitrary_atom_group *s = new Arbitrary_atom_group;
    f->add (f1);    f->add (f2);    f->add (f3);
    s->add (s1);    s->add (s2);    s->add (s3);

    f1.set_x (1);   f1.set_y (10);
                    f2.set_y (10);
    f3.set_y (11);

    s1.set_x (1);
    s3.set_y (1);

    Joint f_joint (f1.position(), f2.position(), f3.position(), f1, f2, f3, f);
    Joint s_joint (s1.position(), s2.position(), s3.position(), s1, s2, s3, s);
    f_joint.chain (s_joint, 1.);

    double 
    res = s1.x(); // -1
    res = s1.y(); //  0
    res = s1.z(); //  0
    res = s2.x(); //  0
    res = s2.y(); //  0
    res = s2.z(); //  0
    res = s3.x(); //  0
    res = s3.y(); // -1 
    res = s3.z(); //  0

    // First group does not move.
    DOUBLES_EQUAL (1.,  f1.x(), 0.000001);
    DOUBLES_EQUAL (10., f1.y(), 0.000001);
    DOUBLES_EQUAL (0.,  f1.z(), 0.000001);
    DOUBLES_EQUAL (0.,  f2.x(), 0.000001);
    DOUBLES_EQUAL (10., f2.y(), 0.000001);
    DOUBLES_EQUAL (0.,  f2.z(), 0.000001);
    DOUBLES_EQUAL (0.,  f3.x(), 0.000001);
    DOUBLES_EQUAL (11., f3.y(), 0.000001);
    DOUBLES_EQUAL (0.,  f3.z(), 0.000001);

    DOUBLES_EQUAL (0.,  s1.x(), 0.000001);
    DOUBLES_EQUAL (10., s1.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s1.z(), 0.000001);
    DOUBLES_EQUAL (1.,  s2.x(), 0.000001);
    DOUBLES_EQUAL (10., s2.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s2.z(), 0.000001);
    DOUBLES_EQUAL (1.,  s3.x(), 0.000001);
    DOUBLES_EQUAL (9.,  s3.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s3.z(), 0.000001);
}

void Joint_test::
test_chain_2 ()
{
    // f3           f3
    // |            |
    // f2 - f1  =>  f2 - f1
    //      s3      s1 - s2
    //      |             |
    // s1 - s2           s3

    Atom_impl f1 ("H");     Atom_impl s1 ("H");
    Atom_impl f2 ("C");     Atom_impl s2 ("C");
    Atom_impl f3 ("C");     Atom_impl s3 ("C");

    Arbitrary_atom_group *f = new Arbitrary_atom_group;
    Arbitrary_atom_group *s = new Arbitrary_atom_group;
    f->add (f1);    f->add (f2);    f->add (f3);
    s->add (s1);    s->add (s2);    s->add (s3);

    f1.set_x (1);   f1.set_y (10);
                    f2.set_y (10);
    f3.set_y (11);

    s2.set_x (1);
    s3.set_x (1);    s3.set_y (1);

    Joint f_joint (f1.position(), f2.position(), f3.position(), f1, f2, f3, f);
    Joint s_joint (s1.position(), s2.position(), s3.position(), s1, s2, s3, s);
    f_joint.chain (s_joint, 1.);

    DOUBLES_EQUAL (0.,  s1.x(), 0.000001);
    DOUBLES_EQUAL (10., s1.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s1.z(), 0.000001);
    DOUBLES_EQUAL (1.,  s2.x(), 0.000001);
    DOUBLES_EQUAL (10., s2.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s2.z(), 0.000001);
    DOUBLES_EQUAL (1.,  s3.x(), 0.000001);
    DOUBLES_EQUAL (9.,  s3.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s3.z(), 0.000001);
}

void Joint_test::
test_chain_3 ()
{
    // f3           f3
    // |            |
    // f2 - f1  =>  f2 - f1
    //      s1      s1 - s2
    //      |             |
    //   s3>s2           s3

    Atom_impl f1 ("H");     Atom_impl s1 ("H");
    Atom_impl f2 ("C");     Atom_impl s2 ("C");
    Atom_impl f3 ("C");     Atom_impl s3 ("C");

    Arbitrary_atom_group *f = new Arbitrary_atom_group;
    Arbitrary_atom_group *s = new Arbitrary_atom_group;
    f->add (f1);    f->add (f2);    f->add (f3);
    s->add (s1);    s->add (s2);    s->add (s3);

    f1.set_x (1);   f1.set_y (10);
                    f2.set_y (10);
    f3.set_y (11);

    s1.set_x (1);    s1.set_y (1);
    s2.set_x (1);
    s3.set_x (1);                       s3.set_z (1);

    Joint f_joint (f1.position(), f2.position(), f3.position(), f1, f2, f3, f);
    Joint s_joint (s1.position(), s2.position(), s3.position(), s1, s2, s3, s);
    f_joint.chain (s_joint, 1.);

    DOUBLES_EQUAL (0.,  s1.x(), 0.000001);
    DOUBLES_EQUAL (10., s1.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s1.z(), 0.000001);
    DOUBLES_EQUAL (1.,  s2.x(), 0.000001);
    DOUBLES_EQUAL (10., s2.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s2.z(), 0.000001);
    DOUBLES_EQUAL (1.,  s3.x(), 0.000001);
    DOUBLES_EQUAL (9.,  s3.y(), 0.000001);
    DOUBLES_EQUAL (0.,  s3.z(), 0.000001);
}

}//MM
