/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <mia/internal/plugintester.hh>
#include <mia/mesh/filter/vtxsort.hh>

using namespace mia;
using namespace mia_meshfilter_vtxsort;

class  MeshVtxsortFixture
{
protected:
       MeshVtxsortFixture();


       void test_expected(const CTriangleMesh& changed, const CTriangleMesh& test) const;

       typedef CTriangleMesh::CTrianglefield::value_type Triangle;
       typedef CTriangleMesh::CColorfield::value_type Color;
       CTriangleMesh mesh;
};

class MeshVtxsortWithAttrFixture : public MeshVtxsortFixture
{
protected:
       MeshVtxsortWithAttrFixture();
};

BOOST_FIXTURE_TEST_CASE( test_simple_mesh_z_order, MeshVtxsortFixture)
{
       auto testv = CTriangleMesh::PVertexfield(new CTriangleMesh::CVertexfield({
              C3DFVector(3, 4, 1), C3DFVector(5, 3, 3),
              C3DFVector(2, 1, 4), C3DFVector(1, 5, 8)
       }));
       auto testt = CTriangleMesh::PTrianglefield(new CTriangleMesh::CTrianglefield({
              Triangle(3, 1, 0), Triangle(1, 0, 2),
              Triangle(3, 0, 2), Triangle(3, 2, 1)
       }));
       CTriangleMesh test(testt, testv);
       auto scale = BOOST_TEST_create_from_plugin<CVtxSortMeshFilterPlugin>("vtxsort:dir=[<0,0,1>]");
       auto filtered_mesh = scale->filter(mesh);
       test_expected(*filtered_mesh, test);
}

BOOST_FIXTURE_TEST_CASE( test_simple_mesh_y_order, MeshVtxsortFixture)
{
       auto testv = CTriangleMesh::PVertexfield(new CTriangleMesh::CVertexfield({
              C3DFVector(2, 1, 4), C3DFVector(5, 3, 3), C3DFVector(3, 4, 1), C3DFVector(1, 5, 8)
       }));
       auto testt = CTriangleMesh::PTrianglefield(new CTriangleMesh::CTrianglefield({
              Triangle(3, 1, 2), Triangle(1, 2, 0), Triangle(3, 2, 0), Triangle(3, 0, 1)
       }));
       CTriangleMesh test(testt, testv);
       auto scale = BOOST_TEST_create_from_plugin<CVtxSortMeshFilterPlugin>("vtxsort:dir=[<0,1,0>]");
       auto filtered_mesh = scale->filter(mesh);
       test_expected(*filtered_mesh, test);
}

BOOST_FIXTURE_TEST_CASE( test_simple_mesh_x_order, MeshVtxsortFixture)
{
       auto testv = CTriangleMesh::PVertexfield(new CTriangleMesh::CVertexfield({
              C3DFVector(1, 5, 8), C3DFVector(2, 1, 4), C3DFVector(3, 4, 1), C3DFVector(5, 3, 3)
       }));
       auto testt = CTriangleMesh::PTrianglefield(new CTriangleMesh::CTrianglefield({
              Triangle(0, 3, 2), Triangle(3, 2, 1), Triangle(0, 2, 1), Triangle(0, 1, 3)
       }));
       CTriangleMesh test(testt, testv);
       auto scale = BOOST_TEST_create_from_plugin<CVtxSortMeshFilterPlugin>("vtxsort:dir=[<1,0,0>]");
       auto filtered_mesh = scale->filter(mesh);
       test_expected(*filtered_mesh, test);
}

BOOST_FIXTURE_TEST_CASE( test_simple_mesh_comb, MeshVtxsortFixture)
{
       auto testv = CTriangleMesh::PVertexfield(new CTriangleMesh::CVertexfield({
              C3DFVector(1, 5, 8), C3DFVector(2, 1, 4), C3DFVector(3, 4, 1),  C3DFVector(5, 3, 3),
       }));
       auto testt = CTriangleMesh::PTrianglefield(new CTriangleMesh::CTrianglefield({
              Triangle(0, 3, 2), Triangle(3, 2, 1), Triangle(0, 2, 1), Triangle(0, 1, 3)
       }));
       CTriangleMesh test(testt, testv);
       auto scale = BOOST_TEST_create_from_plugin<CVtxSortMeshFilterPlugin>("vtxsort:dir=[<2,-1,-2>]");
       auto filtered_mesh = scale->filter(mesh);
       test_expected(*filtered_mesh, test);
}

