/*
 *
 *  Copyright (C) 2017-2019, 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:  dcmiod
 *
 *  Author:  Michael Onken
 *
 *  Purpose: Tests for dcmiod's color conversion functionalities
 *
 */

#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */

#include "dcmtk/dcmiod/cielabutil.h"
#include "dcmtk/ofstd/ofrand.h"
#include "dcmtk/ofstd/oftest.h"

OFTEST(dcmiod_tcielabutil)
{
    // Result variables for testing
    double r1, r2, r3;
    r1 = r2 = r3 = 0.0;

    // Test RGB -> CIELab conversion with medium values
    IODCIELabUtil::rgb2Lab(r1, r2, r3 /* L,a,b */, 0.5, 0.5, 0.5);
    OFCHECK(fabs(53.388 - r1) < 0.001);
    OFCHECK(fabs(0.006 - r2) < 0.001);
    OFCHECK(fabs(-0.010 - r3) < 0.001);

    // Test RGB -> CIEXYZ conversion with medium values
    r1 = r2 = r3 = 0;
    IODCIELabUtil::rgb2Xyz(r1, r2, r3 /* X,Y,Z */, 0.5, 0.5, 0.5);
    OFCHECK(fabs(0.203 - r1) < 0.001);
    OFCHECK(fabs(0.214 - r2) < 0.001);
    OFCHECK(fabs(0.233 - r3) < 0.001);

    // Test RGB -> CIELab conversion with minimum value
    r1 = r2 = r3 = 0;
    IODCIELabUtil::rgb2Lab(r1, r2, r3 /* L,a,b */, 0, 0, 0);
    OFCHECK(fabs(0 - r1) < 0.001);
    OFCHECK(fabs(0 - r2) < 0.001);
    OFCHECK(fabs(0 - r3) < 0.001);

    // Test RGB -> XYZ conversion with minimum values
    r1 = r2 = r3 = 0;
    IODCIELabUtil::rgb2Xyz(r1, r2, r3 /* X,Y,Z */, 0, 0, 0);
    OFCHECK(fabs(0 - r1) < 0.001);
    OFCHECK(fabs(0 - r2) < 0.001);
    OFCHECK(fabs(0 - r3) < 0.001);

    // Test DICOM CIELab to "normalized" CIELab conversion with maximum values
    r1 = r2 = r3 = 0;
    IODCIELabUtil::dicomlab2Lab(r1, r2, r3, 65535, 65535, 65535);
    OFCHECK(fabs(100 - r1) < 0.001);
    OFCHECK(fabs(127 - r2) < 0.001);
    OFCHECK(fabs(127 - r3) < 0.001);

    // Test DICOM CIELab to "normalized" CIELab conversion with minimum values
    r1 = r2 = r3 = 0;
    IODCIELabUtil::dicomlab2Lab(r1, r2, r3, 0, 0, 0);
    OFCHECK(fabs(0 - r1) < 0.001);
    OFCHECK(fabs(-128 - r2) < 0.001);
    OFCHECK(fabs(-128 - r3) < 0.001);

    // Convert between colorspaces back and forth and check whether deviation after
    // roundtrip is less than around 1 promille

    // Initialize random numbers
    OFRandom rnd;

    // We do 1000 runs
    for (size_t numRun = 0; numRun < 1000; numRun++)
    {
        // i1, i2, i3: Input for conversion
        // r1, r2, r3: Results of conversion
        // o1, o2, o3: Output of inverse conversion (should equal i1, i2, i3)
        double i1, i2, i3, o1, o2, o3;
        i1 = i2 = i3 = r1 = r2 = r3 = o1 = o2 = o3 = 0.0;

        // Roundtrip RGB -> CIELab -> RGB
        i1 = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1);
        i2 = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1);
        i3 = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1);
        IODCIELabUtil::rgb2Lab(r1, r2, r3, i1, i2, i3);
        IODCIELabUtil::lab2Rgb(o1, o2, o3, r1, r2, r3);
        OFCHECK(fabs(i1 - o1) < 0.001);
        OFCHECK(fabs(i2 - o2) < 0.001);
        OFCHECK(fabs(i3 - o3) < 0.001);

        // Roundtrip CIELab -> DICOM CIELab -> CIELab
        i1 = i2 = i3 = r1 = r2 = r3 = o1 = o2 = o3 = 0.0;
        i1 = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1) * 100.0;
        i2 = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1) * 255.0 - 128;
        i3 = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1) * 255.0 - 128;
        IODCIELabUtil::lab2DicomLab(r1, r2, r3, i1, i2, i3);
        IODCIELabUtil::dicomlab2Lab(o1, o2, o3, r1, r2, r3);
        OFCHECK(fabs(i1 - o1) < 0.001);
        OFCHECK(fabs(i2 - o2) < 0.001);
        OFCHECK(fabs(i3 - o3) < 0.001);

        // Roundtrip RGB -> CIEXYZ -> CIELab -> dicomCIELab -> RGB
        i1 = i2 = i3 = r1 = r2 = r3 = o1 = o2 = o3 = 0.0;
        i1                                         = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1);
        i2                                         = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1);
        i3                                         = OFstatic_cast(double, rnd.getRND32()) / OFstatic_cast(Uint32, -1);
        IODCIELabUtil::rgb2Xyz(r1, r2, r3, i1, i2, i3);
        IODCIELabUtil::xyz2Lab(o1, o2, o3, r1, r2, r3);
        IODCIELabUtil::lab2DicomLab(r1, r2, r3, o1, o2, o3);
        IODCIELabUtil::dicomLab2RGB(o1, o2, o3, r1, r2, r3);

        OFCHECK(fabs(i1 - o1) < 0.001);
        OFCHECK(fabs(i2 - o2) < 0.001);
        OFCHECK(fabs(i3 - o3) < 0.001);
    }
}
