#include "../../structures/mask2d.h"

#include "../../algorithms/morphologicalflagger.h"

#include <boost/test/unit_test.hpp>

using algorithms::MorphologicalFlagger;

BOOST_AUTO_TEST_SUITE(dilation, *boost::unit_test::label("algorithms"))

static std::string maskToString(Mask2DCPtr mask, bool flip) {
  std::stringstream s;
  if (flip) {
    for (unsigned x = 0; x < mask->Width(); ++x) {
      for (unsigned y = 0; y < mask->Height(); ++y)
        s << (mask->Value(x, y) ? 'x' : ' ');
      s << '\n';
    }
  } else {
    for (unsigned y = 0; y < mask->Height(); ++y) {
      for (unsigned x = 0; x < mask->Width(); ++x)
        s << (mask->Value(x, y) ? 'x' : ' ');
      s << '\n';
    }
  }
  return s.str();
}

static void setMask(Mask2DPtr mask, bool flip, const std::string& str) {
  std::string::const_iterator i = str.begin();
  if (flip) {
    for (unsigned x = 0; x < mask->Width(); ++x) {
      for (unsigned y = 0; y < mask->Height(); ++y) {
        mask->SetValue(x, y, (*i) == 'x');
        ++i;
      }
      ++i;  // newline
    }
  } else {
    for (unsigned y = 0; y < mask->Height(); ++y) {
      for (unsigned x = 0; x < mask->Width(); ++x) {
        mask->SetValue(x, y, (*i) == 'x');
        ++i;
      }
      ++i;  // newline
    }
  }
}

template <bool Flip, typename DilateFunction>
static void testDilation(DilateFunction dilate) {
  std::string expected;
  Mask2DPtr mask = Mask2D::CreateSetMaskPtr<false>(5, 5);
  dilate(mask.get(), 0);
  expected =
      "     \n"
      "     \n"
      "     \n"
      "     \n"
      "     \n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 1);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 2);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 3);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 4);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 5);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 6);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  expected =
      "     \n"
      "     \n"
      "  x  \n"
      "     \n"
      "     \n";
  setMask(mask, Flip, expected);
  dilate(mask.get(), 0);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  dilate(mask.get(), 1);
  expected =
      "     \n"
      "     \n"
      " xxx \n"
      "     \n"
      "     \n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  dilate(mask.get(), 1);
  expected =
      "     \n"
      "     \n"
      "xxxxx\n"
      "     \n"
      "     \n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 1);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
  dilate(mask.get(), 100);
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  setMask(mask, Flip,
          " x   \n"
          "   x \n"
          "x    \n"
          "    x\n"
          " x x \n");
  dilate(mask.get(), 2);
  expected =
      "xxxx \n"
      " xxxx\n"
      "xxx  \n"
      "  xxx\n"
      "xxxxx\n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  setMask(mask, Flip,
          "x    \n"
          " x   \n"
          "  x  \n"
          "   x \n"
          "    x\n");
  dilate(mask.get(), 6);
  expected =
      "xxxxx\n"
      "xxxxx\n"
      "xxxxx\n"
      "xxxxx\n"
      "xxxxx\n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  if (Flip)
    mask = Mask2D::CreateSetMaskPtr<false>(4, 6);
  else
    mask = Mask2D::CreateSetMaskPtr<false>(6, 4);
  dilate(mask.get(), 0);
  expected =
      "      \n"
      "      \n"
      "      \n"
      "      \n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  setMask(mask, Flip,
          "x     \n"
          "  x   \n"
          "    x \n"
          "     x\n");
  dilate(mask.get(), 4);
  expected =
      "xxxxx \n"
      "xxxxxx\n"
      "xxxxxx\n"
      " xxxxx\n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);

  setMask(mask, Flip,
          "x     \n"
          "      \n"
          "    x \n"
          "     x\n");
  dilate(mask.get(), 7);
  expected =
      "xxxxxx\n"
      "      \n"
      "xxxxxx\n"
      "xxxxxx\n";
  BOOST_CHECK_EQUAL(maskToString(mask, Flip), expected);
}

BOOST_AUTO_TEST_CASE(horizontal_dilation) {
  testDilation<false>(MorphologicalFlagger::DilateFlagsHorizontally);
}

BOOST_AUTO_TEST_CASE(vertical_dilation) {
  testDilation<true>(MorphologicalFlagger::DilateFlagsVertically);
}

BOOST_AUTO_TEST_SUITE_END()
