/*
 * Unix interface for liblcms2
 *
 * Copyright 2020 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#if 0
#pragma makedep unix
#endif

#include "config.h"

#ifdef HAVE_LCMS2

#include <stdarg.h>
#include <lcms2.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "wingdi.h"
#include "winuser.h"
#include "icm.h"
#include "mscms_priv.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(mscms);

static DWORD from_bmformat( BMFORMAT format )
{
    static BOOL quietfixme = FALSE;
    DWORD ret;

    switch (format)
    {
    case BM_RGBTRIPLETS: ret = TYPE_RGB_8; break;
    case BM_BGRTRIPLETS: ret = TYPE_BGR_8; break;
    case BM_GRAY:        ret = TYPE_GRAY_8; break;
    case BM_xRGBQUADS:   ret = TYPE_ARGB_8; break;
    case BM_xBGRQUADS:   ret = TYPE_ABGR_8; break;
    case BM_KYMCQUADS:   ret = TYPE_KYMC_8; break;
    default:
        if (!quietfixme)
        {
            FIXME( "unhandled bitmap format %08x\n", format );
            quietfixme = TRUE;
        }
        ret = TYPE_RGB_8;
        break;
    }
    TRACE( "color space: %08x -> %08x\n", format, ret );
    return ret;
}

static DWORD from_type( COLORTYPE type )
{
    DWORD ret;

    switch (type)
    {
    case COLOR_GRAY: ret = TYPE_GRAY_16; break;
    case COLOR_RGB:  ret = TYPE_RGB_16; break;
    case COLOR_XYZ:  ret = TYPE_XYZ_16; break;
    case COLOR_Yxy:  ret = TYPE_Yxy_16; break;
    case COLOR_Lab:  ret = TYPE_Lab_16; break;
    case COLOR_CMYK: ret = TYPE_CMYK_16; break;
    default:
        FIXME( "unhandled color type %08x\n", type );
        ret = TYPE_RGB_16;
        break;
    }

    TRACE( "color type: %08x -> %08x\n", type, ret );
    return ret;
}

static void * CDECL lcms_open_profile( void *data, DWORD size )
{
    return cmsOpenProfileFromMem( data, size );
}

static void CDECL lcms_close_profile( void *profile )
{
    cmsCloseProfile( profile );
}

static void * CDECL lcms_create_transform( void *output, void *target, DWORD intent )
{
    DWORD proofing = 0;
    cmsHPROFILE input = cmsCreate_sRGBProfile(); /* FIXME: create from supplied color space */

    if (target) proofing = cmsFLAGS_SOFTPROOFING;
    return cmsCreateProofingTransform( input, 0, output, 0, target,
                                       intent, INTENT_ABSOLUTE_COLORIMETRIC, proofing );
}

static void * CDECL lcms_create_multi_transform( void *profiles, DWORD count, DWORD intent )
{
    return cmsCreateMultiprofileTransform( profiles, count, 0, 0, intent, 0 );
}

static BOOL CDECL lcms_translate_bits( void *transform, void *srcbits, BMFORMAT input,
                                       void *dstbits, BMFORMAT output, DWORD size )
{
    if (!cmsChangeBuffersFormat( transform, from_bmformat(input), from_bmformat(output) )) return FALSE;
    cmsDoTransform( transform, srcbits, dstbits, size );
    return TRUE;
}

static BOOL CDECL lcms_translate_colors( void *transform, COLOR *in, DWORD count, COLORTYPE input_type,
                                         COLOR *out, COLORTYPE output_type )
{
    unsigned int i;

    if (!cmsChangeBuffersFormat( transform, from_type(input_type), from_type(output_type) )) return FALSE;

    switch (input_type)
    {
    case COLOR_RGB:
        switch (output_type)
        {
        case COLOR_RGB:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].rgb, &out[i].rgb, 1 ); return TRUE;
        case COLOR_Lab:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].rgb, &out[i].Lab, 1 ); return TRUE;
        case COLOR_GRAY: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].rgb, &out[i].gray, 1 ); return TRUE;
        case COLOR_CMYK: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].rgb, &out[i].cmyk, 1 ); return TRUE;
        case COLOR_XYZ:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].rgb, &out[i].XYZ, 1 ); return TRUE;
        default: break;
        }
        break;
    case COLOR_Lab:
        switch (output_type)
        {
        case COLOR_RGB:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].Lab, &out[i].rgb, 1 ); return TRUE;
        case COLOR_Lab:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].Lab, &out[i].Lab, 1 ); return TRUE;
        case COLOR_GRAY: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].Lab, &out[i].gray, 1 ); return TRUE;
        case COLOR_CMYK: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].Lab, &out[i].cmyk, 1 ); return TRUE;
        case COLOR_XYZ:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].Lab, &out[i].XYZ, 1 ); return TRUE;
        default: break;
        }
        break;
    case COLOR_GRAY:
        switch (output_type)
        {
        case COLOR_RGB:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].gray, &out[i].rgb, 1 ); return TRUE;
        case COLOR_Lab:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].gray, &out[i].Lab, 1 ); return TRUE;
        case COLOR_GRAY: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].gray, &out[i].gray, 1 ); return TRUE;
        case COLOR_CMYK: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].gray, &out[i].cmyk, 1 ); return TRUE;
        case COLOR_XYZ:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].gray, &out[i].XYZ, 1 ); return TRUE;
        default: break;
        }
        break;
    case COLOR_CMYK:
        switch (output_type)
        {
        case COLOR_RGB:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].cmyk, &out[i].rgb, 1 ); return TRUE;
        case COLOR_Lab:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].cmyk, &out[i].Lab, 1 ); return TRUE;
        case COLOR_GRAY: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].cmyk, &out[i].gray, 1 ); return TRUE;
        case COLOR_CMYK: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].cmyk, &out[i].cmyk, 1 ); return TRUE;
        case COLOR_XYZ:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].cmyk, &out[i].XYZ, 1 ); return TRUE;
        default: break;
        }
        break;
    case COLOR_XYZ:
        switch (output_type)
        {
        case COLOR_RGB:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].XYZ, &out[i].rgb, 1 ); return TRUE;
        case COLOR_Lab:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].XYZ, &out[i].Lab, 1 ); return TRUE;
        case COLOR_GRAY: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].XYZ, &out[i].gray, 1 ); return TRUE;
        case COLOR_CMYK: for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].XYZ, &out[i].cmyk, 1 ); return TRUE;
        case COLOR_XYZ:  for (i = 0; i < count; i++) cmsDoTransform( transform, &in[i].XYZ, &out[i].XYZ, 1 ); return TRUE;
        default: break;
        }
        break;
    default:
        break;
    }
    FIXME("unhandled input/output pair: %d/%d\n", input_type, output_type);
    return FALSE;
}

static void CDECL lcms_close_transform( void *transform )
{
    cmsDeleteTransform( transform );
}

static const struct lcms_funcs funcs =
{
    lcms_open_profile,
    lcms_close_profile,
    lcms_create_transform,
    lcms_create_multi_transform,
    lcms_translate_bits,
    lcms_translate_colors,
    lcms_close_transform
};

static void lcms_error_handler(cmsContext ctx, cmsUInt32Number error, const char *text)
{
    TRACE("%u %s\n", error, debugstr_a(text));
}

NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out )
{
    if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS;
    cmsSetLogErrorHandler( lcms_error_handler );
    *(const struct lcms_funcs **)ptr_out = &funcs;
    return STATUS_SUCCESS;
}

#endif /* HAVE_LCMS2 */
