/* -*- 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 <cassert>
#include <algorithm>
#include <mia/core/file.hh>
#include <mia/core/filter.hh>
#include <mia/core/msgstream.hh>
#include <mia/core/attribute_names.hh>
#include <mia/2d/imageio.hh>

#include <vistaio/vista4mia.hh>

NS_BEGIN(vista_2d_io)

NS_MIA_USE

using namespace std;
using namespace boost;

class CVista2DImageIOPlugin : public C2DImageIOPlugin
{
public:
       CVista2DImageIOPlugin();
private:
       PData do_load(const string& fname) const;
       bool do_save(const string& fname, const Data& data) const;
       const string do_get_descr() const;
       const std::string do_get_preferred_suffix() const;
};

CVista2DImageIOPlugin::CVista2DImageIOPlugin():
       C2DImageIOPlugin("vista")
{
       add_supported_type(it_bit);
       add_supported_type(it_ubyte);
       add_supported_type(it_sbyte);
       add_supported_type(it_sshort);
       add_supported_type(it_ushort);
       add_supported_type(it_sint);
       add_supported_type(it_uint);
       add_supported_type(it_float);
       add_supported_type(it_double);
       add_standard_vistaio_properties(*this);
       add_suffix(".-");
       add_suffix(".v");
       add_suffix(".V");
       add_suffix(".vista");
       add_suffix(".VISTA");
}


template <typename T>
void read_image(VistaIOImage image, CVista2DImageIOPlugin::Data& result_list, int aqnr)
{
       size_t n = VistaIOImageNBands(image);
       size_t slice_size = VistaIOImageNColumns(image) * VistaIOImageNRows(image);
       typedef typename vista_repnkind<T>::type O;
       O *begin = (O *)VistaIOPixelPtr(image, 0, 0, 0);
       O *end = begin + slice_size;
       size_t idx = 0;

       while (idx < n) {
              T2DImage<T> *result = new T2DImage<T>(C2DBounds(VistaIOImageNColumns(image), VistaIOImageNRows(image)));
              P2DImage presult(result);
              std::copy(begin, end, result->begin());
              copy_attr_list(*result, VistaIOImageAttrList(image));
              presult->set_attribute(IDInstanceNumber, PAttribute(new CIntAttribute(idx)));

              if (!presult->has_attribute(IDAcquisitionNumber))
                     presult->set_attribute(IDAcquisitionNumber, PAttribute(new CIntAttribute(aqnr)));

              result_list.push_back(presult);
              begin += slice_size;
              end   += slice_size;
              ++idx;
       }
}

void copy_from_vista(VistaIOImage image, CVista2DImageIOPlugin::Data& result, int i)
{
       VistaIOBoolean is_unsigned = 0;
       VistaIOExtractAttr (VistaIOImageAttrList(image), "repn-unsigned", NULL, VistaIOBitRepn,
                           &is_unsigned, 0);

       switch (VistaIOPixelRepn(image)) {
       case VistaIOBitRepn :
              read_image<bool>(image, result, i);
              break;

       case VistaIOUByteRepn :
              read_image<unsigned char>(image, result, i);
              break;

       case VistaIOSByteRepn :
              read_image<signed char>(image, result, i);
              break;

       case VistaIOShortRepn :
              if (is_unsigned)
                     read_image<unsigned short>(image, result, i);
              else
                     read_image<signed short>(image, result, i);

              break;

       case VistaIOLongRepn :
              if (is_unsigned)
                     read_image<unsigned int>(image, result, i);
              else
                     read_image<signed int>(image, result, i);

              break;

       case VistaIOFloatRepn :
              read_image<float>(image, result, i);
              break;

       case VistaIODoubleRepn :
              read_image<double>(image, result, i);
              break;

       default:
              throw invalid_argument("2d vista load: Unknown pixel format");
       }
}

CVista2DImageIOPlugin::PData  CVista2DImageIOPlugin::do_load(const string& fname) const
{
       CInputFile f(fname);
       VistaIOImage *images = NULL;
       VistaIOAttrList attr_list = NULL;
       int nimages = VistaIOReadImages(f, &attr_list, &images);

       if (!nimages)
              return 	CVista2DImageIOPlugin::PData();

       CVista2DImageIOPlugin::PData result(new CVista2DImageIOPlugin::Data());

       for (int i = 0; i < nimages; ++i) {
              copy_from_vista(images[i], *result, i);
              VistaIODestroyImage(images[i]);
       }

       VistaIOFree(images);
       VistaIODestroyAttrList (attr_list);
       return result;
}






struct CVImageCreator: public TFilter <VistaIOImage> {
       template <typename T>
       VistaIOImage operator ()( const T2DImage<T>& image) const;
};

template <typename T>
VistaIOImage CVImageCreator::operator ()( const T2DImage<T>& image) const
{
       typedef dispatch_creat_vimage<typename T2DImage<T>::const_iterator, T> dispatcher;
       VistaIOImage result = dispatcher::apply(image.begin(), image.end(), image.get_size().x, image.get_size().y, 1);
       copy_attr_list(VistaIOImageAttrList(result), image);
       return result;
}

bool CVista2DImageIOPlugin::do_save(const string& fname, const C2DImageVector& data) const
{
       COutputFile f(fname);
       vector<VistaIOImage> images(data.size());
       CVImageCreator creator;
       vector<VistaIOImage>::iterator img = images.begin();

       for (C2DImageVector::const_iterator i = data.begin();
            i != data.end(); ++i, ++img) {
              *img = filter(creator, **i);
       }

       bool result = VistaIOWriteImages(f, NULL, data.size(), &images[0]) == TRUE;

       // clean up
       for (vector<VistaIOImage>::iterator i = images.begin(); i != images.end(); ++i)
              VistaIODestroyImage(*i);

       return result;
}

const string CVista2DImageIOPlugin::do_get_descr() const
{
       return "a 2dimage io plugin for vista images";
}

const std::string CVista2DImageIOPlugin::do_get_preferred_suffix() const
{
       return "v";
}


extern "C" EXPORT  CPluginBase *get_plugin_interface()
{
       C2DFVectorTranslator::register_for("pixel");
       return new CVista2DImageIOPlugin();
}

NS_END
