/* -*- 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 <iomanip>
#include <ostream>
#include <fstream>
#include <map>
#include <boost/filesystem.hpp>


#include <queue>

#include <mia/core.hh>
#include <mia/2d/imageio.hh>
#include <mia/2d/filter.hh>
#include <mia/2d/ica.hh>
#include <mia/2d/segsetwithimages.hh>
#include <mia/2d/perfusion.hh>
#include <mia/2d/transformfactory.hh>
NS_MIA_USE;

const SProgramDescription g_description = {
       {pdi_group, "Work in progress"},
       {pdi_short, "Work in progress."},
       {pdi_description, "This program is work in progress"},
       {pdi_example_descr, "Example"},
       {pdi_example_code, "Example"}
};



namespace bfs = boost::filesystem;

class C2DFImage2PImage
{
public:
       P2DImage operator () (const C2DFImage& image) const
       {
              return P2DImage(new C2DFImage(image));
       }
};

void add_feature_mask(P2DImage feature_image, int id, C2DUBImage& mask)
{
       const char *filters[5] = {
              "kmeans:c=5",
              "binarize:min=4,max=4",
              "close:shape=[sphere:r=4]",
              "label",
              "selectbig"
       };
       // run cmeans + binarize + get largest connected component
       auto feature_mask = run_filter_chain(feature_image, 5, filters);
       auto bitmask = dynamic_cast<const C2DBitImage&>(*feature_mask);
       assert(bitmask.get_size() == mask.get_size());
       transform(bitmask.begin(), bitmask.end(), mask.begin(), mask.begin(),
       [id](bool feature, unsigned char m) {
              return feature ? id : m;
       });
}


int do_main( int argc, char *argv[] )
{
       // IO parameters
       string in_filename;
       string out_filename;
       // this parameter is currently not exported - reading the image data is
       // therefore done from the path given in the segmentation set
       bool override_src_imagepath = true;
       // ICA parameters
       size_t components = 0;
       bool normalize = false;
       bool no_meanstrip = false;
       size_t skip_images = 0;
       size_t max_ica_iterations = 400;
       PIndepCompAnalysisFactory icatool;
       int reference = -1;
       int nsegments = 12;
       CCmdOptionList options(g_description);
       options.set_group("File-IO");
       options.add(make_opt( in_filename, "in-file", 'i', "input perfusion data set", CCmdOptionFlags::required_input));
       options.add(make_opt( out_filename, "out-file", 'o', "output file with curves", CCmdOptionFlags::required_output));
       options.set_group("ICA");
       options.add(make_opt( icatool, "internal", "fastica", 0, "FastICA implementationto be used"));
       options.add(make_opt( components, "components", 'C', "ICA components 0 = automatic estimation"));
       options.add(make_opt( normalize, "normalize", 0, "normalized ICs"));
       options.add(make_opt( no_meanstrip, "no-meanstrip", 0,
                             "don't strip the mean from the mixing curves"));
       options.add(make_opt( skip_images, "skip", 'k', "skip images at the beginning of the series "
                             "as they are of other modalities"));
       options.add(make_opt( max_ica_iterations, "max-ica-iter", 'm', "maximum number of iterations in ICA"));
       options.set_group("Evaluation");
       options.add(make_opt( reference, "reference", 'r', "reference frame for curve mask (-1 = use LV peak)"));
       options.add(make_opt( nsegments, "nsegments", 'n', "number of myocardial segments to use (0=use segmented)"));

       if (options.parse(argc, argv) != CCmdOptionList::hr_no)
              return EXIT_SUCCESS;

       // load input data set
       CSegSetWithImages  input_set(in_filename, override_src_imagepath);
       C2DImageSeries input_images = input_set.get_images();
       vector<C2DFImage> series(input_images.size() - skip_images);
       transform(input_images.begin() + skip_images, input_images.end(),
                 series.begin(), Convert2Float());
       // run ICA
       C2DPerfusionAnalysis ica(components, normalize, !no_meanstrip);

       if (max_ica_iterations)
              ica.set_max_ica_iterations(max_ica_iterations);

       if (!ica.run(series)) {
              ica.set_approach(FICA_APPROACH_SYMM);
              ica.run(series);
       }

       if (reference == -1)
              reference = ica.get_LV_peak_time();

       if (reference == -1)
              throw runtime_error("LV peak time based reference requested, but it can't be identified");

       const auto& ref = input_set.get_frames()[reference];
       C2DUBImage mask = ref.get_section_masks(nsegments);
       int rv_id = nsegments > 0 ? nsegments + 1 : ref.get_nsections();
       int lv_id = rv_id + 1;
       int rv_idx = ica.get_RV_peak_idx();

       if (rv_idx < 0)
              throw runtime_error(" no RV feature identified");

       add_feature_mask(ica.get_feature_image(rv_idx), rv_id, mask);
       int lv_idx = ica.get_LV_peak_idx();

       if (lv_idx < 0)
              throw runtime_error(" no LV feature identified");

       add_feature_mask(ica.get_feature_image(lv_idx), lv_id, mask);
       // evaluate time-intensity curves with skipping initial frames
       vector<CSegFrame::SectionsStats> intensity_curves(series.size());
       transform(input_set.get_frames().begin() + skip_images, input_set.get_frames().end(), intensity_curves.begin(),
       [&mask](const CSegFrame & f) {
              return f.get_stats(mask);
       });
       ofstream output(out_filename.c_str());
       int k = skip_images;

       for (auto i = intensity_curves.begin(); i != intensity_curves.end(); ++i, ++k) {
              output << k << " ";

              for (auto ii = i->begin(); ii != i->end(); ++ii)
                     output << ii->first << " " << ii->second << " ";

              output << "\n";
       }

       return EXIT_SUCCESS;
}

#include <mia/internal/main.hh>
MIA_MAIN(do_main);