BOOST_FIXTURE_TEST_CASE( test_mesh_z_order_with_attr, MeshVtxsortWithAttrFixture)
{
       auto testv = CTriangleMesh::PVertexfield(new CTriangleMesh::CVertexfield({
              C3DFVector(3, 4, 1), C3DFVector(5, 3, 3),
              C3DFVector(2, 1, 4), C3DFVector(1, 5, 8)
       }));
       auto testt = CTriangleMesh::PTrianglefield(new CTriangleMesh::CTrianglefield({
              Triangle(3, 1, 0), Triangle(1, 0, 2),
              Triangle(3, 0, 2), Triangle(3, 2, 1)
       }));
       auto testn = CTriangleMesh::PNormalfield(new CTriangleMesh::CNormalfield({
              C3DFVector(0, 0, 3), C3DFVector(0, 0, 2),
              C3DFVector(0, 0, 4), C3DFVector(0, 0, 1)
       }));
       auto testc = CTriangleMesh::PColorfield(new CTriangleMesh::CColorfield({
              Color(1, 0, 0), Color(0, 1, 0), Color(1, 1, 0), Color(0, 0, 1)
       }));
       auto tests = CTriangleMesh::PScalefield(new CTriangleMesh::CScalefield({3, 2, 4, 1}));
       CTriangleMesh test(testt, testv, testn, testc, tests);
       auto scale = BOOST_TEST_create_from_plugin<CVtxSortMeshFilterPlugin>("vtxsort:dir=[<0,0,1>]");
       auto filtered_mesh = scale->filter(mesh);
       test_expected(*filtered_mesh, test);
}


MeshVtxsortFixture::MeshVtxsortFixture(): mesh(4, 4)
{
       CTriangleMesh::CVertexfield vertices{
              C3DFVector(1, 5, 8), C3DFVector(5, 3, 3), C3DFVector(3, 4, 1), C3DFVector(2, 1, 4)};
       typedef T3DVector<unsigned int> Triangle;
       CTriangleMesh::CTrianglefield triangles{
              Triangle(0, 1, 2), Triangle(1, 2, 3), Triangle(0, 2, 3), Triangle(0, 3, 1)};
       copy(vertices.begin(), vertices.end(), mesh.vertices_begin());
       copy(triangles.begin(), triangles.end(), mesh.triangles_begin());
}

MeshVtxsortWithAttrFixture::MeshVtxsortWithAttrFixture()
{
       CTriangleMesh::CNormalfield nfield({C3DFVector(0, 0, 1), C3DFVector(0, 0, 2),
                                           C3DFVector(0, 0, 3), C3DFVector(0, 0, 4)
                                          });
       CTriangleMesh::CColorfield cfield({Color(0, 0, 1), Color(0, 1, 0), Color(1, 0, 0), Color(1, 1, 0)});
       CTriangleMesh::CScalefield sfield({1, 2, 3, 4});
       copy(nfield.begin(), nfield.end(), mesh.normals_begin());
       copy(cfield.begin(), cfield.end(), mesh.color_begin());
       copy(sfield.begin(), sfield.end(), mesh.scale_begin());
}

void MeshVtxsortFixture::test_expected(const CTriangleMesh& changed, const CTriangleMesh& test) const
{
       BOOST_CHECK_EQUAL(changed.vertices_size(), test.vertices_size());
       BOOST_CHECK_EQUAL(changed.triangle_size(), test.triangle_size());
       BOOST_CHECK_EQUAL(changed.get_available_data(), test.get_available_data());

       for (auto ic = changed.vertices_begin(), it = test.vertices_begin();
            ic != changed.vertices_end(); ++ic, ++it) {
              BOOST_CHECK_EQUAL(*ic, *it);
       }

       for (auto ic = changed.triangles_begin(), it = test.triangles_begin();
            ic != changed.triangles_end(); ++ic, ++it) {
              BOOST_CHECK_EQUAL(*ic, *it);
       }

       if (test.get_available_data() & CTriangleMesh::ed_normal) {
              for (auto ic = changed.normals_begin(), it = test.normals_begin();
                   ic != changed.normals_end(); ++ic, ++it) {
                     BOOST_CHECK_EQUAL(*ic, *it);
              }
       }

       if (test.get_available_data() & CTriangleMesh::ed_color) {
              for (auto ic = changed.color_begin(), it = test.color_begin();
                   ic != changed.color_end(); ++ic, ++it) {
                     BOOST_CHECK_EQUAL(*ic, *it);
              }
       }

       if (test.get_available_data() & CTriangleMesh::ed_scale) {
              for (auto ic = changed.scale_begin(), it = test.scale_begin();
                   ic != changed.scale_end(); ++ic, ++it) {
                     BOOST_CHECK_EQUAL(*ic, *it);
              }
       }
}


