/*
 * medInria
 * Copyright (c) INRIA 2013. All rights reserved.
 *
 * medInria is under BSD-2-Clause license. See LICENSE.txt for details in the root of the sources or:
 * https://github.com/medInria/medInria-public/blob/master/LICENSE.txt
 *
 * This software is distributed WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "imageFilters.h"

#include <medAbstractDataFactory.h>

#include <itkIntensityWindowingImageFilter.h>
#include <itkMinimumMaximumImageCalculator.h>

imageFilters::imageFilters()
{
    chosenMethod = BINARIZE;
}

void imageFilters::setInput(medAbstractData *data)
{
    input0 = data;
}

/**
 * @brief By default, the BINARIZE algorithm is chosen
 *
 * @param method
 */
void imageFilters::setParameter(int method)
{
    chosenMethod = method;
}

/**
 * @brief Convert medAbstractData to ITK volume and run the chosen filter algorithm
 *
 * @return true
 * @return false
 */
bool imageFilters::update()
{
    bool res = false;

    if (input0)
    {
        QString id = input0->identifier();

        if (id == "itkDataImageChar3")
        {
            res = runProcess<itk::Image<char, 3>>();
        }
        else if (id == "itkDataImageUChar3")
        {
            res = runProcess<itk::Image<unsigned char, 3>>();
        }
        else if (id == "itkDataImageShort3")
        {
            res = runProcess<itk::Image<short, 3>>();
        }
        else if (id == "itkDataImageUShort3")
        {
            res = runProcess<itk::Image<unsigned short, 3>>();
        }
        else if (id == "itkDataImageInt3")
        {
            res = runProcess<itk::Image<int, 3>>();
        }
        else if (id == "itkDataImageUInt3")
        {
            res = runProcess<itk::Image<unsigned int, 3>>();
        }
        else if (id == "itkDataImageLong3")
        {
            res = runProcess<itk::Image<long, 3>>();
        }
        else if (id == "itkDataImageULong3")
        {
            res = runProcess<itk::Image<unsigned long, 3>>();
        }
        else if (id == "itkDataImageFloat3")
        {
            res = runProcess<itk::Image<float, 3>>();
        }
        else if (id == "itkDataImageDouble3")
        {
            res = runProcess<itk::Image<double, 3>>();
        }
        else
        {
            qDebug() << "imageFilters, error: pixel type not yet implemented ("
                     << id
                     << ")";
        }
    }
    return res;
}

/**
 * @brief Send back the output data generated by the chosen filter
 *
 * @return dtkSmartPointer<medAbstractData>
 */
dtkSmartPointer<medAbstractData> imageFilters::dataOutput()
{
    return this->computedDataOutput;
}

template <class ImageType>
bool imageFilters::runProcess()
{
    bool res = false;

    if (chosenMethod == imageFilters::BINARIZE)
    {
        res = runBinarize<ImageType>();
    }
    return res;
}

/**
 * @brief Set pixel intensities to 0/1 in masks with non-0/1 values,
 * otherwise it returns the original data
 *
 * @tparam ImageType
 * @return true
 * @return false
 */
template <class ImageType>
bool imageFilters::runBinarize()
{
    auto inputImage = dynamic_cast<ImageType *>((itk::Object *)(input0->data()));
    dtkSmartPointer<medAbstractData> binarizeMask = input0;

    // Get minimum and maximum of the data
    typedef itk::MinimumMaximumImageCalculator<ImageType> ImageCalculatorFilterType;
    typename ImageCalculatorFilterType::Pointer imageCalculatorFilter = ImageCalculatorFilterType::New();
    imageCalculatorFilter->SetImage(inputImage);
    imageCalculatorFilter->Compute();
    auto minValueImage = imageCalculatorFilter->GetMinimum();
    auto maxValueImage = imageCalculatorFilter->GetMaximum();

    if (minValueImage != 0)
    {
        typedef itk::IntensityWindowingImageFilter<ImageType, ImageType> WindowingFilterType;
        typename WindowingFilterType::Pointer windowingFilter = WindowingFilterType::New();
        windowingFilter->SetInput(inputImage);
        windowingFilter->SetWindowMinimum(minValueImage);
        windowingFilter->SetWindowMaximum(maxValueImage);
        windowingFilter->SetOutputMinimum(0);
        windowingFilter->SetOutputMaximum(1);
        windowingFilter->Update();
        
        binarizeMask = medAbstractDataFactory::instance()->createSmartPointer(binarizeMask->identifier());
        binarizeMask->setData(windowingFilter->GetOutput());
    }
    computedDataOutput = binarizeMask;
    return true;
}