/* -*- 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 <cstdio>
#include <cstdlib>
#include <sstream>
#include <cassert>
#include <mia/core/file.hh>
#include <mia/core/filter.hh>
#include <mia/core/msgstream.hh>
#include <mia/2d/rgbimageio.hh>

#include "jpeg-common.hh"

NS_BEGIN(RGBIMAGEIO_2D_JPG)

using namespace mia;
using namespace std;
using namespace boost;

class CJpegRGB2DImageIOPlugin : public C2DRGBImageIOPlugin
{
public:
       CJpegRGB2DImageIOPlugin();
private:
       PData do_load(const string& fname) const;
       bool do_save(const string& fname, const CRGB2DImage& data) const;
       const string do_get_descr() const;

       bool do_save_jpeg(FILE *f, const CRGB2DImage& image) const;
};

CJpegRGB2DImageIOPlugin::CJpegRGB2DImageIOPlugin():
       C2DRGBImageIOPlugin("jpg")
{
       add_supported_type(it_ubyte);
       add_suffix(".jpg");
       add_suffix(".JPG");
       add_suffix(".jpeg");
       add_suffix(".JPEG");
}

METHODDEF(void) mia_jpeg_load_error_exit (j_common_ptr /*cinfo*/)
{
       throw create_exception<runtime_error>("JpegRGB::load: error reading from input file");
}

METHODDEF(void) mia_jpeg_save_error_exit (j_common_ptr /*cinfo*/)
{
       throw create_exception<runtime_error>("JpegRGB::save: error reading from input file");
}


C2DRGBImageIOPlugin::PData CJpegRGB2DImageIOPlugin::do_load(const string& fname) const
{
       CInputFile f(fname);

       if (!f)
              throw create_exception<runtime_error>("CJpegRGB2DImageIO::save:unable to open input file '", fname, "'");

       miajpeg::JpegDecompress decompress;
       decompress.err.error_exit = mia_jpeg_load_error_exit;
       jpeg_stdio_src(&decompress.info, f);

       // is it a jpeg image?
       if (jpeg_read_header(&decompress.info, TRUE) != JPEG_HEADER_OK)
              return C2DRGBImageIOPlugin::PData();

       jpeg_start_decompress(&decompress.info);

       // only one component?
       if (decompress.info.output_components != 3) {
              throw create_exception<invalid_argument>(":MIA this plugin only supports RGB images, but got an image with ",
                            decompress.info.output_components, " color components.");
       }

       int row_stride = decompress.info.output_width * decompress.info.output_components;
       vector<JSAMPLE> buf(row_stride);
       cvdebug() << "owidth=" << decompress.info.output_width
                 << ", row_stride="  << row_stride << "\n";
       JSAMPROW buffer[1];
       buffer[0] = &buf[0];
       cvdebug() << "Create image of size "
                 << decompress.info.output_width << "x" << decompress.info.output_height << "\n";
       CRGB2DImage *result = new CRGB2DImage(C2DBounds(decompress.info.output_width, decompress.info.output_height));
       PRGB2DImage presult(result);
       unsigned char *p = presult->pixel();
       cvdebug() << "output pointer base = " << (long)p << "\n";

       while (decompress.info.output_scanline < decompress.info.output_height) {
              cvdebug() << "scanline=" << decompress.info.output_scanline
                        << " - " << decompress.info.output_height
                        << " outpos = " << decompress.info.output_scanline *row_stride << "\n";
              jpeg_read_scanlines(&decompress.info, buffer, 1);
              cvdebug() << "output pointer = " << (long)&p[decompress.info.output_scanline * row_stride] << "\n";
              copy(buf.begin(), buf.end(), &p[(decompress.info.output_scanline - 1) * row_stride]);
       }

       return presult;
}

bool CJpegRGB2DImageIOPlugin::do_save_jpeg(FILE *f, const CRGB2DImage& image) const
{
       miajpeg::JpegCompress compress;
       compress.err.error_exit = mia_jpeg_save_error_exit;
       compress.info.image_width = image.get_size().x;
       compress.info.image_height = image.get_size().y;
       vector<JSAMPLE> samples(image.get_size().x * 3);
       JSAMPROW scanline[1];
       scanline[0] = &samples[0];
       compress.info.input_components = 3;
       compress.info.in_color_space = JCS_RGB;
       jpeg_set_defaults(&compress.info);
       jpeg_set_quality(&compress.info, 80, 1);
       jpeg_stdio_dest(&compress.info, f);
       jpeg_start_compress(&compress.info, TRUE);
       int dx = image.get_size().x * 3;

       for (unsigned int y = 0; y < image.get_size().y; ++y) {
              copy(image.pixel() + y * dx, image.pixel() + (y + 1) * dx, scanline[0]);
              jpeg_write_scanlines(&compress.info, scanline, 1);
       }

       jpeg_finish_compress(&compress.info);
       return true;
}

bool CJpegRGB2DImageIOPlugin::do_save(const string& fname, const CRGB2DImage& image) const
{
       TRACE_FUNCTION;
       COutputFile f(fname);

       if (!f)
              throw create_exception<runtime_error>("CJpegRGB2DImageIO::save:unable to open output file '", fname, "'");

       return do_save_jpeg(f, image);
}

const string CJpegRGB2DImageIOPlugin::do_get_descr() const
{
       return "A 2dimage io plugin for jpeg RGB images, Saving is et to 80% quality.";
}

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



NS_END
