/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK 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 Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#include "ItkImageComponentExtension.h"
#include "ItkImageComponent.h"

// save to itk
#include <itkImageFileWriter.h>
#include <itkVTKImageToImageFilter.h>

//-- CamiTK includes
#include <AbortException.h>
#include <Log.h>
using namespace camitk;

// --------------- getName -------------------
QString ItkImageComponentExtension::getName() const {
    return "ItkImages Component";
}

// --------------- getDescription -------------------
QString ItkImageComponentExtension::getDescription() const {
    return QString("Manage any file type supported by itk and not by vtk in <b>CamiTK</b>.<br/>")
           +  "For more info about image data format: <a href=\"http://www.dclunie.com/medical-image-faq/html\">http://www.dclunie.com/medical-image-faq/html</a><br/><ul>"
           + "<li>hdr is the Analyse header file format</li>"
           + "<li>spr is the Stimulate header file format</li>"
           + "<li>gipl is the Guys Image Processing Lab Format</li>"
           + "<li>pic is the Bio-Rad file format, used by confocal microscopes like MRC 1024 and MRC 600</li>"
//  + "<li>PAR/REC is the research Philips format</li>"
           + "<li>LSM is a line of confocal laser scanning microscopes produced by the Zeiss company</li>"
//  + "<li>ima is the Siemens Vision image format</li>"
//  + "<li>mnc (MINC / MINC2) is developed over the past 15 years at the Brain Imaging Centre (or Centre) at the Montreal Neurological Institute</li>"
           + "<li>Nrrd is the \"Nearly Raw Raster Data\"</li>"
//  + "<li>cub see <a href=\"http://www.insight-journal.org/browse/publication/118\">http://www.insight-journal.org/browse/publication/118</a></li></ul><br/>"
           + "<li>NIfTI-1 (.nii) is adapted from the widely used ANALYZE 7.5 file format. For more info about image data format: <a href=\"http://nifti.nimh.nih.gov/nifti-1/\">http://nifti.nimh.nih.gov/nifti-1/</a> </li>"
//  + "<li>cub see <a href=\"http://www.insight-journal.org/browse/publication/118\">http://www.insight-journal.org/browse/publication/118</a></li></ul><br/>"
           + "</ul>";
}

// --------------- getFileExtensions -------------------
QStringList ItkImageComponentExtension::getFileExtensions() const {
    QStringList ext;
    ext << "hdr" << "spr" << "gipl" << "pic" << "lsm" << "nrrd" << "hdr.gz" << "nii" << "nii.gz" << "img" << "img.gz";
    return ext;
}

// --------------- open -------------------
Component* ItkImageComponentExtension::open(const QString& fileName) {
    ItkImageComponent* res = nullptr;
    try {
        res = new ItkImageComponent(fileName);
    }
    catch (const AbortException& e) {
        CAMITK_ERROR(tr("CamiTK exception: %1").arg(e.what()));
        throw e;
    }
    return res;
}

// --------------- save -------------------
bool ItkImageComponentExtension::save(Component* c) const {
    bool succeed = false;

    ImageComponent* img = dynamic_cast<ImageComponent*>(c);
    if (!img) {
        return succeed;
    }

    // instantiate the template function saveImage
    // according to the input image type.
    // Here by default, the Ouput pixel type corresponds to the input pixel type.

    int* dims = img->getImageData()->GetDimensions();
    int dim = 0;

    if (dims[2] == 0) {
        dim = 2;
    }
    else {
        dim = 3;
    }

    vtkSmartPointer<vtkImageData> data = img->getImageData();

    int scalarType = data->GetScalarType();

    switch (scalarType) {

        case VTK_CHAR :

            if (dim == 2) {
                typedef itk::Image<char, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<char, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_UNSIGNED_CHAR :

            if (dim == 2) {
                typedef itk::Image<unsigned char, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<unsigned char, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_SIGNED_CHAR :

            if (dim == 2) {
                typedef itk::Image<signed char, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<signed char, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_SHORT :

            if (dim == 2) {
                typedef itk::Image<short, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<short, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_UNSIGNED_SHORT :

            if (dim == 2) {
                typedef itk::Image<unsigned short, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<unsigned short, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_INT :

            if (dim == 2) {
                typedef itk::Image<int, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<int, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_UNSIGNED_INT :

            if (dim == 2) {
                typedef itk::Image<unsigned int, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<unsigned int, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_LONG :

            if (dim == 2) {
                typedef itk::Image<long, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<long, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_UNSIGNED_LONG :

            if (dim == 2) {
                typedef itk::Image<unsigned long, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<unsigned long, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_FLOAT :

            if (dim == 2) {
                typedef itk::Image<float, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<float, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        case VTK_DOUBLE :

            if (dim == 2) {
                typedef itk::Image<double, 2> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }
            else {   // if dim == 3
                typedef itk::Image<double, 3> ImageType;
                succeed = ItkImageComponentExtension::saveImage<ImageType>(img);
            }

            break;

        default :
            break;
    }

    return succeed;


}

// --------------- saveImage -------------------
template<class ImageType> bool ItkImageComponentExtension::saveImage(ImageComponent* img) {
    typedef itk::VTKImageToImageFilter<ImageType> VtkToItkFilterType;
    typedef itk::ImageFileWriter< ImageType >     WriterType;

    QString filename = img->getFileName();
    if (filename.isEmpty()) {
        return false;
    }
    vtkSmartPointer<vtkImageData> vtkImage = img->getImageData();


    typename VtkToItkFilterType::Pointer vtkToItkFilter = VtkToItkFilterType::New();
    typename WriterType::Pointer         writer         = WriterType::New();


    vtkToItkFilter->SetInput(vtkImage);
    writer->SetFileName(filename.toUtf8());
    writer->SetInput(vtkToItkFilter->GetOutput());

    try {
        writer->Update();
    }
    catch (const itk::ExceptionObject& err) {
        std::stringstream buffer;
        err.Print(buffer);
        CAMITK_ERROR_ALT(tr("Saving Error: problem occurs while saving file \"%1\", ITK Exception: %2\n%3").arg(filename, err.what(), buffer.str().c_str()))
        return false;
    }

    return true;
}

