/* -*- 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/2d/filter/mlv.hh>

NS_MIA_USE
using namespace std;
using namespace ::boost;
using namespace ::boost::unit_test;
using namespace mlv_2dimage_filter;

BOOST_AUTO_TEST_CASE( test_mlv )
{
#ifndef WIN32
       // deterministic test, aleays use the same seed
       srand48(0);
#endif

       for (int width = 2; width < 12; ++width) {
              int w = width / 2;
              cvdebug() << "test filter of width " << w << "\n";
              ostringstream filter_descr;
              filter_descr << "mlv:w=" << w;
              auto filter = BOOST_TEST_create_from_plugin<C2DMLVImageFilterFactory>(filter_descr.str().c_str());
              int isize = 4 * w + 1;
              int fwidth = 2 * w;
              C2DBounds size(isize, isize);
              C2DFImage *src = new C2DFImage(size);

              for (C2DFImage::iterator i = src->begin(), e = src->end(); i != e; ++i)
#ifndef WIN32
                     *i = drand48();

#else
                     *i = 1.0f; // windows doesnt have drand48
#endif
              // create the reference image
              C2DFImage ref(src->get_size());
              const C2DBounds exsize(isize + fwidth, isize + fwidth);
              C2DFImage sigma(exsize);
              C2DFImage mu(exsize);

              for (int y = 0; y < isize + fwidth; ++y) {
                     for (int x = 0; x < isize + fwidth; ++x) {
                            float sum  = 0.0f;
                            float sum2 = 0.0f;
                            int n = 0;

                            for (int iy = max(0, y - fwidth); iy < min(isize, y + 1); ++iy)
                                   for (int ix = max(0, x - fwidth); ix < min(isize, x + 1); ++ix) {
                                          float const& val = (*src)(ix, iy);
                                          sum += val;
                                          sum2 += val * val;
                                          ++n;
                                   }

                            float m = n > 0 ? mu(x, y) = sum / n : 0;
                            sigma(x, y) = (n > 1) ? (sum2 - n * m * m) / (n - 1) : 0.0f;
                     }
              }

              for (int y = 0; y < isize; ++y) {
                     for (int x = 0; x < isize; ++x) {
                            float best_mu = 0.0;
                            float best_sigma = numeric_limits<float>::max();

                            for (int iy = y; iy < y + fwidth + 1; ++iy)
                                   for (int ix = x; ix < x + fwidth + 1; ++ix) {
                                          float mu_val = mu(ix, iy);

                                          if (best_sigma > sigma(ix, iy) ) {
                                                 best_sigma = sigma(ix, iy);
                                                 best_mu = mu_val;
                                          } else if (best_sigma == sigma(ix, iy) && best_mu != mu_val ) {
                                                 const auto ref = (*src)(x, y);

                                                 if (::fabs(mu_val - ref) < ::fabs(best_mu - ref))
                                                        best_mu = mu_val;
                                          }
                                   }

                            ref(x, y) = best_mu;
                     }
              }

              P2DImage src_wrap(src);
              P2DImage res_wrap = filter->filter(*src_wrap);
              C2DFImage *res = dynamic_cast<C2DFImage *>(res_wrap.get());
              BOOST_REQUIRE(res);
              BOOST_CHECK_EQUAL(res->get_size(), src->get_size());
              BOOST_REQUIRE(res->get_size() == src->get_size());

              for (auto ires = res->begin(), iref = ref.begin();
                   ires != res->end(); ++ires, ++iref) {
                     BOOST_CHECK_CLOSE(*ires, *iref, 0.1);
              }

              // redo to test 2nd code path
              res_wrap = filter->filter(*src_wrap);
              auto res2 = dynamic_cast<const C2DFImage&>(*res_wrap);

              for (auto ires = res2.begin(), iref = ref.begin();
                   ires != res2.end(); ++ires, ++iref) {
                     BOOST_CHECK_CLOSE(*ires, *iref, 0.1);
              }
       }
}
