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

#include <ImfOutputFile.h>
#include <ImfInputFile.h>
#include <ImfChannelList.h>
#include <ImfStringAttribute.h>
#include <ImfMatrixAttribute.h>

#include <mia/core/file.hh>
#include <mia/core/filter.hh>
#include <mia/core/msgstream.hh>
#include <mia/2d/vfio.hh>



NS_BEGIN(EXRIO_VF)

NS_MIA_USE

using namespace std;
using namespace boost;
using namespace Imf;
using namespace Imath;

class CEXR2DVFIOPlugin : public C2DVFIOPlugin {
public:
	CEXR2DVFIOPlugin();
private:
	PData do_load(const string& fname) const;
	bool do_save(const string& fname, const Data& data) const;
	const string do_get_descr() const;
};

CEXR2DVFIOPlugin::CEXR2DVFIOPlugin():
	C2DVFIOPlugin("exr")
{
	add_supported_type(it_float);
	add_supported_type(it_uint);

	add_suffix(".exr");
	add_suffix(".EXR");
}

CEXR2DVFIOPlugin::PData  CEXR2DVFIOPlugin::do_load(const string& filename) const
{
	try {
		InputFile file (filename.c_str());

		Box2i dw = file.header().dataWindow();
		C2DBounds size(dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1);
		int dx = dw.min.x;
		int dy = dw.min.y;
		cvdebug() << "EXR:Get vf of size " <<size.x << ", "<< size.y << "\n";

		const ChannelList& channels = file.header().channels();
		FrameBuffer frameBuffer;

		std::shared_ptr<C2DIOVectorfield > vf(new C2DIOVectorfield(size));

		for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {

			const Channel& channel = i.channel();

			cvdebug() << "channel '"<< i.name() <<"' of type " << channel.type << "\n";
			switch (channel.type) {
			case FLOAT: {

				frameBuffer.insert ("X", Slice(FLOAT, (char*)(&(*vf)(0,0).x - (dx + size.x * dy)),
							       8, 8 * size.x, 1, 1, 0.0));
				frameBuffer.insert ("Y", Slice(FLOAT, (char*)(&(*vf)(0,0).y - (dx + size.x * dy)),
                                                                    8, 8 * size.x, 1, 1, 0.0));
			}break;
			default:
				throw invalid_argument("EXRVFIO::load: only FLOAT supported");
			};
		}
		file.setFrameBuffer (frameBuffer);
		file.readPixels (dw.min.y, dw.max.y);
		return vf;
	}

	catch (const std::exception& x) {
		// should add an debug message 
		cvwarn() << "OpenXER: failed reading vector field from '"<< filename<< "':" << x.what() << "\n"; 
	}
	return CEXR2DVFIOPlugin::PData();
}

bool CEXR2DVFIOPlugin::do_save(const string& fname, const Data& vf) const
{

	cvdebug() << "CEXR2DVFIOPlugin::do_save vf size ("<<
		vf.get_size().x << ", " << vf.get_size().y<<")\n";

	try {
		Header header (vf.get_size().x, vf.get_size().y);

		header.channels().insert ("X", Channel (FLOAT));
		header.channels().insert ("Y", Channel (FLOAT));

		OutputFile file (fname.c_str(), header);

		FrameBuffer frameBuffer;

		frameBuffer.insert ("X",                    // name
				    Slice (FLOAT,               // type
					   (char *) &vf(0,0).x, // base
					   8,           // xStride
					   8 * vf.get_size().x));     // yStride

		frameBuffer.insert ("Y",                         // name
				    Slice (FLOAT,                // type
					   (char *) &vf(0,0).y,  // base
					   8,                    // xStride
					   8 * vf.get_size().x));// yStride


		file.setFrameBuffer (frameBuffer);
		file.writePixels (vf.get_size().y);
	}
	catch (...) {
		return false;
	}

	return true;
}

const string CEXR2DVFIOPlugin::do_get_descr() const
{
	return "a 2dvf io plugin for OpenEXR vfs";
}

extern "C" EXPORT  CPluginBase *get_plugin_interface()
{
	return new CEXR2DVFIOPlugin();
}

NS_END

