/*
 *
 *  Copyright (C) 1998-2024, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module:  dcmimgle
 *
 *  Author:  Joerg Riesmeier
 *
 *  Purpose: DicomCIELABFunction (Source)
 *
 */


#include "dcmtk/config/osconfig.h"

#include "dcmtk/dcmimgle/diciefn.h"
#include "dcmtk/ofstd/ofstream.h"
#include <cstring>

/*----------------*
 *  constructors  *
 *----------------*/

DiCIELABFunction::DiCIELABFunction(const char *filename,
                                   const E_DeviceType deviceType,
                                   const signed int ord)
  : DiDisplayFunction(filename, deviceType, ord)
{
    if (!Valid)
    {
        DCMIMGLE_ERROR("invalid DISPLAY file ... ignoring");
    }
}


DiCIELABFunction::DiCIELABFunction(const double *val_tab,             // UNTESTED !!
                                   const unsigned long count,
                                   const Uint16 max,
                                   const E_DeviceType deviceType,
                                   const signed int ord)
  : DiDisplayFunction(val_tab, count, max, deviceType, ord)
{
    if (!Valid)
    {
        DCMIMGLE_ERROR("invalid DISPLAY values ... ignoring");
    }
}


DiCIELABFunction::DiCIELABFunction(const Uint16 *ddl_tab,             // UNTESTED !!
                                   const double *val_tab,
                                   const unsigned long count,
                                   const Uint16 max,
                                   const E_DeviceType deviceType,
                                   const signed int ord)
  : DiDisplayFunction(ddl_tab, val_tab, count, max, deviceType, ord)
{
    if (!Valid)
    {
        DCMIMGLE_ERROR("invalid DISPLAY values ... ignoring");
    }
}


DiCIELABFunction::DiCIELABFunction(const double val_min,
                                   const double val_max,
                                   const unsigned long count,
                                   const E_DeviceType deviceType,
                                   const signed int ord)
  : DiDisplayFunction(val_min, val_max, count, deviceType, ord)
{
    if (!Valid)
    {
        DCMIMGLE_ERROR("invalid DISPLAY values ... ignoring");
    }
}


/*--------------*
 *  destructor  *
 *--------------*/

DiCIELABFunction::~DiCIELABFunction()
{
}


/********************************************************************/


DiDisplayLUT *DiCIELABFunction::getDisplayLUT(unsigned long count)
{
    DiDisplayLUT *lut = NULL;
    if (count <= MAX_TABLE_ENTRY_COUNT)
    {
        if ((DeviceType == EDT_Printer) || (DeviceType == EDT_Scanner))
        {
            /* hardcopy: values are in optical density, first convert them to luminance */
            double *tmp_tab = convertODtoLumTable(LODValue, ValueCount, OFFalse /*useAmb*/);
            if (tmp_tab != NULL)
            {
                checkMinMaxDensity();
                /* create new CIELAB LUT */
                lut = new DiCIELABLUT(count, MaxDDLValue, DDLValue, tmp_tab, ValueCount,
                    convertODtoLum(MaxValue, OFFalse /*useAmb*/), convertODtoLum(MinValue, OFFalse /*useAmb*/),
                    getMinLuminanceValue(), getMaxLuminanceValue(), AmbientLight, (DeviceType == EDT_Scanner));
                /* delete temporary table */
                delete[] tmp_tab;
            }
        } else {
            /* softcopy: values are already in luminance */
            lut = new DiCIELABLUT(count, MaxDDLValue, DDLValue, LODValue, ValueCount,
                MinValue, MaxValue, -1 /*Lmin*/, -1 /*Lmax*/, AmbientLight, (DeviceType == EDT_Camera));
        }
    }
    return lut;
}


