/* -*- 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 <cerrno>
#include <cstring>

#include <mia/core/file.hh>
#include <mia/core/filter.hh>
#include <mia/core/msgstream.hh>


#include <vistaio/3dvistaio.hh>
#include <vistaio/vista4mia.hh>

NS_BEGIN(vista_3d_io)

NS_MIA_USE

using namespace std;
using namespace boost;


CVista3DImageIOPlugin::CVista3DImageIOPlugin():
       C3DImageIOPlugin("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>
P3DImage read_image(VistaIOImage image)
{
       cvdebug() << "Read  image of type '" << CPixelTypeDict.get_name(pixel_type<T>::value) << "'\n";
       typedef typename vista_repnkind<T>::type O;
       T3DImage<T> *result = new T3DImage<T>(C3DBounds(VistaIOImageNColumns(image), VistaIOImageNRows(image), VistaIOImageNBands(image)));
       P3DImage presult(result);
       O *begin = (O *)VistaIOPixelPtr(image, 0, 0, 0);
       O *end = begin + result->size();
       std::copy(begin, end, result->begin());
       copy_attr_list(*result, VistaIOImageAttrList(image));
       return presult;
}

P3DImage copy_from_vista(VistaIOImage image)
{
       VistaIOBoolean is_unsigned = 0;
       VistaIOExtractAttr (VistaIOImageAttrList(image), "repn-unsigned", NULL, VistaIOBitRepn,
                           &is_unsigned, 0);

       // this could be changed to add a bunch of images
       // however, then one would also have to write these as such ...
       switch (VistaIOPixelRepn(image)) {
       case VistaIOBitRepn :
              return read_image<bool>(image);

       case VistaIOUByteRepn :
              return read_image<unsigned char>(image);

       case VistaIOSByteRepn :
              return read_image<signed char>(image);

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

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

       case VistaIOFloatRepn :
              return read_image<float>(image);

       case VistaIODoubleRepn :
              return read_image<double>(image);

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

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

       if (!f) {
              stringstream msg;
              msg << "Error opening '" << fname << "': " << strerror(errno);
              throw runtime_error(msg.str());
       }

       VistaIOImage *images;
       VistaIOAttrList attr_list;
       int nimages = VistaIOReadImages(f, &attr_list, &images);

       // a vista file?
       if (!nimages)
              return CVista3DImageIOPlugin::PData();

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

       for (int i = 0; i < nimages; ++i) {
              cvdebug() << "Read image no " << i << "\n";
              P3DImage r = copy_from_vista(images[i]);
              result->push_back(r);
              VistaIODestroyImage(images[i]);
       }

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


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

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

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

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

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

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

       return result;
}

const string CVista3DImageIOPlugin::do_get_descr() const
{
       return "Vista 3D";
}

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


extern "C" EXPORT  CPluginBase *get_plugin_interface()
{
       CVoxelAttributeTranslator::register_for("voxel");
       CVoxelAttributeTranslator::register_for("origin3d");
       C3DRotationAttributeTranslate::register_for("rotation3d");
       C3DIntAttributeTranslator::register_for("ca");
       C3DIntAttributeTranslator::register_for("cp");
       return new CVista3DImageIOPlugin();
}

NS_END
