/* -*- 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/autotest.hh>
#include <mia/3d/filter/morphological.hh>

NS_MIA_USE
using namespace std;
using namespace ::boost::unit_test;
namespace bfs = boost::filesystem;
using namespace morph_3dimage_filter;

void SetPluginpath()
{
}

struct SetPathFixture {
       SetPathFixture()
       {
              SetPluginpath();
       }
};

struct DilateTestFixture: SetPathFixture  {

       void do_test_bit(const C3DShape::Mask& mask, const C3DShape::Size& size,
                        P3DShape shape, bool hint) const
       {
              C3DBitImage *src = new C3DBitImage(C3DBounds(size));
              fill(src->begin(), src->end(), false);
              (*src)(size / 2) = true;
              P3DImage src_wrap(src);
              C3DDilate dilate( shape, hint );
              P3DImage result = dilate.filter(*src_wrap);
              BOOST_CHECK_EQUAL(src_wrap->get_size(), result->get_size());
              C3DBitImage *presult = dynamic_cast<C3DBitImage *>(result.get());
              BOOST_REQUIRE(presult);
              BOOST_CHECK(equal(presult->begin(), presult->end(), mask.begin()));
       }

       template <typename T>
       void do_test_T(const C3DShape::Mask& /*mask*/, const C3DShape::Size& size, P3DShape shape) const
       {
              T3DImage<T> *src = new T3DImage<T>(C3DBounds(size));
              typename T3DImage<T>::iterator i_src = src->begin();

              for (size_t z = 0; z < src->get_size().z; ++z)
                     for (size_t y = 0; y < src->get_size().y; ++y)
                            for (size_t x = 0; x < src->get_size().x; ++x, ++i_src) {
                                   *i_src = x + 2 * y + 3 * z;
                            }

              P3DImage src_wrap(src);
              C3DDilate dilate( shape, false );
              P3DImage result = dilate.filter(*src_wrap);
              BOOST_CHECK_EQUAL(src_wrap->get_size(), result->get_size());
              T3DImage<T> *presult = dynamic_cast< T3DImage<T> *> (result.get());
              BOOST_REQUIRE(presult);
              T3DImage<T> ref = T3DImage<T>(C3DBounds(size));
              typename T3DImage<T>::iterator ref_i = ref.begin();
              i_src = src->begin();

              for (size_t z = 0; z < src->get_size().z; ++z)
                     for (size_t y = 0; y < src->get_size().y; ++y)
                            for (size_t x = 0; x < src->get_size().x; ++x, ++ref_i, ++i_src) {
                                   *ref_i = *i_src;

                                   for (auto si = shape->begin(),  se = shape->end(); si != se; ++si) {
                                          C3DBounds h(x + si->x, y + si->y, z + si->z);

                                          if (h < src->get_size()) {
                                                 T val = (*src)(h);

                                                 if (*ref_i < val)
                                                        *ref_i = val;
                                          }
                                   }
                            }

              BOOST_CHECK(equal(ref.begin(), ref.end(), presult->begin()));
       }
};


BOOST_FIXTURE_TEST_CASE( test_dilate, DilateTestFixture)
{
       cvdebug() << "C3DDilateStackFilterFactory::do_test() \n";
       CParsedOptions options;
       const C3DShapePluginHandler::Instance& sh = C3DShapePluginHandler::instance();
       BOOST_REQUIRE(sh.size());

       for (C3DShapePluginHandler::Instance::const_iterator shb = sh.begin();
            shb != sh.end(); ++shb) {
              P3DShape shape(shb->second->create(options, ""));
              cvdebug() << "created shape " << shb->first << "\n";
              C3DShape::Mask mask = shape->get_mask();
              C3DShape::Size size = shape->get_size();
#define TEST_TYPE(TYPE, TEXT) cvdebug() << TEXT"\n"; \
		do_test_T<TYPE>(mask, size, shape)
              TEST_TYPE(unsigned short, "unsigned short");
              TEST_TYPE(signed short, "signed short");
              TEST_TYPE(unsigned int, "unsigned int");
              TEST_TYPE(signed int, "signed int");
              TEST_TYPE(float, "float");
              TEST_TYPE(double, "double");
              TEST_TYPE(unsigned char, "unsigned char");
              TEST_TYPE(signed char, "signed char");
#undef TEST_TYPE
              // test a bit image
              do_test_bit(mask, size, shape, false);
              do_test_bit(mask, size, shape, true);
       }
}