int DiCIELABFunction::writeCurveData(const char *filename,
                                     const OFBool mode)
{
    if ((filename != NULL) && (strlen(filename) > 0))
    {
        STD_NAMESPACE ofstream file(filename);
        if (file)
        {
            const OFBool inverseLUT = (DeviceType == EDT_Scanner) || (DeviceType == EDT_Camera);
            /* comment header */
            file << "# Display function       : CIELAB" << OFendl;
            if (DeviceType == EDT_Printer)
                file << "# Type of output device  : Printer (hardcopy)" << OFendl;
            else if (DeviceType == EDT_Scanner)
                file << "# Type of output device  : Scanner (hardcopy)" << OFendl;
            else if (DeviceType == EDT_Camera)
                file << "# Type of output device  : Camera (softcopy)" << OFendl;
            else
                file << "# Type of output device  : Monitor (softcopy)" << OFendl;
            file << "# Digital driving levels : " << ValueCount << OFendl;
            if ((DeviceType == EDT_Printer) || (DeviceType == EDT_Scanner))
                file << "# Illumination  [cd/m^2] : " << Illumination << OFendl;
            file << "# Ambient light [cd/m^2] : " << AmbientLight << OFendl;
            if ((DeviceType == EDT_Printer) || (DeviceType == EDT_Scanner))
            {
                const double min_lum = getMinLuminanceValue();
                const double max_lum = getMaxLuminanceValue();
                file << "# Luminance w/o [cd/m^2] : " << convertODtoLum(MaxValue, OFFalse /*useAmb*/) << " - "
                                                      << convertODtoLum(MinValue, OFFalse /*useAmb*/);
                if ((min_lum >= 0) || (max_lum >= 0))
                {
                    file << " (Lmin = ";
                    if (min_lum >= 0)
                        file << min_lum;
                    else
                        file << "n/s";
                    file << ", Lmax = ";
                    if (max_lum >= 0)
                        file << max_lum;
                    else
                        file << "n/s";
                    file << ")";
                }
                file << OFendl;
                file << "# Optical density   [OD] : " << MinValue << " - " << MaxValue;
                if ((MinDensity >= 0) || (MaxDensity >= 0))
                {
                    file << " (Dmin = ";
                    if (MinDensity >= 0)
                        file << MinDensity;
                    else
                        file << "n/s";
                    file << ", Dmax = ";
                    if (MaxDensity >= 0)
                        file << MaxDensity;
                    else
                        file << "n/s";
                    file << ")";
                }
                file << OFendl;
            } else
                file << "# Luminance w/o [cd/m^2] : " << MinValue << " - " << MaxValue << OFendl;
            file << "# Interpolation method   : ";
            if (getPolynomialOrder() > 0)
                file << "Curve fitting algorithm with order " << getPolynomialOrder() << OFendl << OFendl;
            else
                file << "Cubic spline interpolation" << OFendl << OFendl;
            /* print headings of the table */
            if (mode)
            {
                file << "# NB: values for CC, CIELAB and PSC";
                if (inverseLUT)
                    file << "'";                // add ' to PSC
                file << " are specified in cd/m^2" << OFendl << OFendl;
                file << "DDL\tCC\tCIELAB\tPSC";
                if (inverseLUT)
                    file << "'";                // add ' to PSC
                file << OFendl;
            } else {
                file << "# NB: values for CC and CIELAB are specified in cd/m^2" << OFendl << OFendl;
                file << "DDL\tCIELAB" << OFendl;
            }
            /* create CIELAB LUT and write curve data to file */
            DiCIELABLUT *lut = NULL;
            if ((DeviceType == EDT_Printer) || (DeviceType == EDT_Scanner))
            {
                /* hardcopy: values are in optical density, first convert them to luminance */
                double *tmp_tab = convertODtoLumTable(LODValue, ValueCount, OFFalse /*useAmb*/);
                if (tmp_tab != NULL)
                {
                    checkMinMaxDensity();
                    lut = new DiCIELABLUT(ValueCount, MaxDDLValue, DDLValue, tmp_tab, ValueCount,
                        convertODtoLum(MaxValue, OFFalse /*useAmb*/), convertODtoLum(MinValue, OFFalse /*useAmb*/),
                        getMinLuminanceValue(), getMaxLuminanceValue(), AmbientLight, inverseLUT, &file, mode);
                    /* delete temporary table */
                    delete[] tmp_tab;
                }
            } else {
                /* softcopy: values are already in luminance */
                lut = new DiCIELABLUT(ValueCount, MaxDDLValue, DDLValue, LODValue, ValueCount,
                    MinValue, MaxValue, -1 /*Lmin*/, -1 /*Lmax*/, AmbientLight, inverseLUT, &file, mode);
            }
            int status = (lut != NULL) && (lut->isValid());
            delete lut;
            return status;
        }
    }
    return 0;
}
