//---------------------------------------------------------------------------------
//
//  Little Color Management System
//  Copyright (c) 1998-2020 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//---------------------------------------------------------------------------------
//

#include "lcms2.h"

// 0  for anything 0..127
// y=x for anything 127..255
//
// Y = (a*x + b)^g + e | x >= d
// Y = (c*x + f)       | x < d
// g = 1
// a = 1
// b = 0
// c = 0
// d = 127/255.;
// e = 0
// f = 0

static
cmsToneCurve* build_parametric()
{
    cmsFloat64Number Parameters[7];

    Parameters[0] = 1;
    Parameters[1] = 1;
    Parameters[2] = 0;
    Parameters[3] = 0;
    Parameters[4] = 127.0/255.0;
    Parameters[5] = 0;
    Parameters[6] = 0;

    return cmsBuildParametricToneCurve(0, 5, Parameters);
}


static
cmsInt32Number samplerOut(CMSREGISTER const cmsUInt16Number In[],
                          CMSREGISTER cmsUInt16Number Out[],
                          CMSREGISTER void* Cargo)
{
    cmsCIELab Lab;

    cmsLabEncoded2Float(&Lab, In);

    if (Lab.L > 80.0  && Lab.L < 100 && fabs(Lab.a) < 5 && fabs(Lab.b) < 5)
    {
        Out[0] = Out[1] = Out[2] = 0xffff;
    }
    else
    {
        Out[0] = Out[1] = Out[2] = 0x0000;
    }

    return TRUE;
}

static
cmsInt32Number samplerIn(CMSREGISTER const cmsUInt16Number In[],
                          CMSREGISTER cmsUInt16Number Out[],
                          CMSREGISTER void* Cargo)
{
    cmsCIELab Lab;


    if ((In[0] >> 8) == 0xe6 &&    
        (In[1] >> 8) == 0xbf &&
        (In[2] >> 8) == 0xbf)
    {
        Lab.L = 90;
        Lab.a = Lab.b = 0;
    }
    else
    {
        Lab.L = 0;
        Lab.a = Lab.b = 0;
    }

    cmsFloat2LabEncoded(Out, &Lab);
    
    return TRUE;
}



static
cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description)
{
    cmsMLU* DescriptionMLU, * CopyrightMLU;
    cmsBool  rc = FALSE;
    cmsContext ContextID = cmsGetProfileContextID(hProfile);

    DescriptionMLU = cmsMLUalloc(ContextID, 1);
    CopyrightMLU = cmsMLUalloc(ContextID, 1);

    if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;

    if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error;
    if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error;

    if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error;
    if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error;

    rc = TRUE;

Error:

    if (DescriptionMLU)
        cmsMLUfree(DescriptionMLU);
    if (CopyrightMLU)
        cmsMLUfree(CopyrightMLU);
    return rc;
}

int main(int argc, char* argv[])
{    

    cmsUInt32Number dimensions[MAX_INPUT_DIMENSIONS] = { 0 };
    static const cmsUInt16Number linearTram[2] = { 0x0000, 0xffff };
    cmsToneCurve* linear[3];
    cmsToneCurve* parametric[3];
    
    cmsStage* CLUT_in;
    cmsStage* CLUT_out;
    cmsStage* linear_stage;
    cmsStage* parametric_stage;
    cmsStage* identity_matrix_stage;
    cmsPipeline* tag_a2b0;
    cmsPipeline* tag_b2a0;

    const cmsFloat64Number Mat[] = { 1, 0, 0,
                                     0, 1, 0,
                                     0, 0, 1 };


    cmsHPROFILE hICC = cmsCreateProfilePlaceholder(0);
    
    cmsSetProfileVersion(hICC, 4.3);

    cmsSetDeviceClass(hICC, cmsSigDisplayClass);
    cmsSetColorSpace(hICC, cmsSigRgbData);
    cmsSetPCS(hICC, cmsSigLabData);

    cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
            
    dimensions[0] = 11;
    dimensions[1] = 5;
    dimensions[2] = 5;

    CLUT_in = cmsStageAllocCLut16bitGranular(0, dimensions, 3, 3, NULL);
    CLUT_out = cmsStageAllocCLut16bitGranular(0, dimensions, 3, 3, NULL);

    cmsStageSampleCLut16bit(CLUT_in,  samplerIn,  NULL, 0);
    cmsStageSampleCLut16bit(CLUT_out, samplerOut, NULL, 0);
    
    linear[0] = cmsBuildTabulatedToneCurve16(0, 2, linearTram);
    linear[1] = cmsDupToneCurve(linear[0]);
    linear[2] = cmsDupToneCurve(linear[0]);

    parametric[0] = build_parametric();
    parametric[1] = cmsDupToneCurve(parametric[0]);
    parametric[2] = cmsDupToneCurve(parametric[0]);

    linear_stage = cmsStageAllocToneCurves(0, 3, linear);
    parametric_stage = cmsStageAllocToneCurves(0, 3, parametric);
    identity_matrix_stage = cmsStageAllocMatrix(0, 3, 3, Mat, NULL);

    // A2B  A - CLUT - M - Matrix - B
    tag_a2b0 = cmsPipelineAlloc(0, 3, 3);

    cmsPipelineInsertStage(tag_a2b0, cmsAT_BEGIN, parametric_stage);
    cmsPipelineInsertStage(tag_a2b0, cmsAT_END, CLUT_in);
    cmsPipelineInsertStage(tag_a2b0, cmsAT_END, linear_stage);
    cmsPipelineInsertStage(tag_a2b0, cmsAT_END, identity_matrix_stage);
    cmsPipelineInsertStage(tag_a2b0, cmsAT_END, cmsStageDup(linear_stage));

    // B2A is B - Matrix - M - CLUT - A
    tag_b2a0 = cmsPipelineAlloc(0, 3, 3);
    cmsPipelineInsertStage(tag_b2a0, cmsAT_BEGIN, cmsStageDup(linear_stage));
    cmsPipelineInsertStage(tag_b2a0, cmsAT_END, cmsStageDup(identity_matrix_stage));
    cmsPipelineInsertStage(tag_b2a0, cmsAT_END, cmsStageDup(linear_stage));
    cmsPipelineInsertStage(tag_b2a0, cmsAT_END, CLUT_out);    
    cmsPipelineInsertStage(tag_b2a0, cmsAT_END, cmsStageDup(parametric_stage));

    SetTextTags(hICC, L"Stress test profile, provided by LittleCMS");

    cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*)cmsD50_XYZ());       
    cmsWriteTag(hICC, cmsSigAToB0Tag, (void*)tag_a2b0);
    cmsWriteTag(hICC, cmsSigBToA0Tag, (void*)tag_b2a0);
    
    cmsPipelineFree(tag_a2b0);
    cmsPipelineFree(tag_b2a0);

    cmsSaveProfileToFile(hICC, "stress.icc");

    cmsCloseProfile(hICC);
	
	return 0;
}
