/* -*- 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 <mia/core.hh>
#include <mia/2d.hh>
#include <mia/2d/rgbimageio.hh>
#include <mia/internal/main.hh>

NS_MIA_USE;
using namespace std;


const SProgramDescription g_general_help = {
       {pdi_group, "Analysis, filtering, combining, and segmentation of 2D images"},
       {pdi_short, "combine gray scale images to an rgb image."},
       {
              pdi_description, "This program combines up to three gray scale image to a "
              "three channel RGB image with eight bit per color channel. The input images "
              "must be gray scale of eight bit colordepth. If at input one channel is not "
              "given, it will be set to zero. at least one input channel must be given."
       },
       {
              pdi_example_descr, "Combine the images red.png, blue.tiff, and green.bmp to "
              "the output image rgb.jpg."
       },
       {pdi_example_code, "-r red.png -b blue.tiff -g green.bmp -o rgb.jpg"}
};

static void get_size(const C2DImage *img, C2DBounds& size)
{
       if (img) {
              if ((size.x == 0) && (size.y == 0))
                     size = img->get_size();
              else if ((size.x != img->get_size().x) || (size.y != img->get_size().y))
                     throw create_exception<invalid_argument>("Input images must be of the same size, but I got ",
                                   size, " vs. ",  img->get_size());
       }
}

const C2DUBImage& cast_or_zero(P2DImage img, const C2DUBImage& zero, const char *const channel)
{
       if (img) {
              if (img->get_pixel_type() != it_ubyte)
                     throw create_exception<invalid_argument>("Input image for channel '",
                                   channel, "' is not of type 'unsigned byte'");

              return dynamic_cast<const C2DUBImage&>(*img);
       } else
              return zero;
}

CRGB2DImage combine_channels(const C2DUBImage& red_ub, const C2DUBImage& green_ub, const C2DUBImage& blue_ub)
{
       CRGB2DImage result(red_ub.get_size());
       auto p = result.pixel();

       for (auto r = red_ub.begin(), g = green_ub.begin(), b = blue_ub.begin();
            r != red_ub.end(); ++r, ++g, ++b) {
              *p++ = *r;
              *p++ = *g;
              *p++ = *b;
       }

       return result;
}

int do_main( int argc, char *argv[] )
{
       string red_filename;
       string blue_filename;
       string green_filename;
       string out_filename;
       const auto& imageio = C2DImageIOPluginHandler::instance();
       CCmdOptionList options(g_general_help);
       options.add(make_opt( blue_filename, "blue", 'b', "input image for blue channel",
                             CCmdOptionFlags::input, &imageio));
       options.add(make_opt( green_filename, "green", 'g', "input image for green channel",
                             CCmdOptionFlags::input, &imageio));
       options.add(make_opt( red_filename, "red", 'r', "input image for red channel",
                             CCmdOptionFlags::input, &imageio));
       options.add(make_opt( out_filename, "out-file", 'o', "combined output image", CCmdOptionFlags::required_output, &imageio));

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

       P2DImage blue, green, red;

       if (!blue_filename.empty())
              blue = load_image<P2DImage>(blue_filename);

       if (!green_filename.empty())
              green = load_image<P2DImage>(green_filename);

       if (!red_filename.empty())
              red = load_image<P2DImage>(red_filename);

       if (!( red || green || blue))
              throw invalid_argument("No input available. At least one color channel must be given!");

       C2DBounds size;
       get_size(red.get(), size);
       get_size(green.get(), size);
       get_size(blue.get(), size);
       const C2DUBImage zero(size);
       const C2DUBImage& red_ub = cast_or_zero(red, zero, "red");
       const C2DUBImage& green_ub = cast_or_zero(green, zero, "green");
       const C2DUBImage& blue_ub = cast_or_zero(blue, zero, "blue");
       auto output = combine_channels(red_ub, green_ub, blue_ub);
       return save_image(out_filename, output) ? EXIT_SUCCESS : EXIT_FAILURE;
};

MIA_MAIN(do_main);
