/* -*- 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 <climits>

#include <boost/test/unit_test_suite.hpp>
#include <boost/test/unit_test.hpp>

#include <mia/core/msgstream.hh>
#include <mia/core/history.hh>
#include <mia/2d/imageio.hh>
#include <mia/core/filter.hh>


using namespace std;
using namespace boost::unit_test;

NS_MIA_BEGIN
typedef C2DImageIOPluginHandler::Instance::Interface Interface;




class CImageCompare: public TFilter<bool>  {
public:
	template <typename  T>
	CImageCompare::result_type operator()(const T2DImage<T>& a, const T2DImage<T>& b) const
	{
		return equal(a.begin(), a.end(), b.begin());
	}
};

static bool is_equal(const C2DImage& a, const C2DImage& b)
{
	BOOST_REQUIRE(a.get_pixel_type() == b.get_pixel_type());
	BOOST_REQUIRE(a.get_size() == b.get_size());

	CImageCompare compare;

	return filter_equal(compare, a, b);
}

static void test_attributes_equal(const CAttributedData& a, const CAttributedData& b)
{
	BOOST_CHECK(a == b);
}

static void check_save_load(const C2DImageVector& images, const Interface& imgio)
{
	const string format = imgio.get_name();
	const string tmp_name = string("imgtest") + string(".") + format;

	cvdebug() << format << ":" << tmp_name << endl;

	BOOST_REQUIRE(imgio.save(tmp_name.c_str(), images));

	cvdebug() << format << "saved" << std::endl;

	std::shared_ptr<C2DImageVector > reread(imgio.load(tmp_name.c_str()));

	BOOST_REQUIRE(reread.get());
	cvdebug() << images.size() << " vs. " << reread->size() << "\n";
	BOOST_REQUIRE(images.size() == reread->size());

	C2DImageVector::const_iterator in_i = images.begin();
	C2DImageVector::const_iterator in_e = images.end();

	C2DImageVector::const_iterator lo_i = reread->begin();

	for (; in_i != in_e; ++in_i, ++lo_i) {
		BOOST_REQUIRE(is_equal(**in_i, **lo_i));
		if (imgio.has_property(io_plugin_property_has_attributes)) {
			test_attributes_equal(**in_i, **lo_i);
		}

	}

	unlink(tmp_name.c_str());
	cvdebug() << tmp_name << " unlinked\n";
}


template <typename T>
static P2DImage create_image()
{
	T2DImage<T> *r = new T2DImage<T>(C2DBounds(13,19));
	int k = 0;
	for(typename T2DImage<T>::iterator i = r->begin(); i != r->end(); ++i, ++k)
		*i = T(k);

	r->set_pixel_size(C2DFVector(1.5f, 4.6f));
	r->set_attribute("some", PAttribute(new TAttribute<string>("some text")));

	return  P2DImage(r);
}

template <>
P2DImage create_image<bool>()
{
	C2DBitImage *r = new C2DBitImage(C2DBounds(13,19));
	for(C2DBitImage::iterator i = r->begin(); i != r->end(); ++i)
#ifdef WIN32
			*i =  rand() > RAND_MAX/2;
#else
			*i =  drand48() > 0.5;
#endif

	r->set_pixel_size(C2DFVector(1.5f, 4.6f));
	r->set_attribute("some", PAttribute(new TAttribute<string>("some other text")));
	return  P2DImage(r);
}

static P2DImage create_image_of_type(EPixelType type)
{
	switch (type) {
	case it_bit:    return create_image<bool>();
	case it_sbyte:  return create_image<int8_t>();
	case it_ubyte:  return create_image<uint8_t>();
	case it_sshort: return create_image<int16_t>();
	case it_ushort: return create_image<uint16_t>();
	case it_sint:   return create_image<int32_t>();
	case it_uint:   return create_image<uint32_t>();
	case it_slong:  return create_image<int64_t>();
	case it_ulong:  return create_image<uint64_t>();
	case it_float:  return create_image<float>();
	case it_double: return create_image<double>();
	default:
		BOOST_FAIL("IO plugin supports more types then we know of");
		return P2DImage();
	}
}



static void test_multi(const Interface & plugin)
{
	C2DImageVector src_list;
	Interface::PixelTypeSet pixeltypes = plugin.supported_pixel_types();

	for(set<EPixelType>::const_iterator i = pixeltypes.begin();
	    i != pixeltypes.end(); ++i) {
		src_list.push_back(create_image_of_type(*i));
	}
	check_save_load(src_list, plugin);
}

static void test_single(const Interface & plugin)
{
	cvdebug() << "test_imageio_plugin_single:" << plugin.get_name() << "\n";

	Interface::PixelTypeSet pixeltypes = plugin.supported_pixel_types();

	for(set<EPixelType>::const_iterator i = pixeltypes.begin();
	    i != pixeltypes.end(); ++i) {
		C2DImageVector src_list;
		src_list.push_back(create_image_of_type(*i));
		check_save_load(src_list, plugin);
	}

	cvdebug() << "test_imageio_plugin_single:" << plugin.get_name() << " done\n";
}

void EXPORT_2DTEST test_2dimageio_plugins()
{
	const C2DImageIOPluginHandler::Instance& plugins = C2DImageIOPluginHandler::instance();

	for (C2DImageIOPluginHandler::Instance::const_iterator i = plugins.begin();
	     i != plugins.end(); ++i) {

		if (i->second->has_property(io_plugin_property_multi_record))
			test_multi(*i->second);
		else
			test_single(*i->second);
	}
}

NS_MIA_END
