/* -*- 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 <iterator>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <ostream>
#include <cmath>
#include <list>
#include <cassert>
#include <boost/filesystem.hpp>

#include <mia/core.hh>
#include <mia/internal/main.hh>
#include <mia/2d/segsetwithimages.hh>
#include <mia/2d/imageio.hh>
#include <mia/2d/filter.hh>

#include <numeric> 

using namespace std;
using namespace mia;
namespace bfs=boost::filesystem;

const SProgramDescription g_description = {
        {pdi_group, "Tools for Myocardial Perfusion Analysis"}, 
	{pdi_short, "Evaluate mean distance between segmentations."}, 
	{pdi_description, "Get the mean distance of a segmentation boundary to the reference boundary."}, 
	{pdi_example_descr, "Evaluate the mean absolute border distanceof the segmentations "
	 "of set segment.set with respect to the segmentation given in frame 20."}, 
	{pdi_example_code, " -i segment.set -r 20"}
}; 
	

double mean_frame_border_distance(const C2DDImage& distance, const C2DBitImage& mask) 
{
	double result = 0.0; 
	int n = 0; 
	auto im = mask.begin(); 
	auto em = mask.end(); 
	auto id = distance.begin(); 

	while (im != em) {
		if (*im) {
			result += *id * *id; 
			++n; 
		}
		++id; 
		++im; 
	}
	return n ? sqrt(result / n) : 0.0; 
}

C2DBitImage get_boundary(const C2DUBImage& mask) 
{
	auto orig_mask = run_filter_chain(P2DImage(mask.clone()), {"binarize:min=1", "close:shape=8n"}); 
	auto enlarged_mask = run_filter(*orig_mask, "dilate:shape=8n");
	
	const C2DBitImage& om = dynamic_cast<const C2DBitImage&>(*orig_mask); 
	const C2DBitImage& em = dynamic_cast<const C2DBitImage&>(*enlarged_mask); 
	

	C2DBitImage result(om.get_size());
	transform(em.begin(), em.end(), om.begin(), result.begin(), 
		  [](bool enlarged, bool orig) {
			  return enlarged && !orig;
		  }); 
	return result; 
}

P2DImage get_distance_transform(const C2DImage& image) 
{
	return run_filter(image, "distance"); 
}

int do_main(int argc, char *argv[])
{
	string src_filename;
	size_t reference = 20;
	int  skip = 0; 

	CCmdOptionList options(g_description);
	options.add(make_opt( src_filename, "in-file", 'i', "input segmentation set", CCmdOptionFlags::required_input));
	options.add(make_opt( reference, "reference", 'r', "reference frame"));
	options.add(make_opt( skip, "skip", 'k', "skip images at the beginning"));
	
	options.set_stdout_is_result();
	
	if (options.parse(argc, argv) != CCmdOptionList::hr_no)
		return EXIT_SUCCESS; 

	CSegSetWithImages srcset(src_filename, true);

	if (skip < 0) {
                // if RV peak is given in the segmentation file, use it, otherwiese use 
		// absolue value of skip 
		int sk = srcset.get_RV_peak(); 
		skip = (sk < 0 ) ? -skip : sk; 
	}

	const auto& src_frames = srcset.get_frames();

	if (reference  >=  src_frames.size())
		throw invalid_argument("The requested reference frame is larger then the size of the set."); 
	
	if (skip >= (int)src_frames.size()) 
		throw create_exception<invalid_argument>( "Try to skip ", skip, " frames, but series has only ", src_frames.size()); 

	auto isrc_frame = src_frames.begin() + skip;
	auto esrc_frame = src_frames.end();

	auto reference_boundary = get_boundary(src_frames[reference].get_section_masks(1)); 
	P2DImage img(reference_boundary.clone()); 
	auto dtf = get_distance_transform(reference_boundary); 
	const C2DDImage& fdtf = dynamic_cast<const C2DDImage&>(*dtf); 
	
	vector<double> mean_frame_dist(src_frames.size() - skip); 
	auto im = mean_frame_dist.begin(); 
	
	int k = skip; 
	while (isrc_frame != esrc_frame) {
		auto boundary = get_boundary(isrc_frame->get_section_masks(1));
		*im = mean_frame_border_distance(fdtf, boundary); 
		cvmsg() << k << ": " << *im << "\n"; 
		++isrc_frame; 
		++im; 
		++k; 
	}
	double mean = accumulate(mean_frame_dist.begin(), mean_frame_dist.end(), 0.0) / mean_frame_dist.size(); 
	cout << mean << "\n"; 
	return 0;
}

MIA_MAIN(do_main);