struct ErodeTestFixture: SetPathFixture {


       void  do_test_bit(const C3DShape::Mask& mask, const C3DShape::Size& size,
                         P3DShape shape, bool hint) const
       {
              C3DBitImage *src = new C3DBitImage(C3DBounds(size));
              fill(src->begin(), src->end(), true);
              (*src)(size / 2) = false;
              P3DImage src_wrap(src);
              C3DErode erode( shape, hint );
              P3DImage result = erode.filter(*src_wrap);
              BOOST_CHECK_EQUAL(src->get_size(), result->get_size());
              C3DBitImage *presult = dynamic_cast<C3DBitImage *>(result.get());
              BOOST_REQUIRE(presult);
              transform(mask.begin(), mask.end(), src->begin(), logical_not<bool>());
              BOOST_CHECK(equal(presult->begin(), presult->end(), src->begin()));
       }

       template <typename T>
       void do_test_T(const C3DShape::Mask& /*mask*/, const C3DShape::Size& size, P3DShape shape) const
       {
              T3DImage<T> *src = new T3DImage<T>(C3DBounds(size));
              typename T3DImage<T>::iterator i_src = src->begin();

              for (size_t z = 0; z < src->get_size().z; ++z)
                     for (size_t y = 0; y < src->get_size().y; ++y)
                            for (size_t x = 0; x < src->get_size().x; ++x, ++i_src) {
                                   *i_src = x + 2 * y + 3 * z;
                            }

              P3DImage src_wrap(src);
              C3DErode erode( shape, false );
              P3DImage result = erode.filter(*src_wrap);
              BOOST_CHECK_EQUAL(src->get_size(), result->get_size());
              T3DImage<T> *presult = dynamic_cast< T3DImage<T> *> (result.get());
              BOOST_REQUIRE(presult);
              T3DImage<T> ref = T3DImage<T>(C3DBounds(size));
              typename T3DImage<T>::iterator ref_i = ref.begin();
              i_src = src->begin();

              for (size_t z = 0; z < src->get_size().z; ++z)
                     for (size_t y = 0; y < src->get_size().y; ++y)
                            for (size_t x = 0; x < src->get_size().x; ++x, ++ref_i, ++i_src) {
                                   *ref_i = *i_src;

                                   for (auto si = shape->begin(),
                                        se = shape->end(); si != se; ++si) {
                                          C3DBounds h(x + si->x, y + si->y, z + si->z);

                                          if (h < src->get_size()) {
                                                 T val = (*src)(h);

                                                 if (*ref_i > val)
                                                        *ref_i = val;
                                          }
                                   }
                            }

              BOOST_CHECK(equal(ref.begin(), ref.end(), presult->begin()));
       }
};


BOOST_FIXTURE_TEST_CASE( test_erode, ErodeTestFixture)
{
       cvdebug() << "C3DErodeStackFilterFactory::do_test() \n";
       CParsedOptions options;
       const C3DShapePluginHandler::Instance& sh = C3DShapePluginHandler::instance();
       BOOST_REQUIRE(sh.size());

       for (C3DShapePluginHandler::Instance::const_iterator shb = sh.begin();
            shb != sh.end(); ++shb) {
              P3DShape shape(shb->second->create(options, ""));
              cvdebug() << "created shape " << shb->first << "\n";
              C3DShape::Mask mask = shape->get_mask();
              C3DShape::Size size = shape->get_size();
#define TEST_TYPE(TYPE, TEXT) cvdebug() << TEXT"\n";		\
		do_test_T<TYPE>(mask, size, shape)
              TEST_TYPE(unsigned short, "unsigned short");
              TEST_TYPE(signed short, "signed short");
              TEST_TYPE(unsigned int, "unsigned int");
              TEST_TYPE(signed int, "signed int");
              TEST_TYPE(float, "float");
              TEST_TYPE(double, "double");
              TEST_TYPE(unsigned char, "unsigned char");
              TEST_TYPE(signed char, "signed char");
#undef	TEST_TYPE
              // test a bit image
              do_test_bit(mask, size, shape, false);
              do_test_bit(mask, size, shape, true);
       }
}
