/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkColorSpace.h"
#include "include/core/SkData.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTypes.h"
#include "include/encode/SkICC.h"
#include "modules/skcms/skcms.h"
#include "tests/Test.h"
#include "tools/Resources.h"

#include <cmath>
#include <cstdint>
#include <cstdlib>

DEF_TEST(AdobeRGB, r) {
    if (sk_sp<SkData> profile = GetResourceAsData("icc_profiles/AdobeRGB1998.icc")) {
        skcms_ICCProfile parsed;
        REPORTER_ASSERT(r, skcms_Parse(profile->data(), profile->size(), &parsed));
        REPORTER_ASSERT(r, !parsed.has_CICP);

        auto got  = SkColorSpace::Make(parsed);
        auto want = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
        REPORTER_ASSERT(r, SkColorSpace::Equals(got.get(), want.get()));
    }
}

DEF_TEST(HDR_ICC, r) {
    constexpr size_t kTestCount = 3;
    SK_API sk_sp<SkData> profile[kTestCount] = {
            SkWriteICCProfile(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020),
            SkWriteICCProfile(SkNamedTransferFn::kHLG, SkNamedGamut::kDisplayP3),
            SkWriteICCProfile(SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB),
    };

    SK_API sk_sp<SkData> dst_profile[kTestCount] = {
            SkWriteICCProfile(SkNamedTransferFn::kLinear, SkNamedGamut::kRec2020),
            SkWriteICCProfile(SkNamedTransferFn::kLinear, SkNamedGamut::kDisplayP3),
            SkWriteICCProfile(SkNamedTransferFn::kLinear, SkNamedGamut::kSRGB),
    };

    constexpr size_t kPixelCount = 6;

    // clang-format off
    float pixels[kPixelCount][3] = {
            { 0.0f, 0.0f, 0.0f, },
            { 0.5f, 0.5f, 0.5f, },
            { 0.5f, 0.0f, 0.0f, },
            { 0.0f, 0.5f, 0.0f, },
            { 0.0f, 0.0f, 0.5f, },
            { 1.0f, 1.0f, 1.0f, },
    };
    float dst_pixels_expected[kTestCount][kPixelCount][3] = {
            {
                    { 0.f,     0.f,     0.f,     },
                    { 0.3126f, 0.3125f, 0.3125f, },
                    { 0.4061f, 0.f,     0.f,     },
                    { 0.f,     0.3475f, 0.f,     },
                    { 0.f,     0.f,     0.4426f, },
                    { 1.f,     1.f,     1.f,     },
            },
            {
                    { 0.f,     0.f,     0.f,     },
                    { 0.1044f, 0.1044f, 0.1044f, },
                    { 0.1044f, 0.f,     0.f,     },
                    { 0.f,     0.1044f, 0.f,     },
                    { 0.f,     0.f,     0.1044f, },
                    { 1.f,     1.f,     1.f,     },
            },
            {
                    { 0.f,     0.0f,    0.0f,    },
                    { 0.2140f, 0.2140f, 0.2140f, },
                    { 0.2140f, 0.0f,    0.0f,    },
                    { 0.0f,    0.2140f, 0.0f,    },
                    { 0.0f,    0.0f,    0.2140f, },
                    { 1.0f,    1.0f,    1.0f,    },
            },
    };
    // clang-format on
    bool cicp_expected[kTestCount] = {true, true, false};
    bool a2b_expected[kTestCount] = {true, false, false};
    uint32_t cicp_primaries_expected[kTestCount] = {9, 12, 0};
    uint32_t cicp_trfn_expected[kTestCount] = {16, 18, 0};

    for (size_t test = 0; test < kTestCount; ++test) {
        skcms_ICCProfile parsed;
        REPORTER_ASSERT(r, skcms_Parse(profile[test]->data(), profile[test]->size(), &parsed));

        REPORTER_ASSERT(r, parsed.has_A2B == a2b_expected[test]);
        REPORTER_ASSERT(r, parsed.has_CICP == cicp_expected[test]);
        if (cicp_expected[test]) {
            REPORTER_ASSERT(r, parsed.CICP.color_primaries == cicp_primaries_expected[test]);
            REPORTER_ASSERT(r, parsed.CICP.transfer_characteristics == cicp_trfn_expected[test]);
            REPORTER_ASSERT(r, parsed.CICP.matrix_coefficients == 0);
            REPORTER_ASSERT(r, parsed.CICP.video_full_range_flag == 1);
        }

        skcms_ICCProfile dst_parsed;
        REPORTER_ASSERT(
                r, skcms_Parse(dst_profile[test]->data(), dst_profile[test]->size(), &dst_parsed));

        for (size_t pixel = 0; pixel < kPixelCount; ++pixel) {
            float dst_pixel_actual[3]{0.f, 0.f, 0.f};
            bool xform_result = skcms_Transform(pixels[pixel],
                                                skcms_PixelFormat_RGB_fff,
                                                skcms_AlphaFormat_Opaque,
                                                &parsed,
                                                dst_pixel_actual,
                                                skcms_PixelFormat_RGB_fff,
                                                skcms_AlphaFormat_Opaque,
                                                &dst_parsed,
                                                1);
            REPORTER_ASSERT(r, xform_result);

            auto approx_equal = [=](float x, float y) { return std::abs(x - y) < 1e-3f; };
            for (size_t i = 0; i < 3; ++i) {
                REPORTER_ASSERT(
                        r, approx_equal(dst_pixel_actual[i], dst_pixels_expected[test][pixel][i]));
            }
        }
    }
}
