/*

 * Copyright (c) 2025 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



#include "ge_frosted_glass_effect.h"



#include <cmath>

#include "ge_log.h"

#include "ge_visual_effect_impl.h"

#include "ge_sdf_rrect_shader_shape.h"

#include "ge_shader_diagnostics.h"



namespace OHOS {

namespace Rosen {



namespace {

template<typename V>

inline V Interpolate(const V& base, const V& dark, float t)

{

    return base * (1.0f - t) + dark * t;

}

} // namespace



namespace FrostedGlassEffect {

void InterpolateAdaptiveParams(Drawing::GEFrostedGlassEffectParams& params)

{

    const auto& t = params.darkScale;

    // Only interpolate adaptive parameters defined in the header:

    // Dark-mode set for FrostedGlassEffect params.

    params.weightsEmboss = Interpolate(params.weightsEmboss, params.darkModeWeightsEmboss, t);

    params.bgRates = Interpolate(params.bgRates, params.darkModeBgRates, t);

    params.bgKBS = Interpolate(params.bgKBS, params.darkModeBgKBS, t);

    params.bgPos = Interpolate(params.bgPos, params.darkModeBgPos, t);

    params.bgNeg = Interpolate(params.bgNeg, params.darkModeBgNeg, t);

    params.edLightAngles = Interpolate(params.edLightAngles, params.darkModeEdLightAngles, t);

    params.edLightKBS = Interpolate(params.edLightKBS, params.darkModeEdLightKBS, t);

}

} // namespace FrostedGlassEffect



thread_local static std::shared_ptr<Drawing::RuntimeEffect> g_frostedGlassShaderEffect = nullptr;

constexpr size_t NUM_0 = 0;

constexpr size_t NUM_1 = 1;

constexpr size_t NUM_2 = 2;

static constexpr char MAIN_SHADER_PROG[] = R"(

    // -----------------------------------------------------------------------------

    // Rounded-rect frosted glass style shader with toggleable effects.

    // Groups are ordered as: COMMON → BACKGROUND → INNER SHADOW → ENV LIGHT → HIGHLIGHT → main

    // Each effect block puts "parameters first, then functions", so you can splice blocks easily.

    // -----------------------------------------------------------------------------



    // ============================================================================

    // 0) COMMON PARAMS & FUNCTIONS (shared by multiple effects)

    // ============================================================================

    uniform shader image;

    uniform shader edgeBlurredImg;

    uniform shader bgBlurredImg;

    uniform shader sdfNormalImg;

    uniform vec2 iResolution;

    // ----- Shape Core -----

    uniform float borderWidth;   // SDF band width for emboss math

    uniform float offset;        // inner band offset for embossing



    // ----- AA / Numeric -----

    const float ANTI_ALIASING = 0.5;  // global AA softness scaling

    const float PI = 3.14159265358979323846;

    const float EPSILON = 1e-4;

    const float N_EPS = 1e-6;



    // ----- Buffers / Scale -----

    uniform float downSampleFactor;  // full-res : blur buffer res ratio (1 = same)



    // ----- Common SDF & Geometry helpers -----

    float SdfRRect(vec2 p, vec2 b, float r)

    {

        vec2 q = abs(p) - b + r;

        return length(max(q, 0.0)) - r;

    }



    vec2 SafeNormalize(vec2 v)

    {

        return v / max(length(v), N_EPS);

    }



    vec2 GradRRect(vec2 p, vec2 b, float r)

    {

        float rr = min(r, min(b.x, b.y));

        vec2 w = abs(p) - b - vec2(rr);

        float g = max(w.x, w.y);

        vec2 q = max(w, 0.0);

        float l = length(q);



        vec2 grad;

        if (g > 0.0) {

            grad = (l > N_EPS) ? (q / l) : vec2(1.0, 0.0);

        } else {

            grad = (w.x > w.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);

        }

        grad *= sign(p);

        return grad;  // outward (unnormalized)

    }



    vec2 ToDownsamplePx(vec2 deltaFullPx, float downFactor)

    {

        return deltaFullPx / max(downFactor, N_EPS);

    }



    // ----- Image sampling helpers -----

    vec4 BaseBlur(vec2 coord)

    {

        return bgBlurredImg.eval(coord);  // pre-blurred background

    }



    // Optional original-image pixel sampling (not used in main but kept for reuse)

    vec4 SampleUV(vec2 uv, vec2 res)

    {

        return image.eval(uv + 0.5 * res);

    }



    // ----- Common Color Utilities (Sat / KBS / CompareBlend) -----

    vec3 Sat(vec3 src255, float n, float p1r, float p2r, float p1g, float p2g, float p1b, float p2b)

    {

        // `src255` in [0,255]; `n` is base saturation factor

        float r = src255.r;

        float g = src255.g;

        float b = src255.b;

        float rnn = r * (0.2412016 * (1. - n) + n) + g * (0.6922296 * (1. - n)) + b * (0.0665688 * (1. - n));

        float gnn = r * (0.2412016 * (1. - n)) + g * (0.6922296 * (1. - n) + n) + b * (0.0665688 * (1. - n));

        float bnn = r * (0.2412016 * (1. - n)) + g * (0.6922296 * (1. - n)) + b * (0.0665688 * (1. - n) + n);



        float dr = rnn - r;

        float grt = step(0., dr);

        float rr = (r + dr * p1r) * grt + (r + dr * p2r) * (1. - grt);



        float dg = gnn - g;

        grt = step(0., dg);

        float gg = (g + dg * p1g) * grt + (g + dg * p2g) * (1. - grt);



        float db = bnn - b;

        grt = step(0., db);

        float bb = (b + db * p1b) * grt + (b + db * p2b) * (1. - grt);



        return vec3(rr, gg, bb);

    }



    vec3 ApplyKBS(vec3 c01, float K, float B, float S, float p1r, float p2r, float p1g, float p2g,

        float p1b, float p2b)

    {

        vec3 x = c01 * 255.0;

        x = x * K + vec3(B);

        x = Sat(x, S, p1r, p2r, p1g, p2g, p1b, p2b);

        return clamp(x / 255.0, 0.0, 1.0);

    }



    vec3 CompareBlend(vec3 src, vec3 dst)

    {

        const vec3 grayBase = vec3(0.2412016, 0.6922296, 0.0665688);

        float lb = dot(src, grayBase);

        float le = dot(dst, grayBase);

        return mix(src, dst, le / max((lb + le), EPSILON));

    }



    // ============================================================================

    // 1) BACKGROUND (DARKEN + VIBRANCY)

    //    Parameters first, then functions that specifically shape the background.

    // ============================================================================



    // ----- Params -----

    uniform float bgFactor;  // global attenuation for blurred background



    // ----- Functions specific to background shaping -----

    vec3 BlurVibrancy(vec3 c01)

    {

        vec3 x = c01 * 255.0;

        x = -0.0000289 * pow(x, vec3(3.)) + 0.0108341 * pow(x, vec3(2.)) + 0.0073494 * x + 25.4709106;

        x = Sat(x, 1.2, 0.3, 0.5, 0.5, 0.5, 1., 1.);

        return clamp(x / 255.0, 0.0, 1.0);

    }



    // ============================================================================

    // 2) INNER EDGE SHADOW

    //    Parameters first, then functions used only by inner shadow.

    //    (Shared geometry helpers already live in COMMON.)

    // ============================================================================



    // ----- Params -----

    uniform float innerShadowRefractPx;  // inward offset along outward normal (px, full-res)

    uniform float innerShadowWidth;

    uniform float sdK;                   // K/B/S for inner shadow color

    uniform float sdB;

    uniform float sdS;



    const float innerShadowExp = 4.62;



    // ----- Functions (wrappers specific to inner shadow coloring) -----

    vec3 InnerShadowVibrancy(vec3 c01)

    {

        return ApplyKBS(c01, sdK, sdB, sdS, 1.0, 1.7, 1.5, 3.0, 2.0, 1.0);

    }



    // Optional extra inner-shadow exponential fade (not used in your main now)

    float EdgeExpInAA(vec2 p, vec2 b, float r, float decayLenPx, float aaPx)

    {

        float sd = SdfRRect(p, b, r);

        float x = max(0.0, -sd);

        float core = exp(-x / max(decayLenPx, N_EPS));

        float cover = smoothstep(aaPx, -aaPx, sd);

        return core * cover;

    }



    // ============================================================================

    // 3) ENVIRONMENTAL EDGE LIGHT (outward refraction brightening)

    //    Parameters first, then functions used only by env light.

    // ============================================================================



    // ----- Params -----

    uniform float refractOutPx;  // outward offset along outward normal (px, full-res)

    uniform float envK;          // K/B/S for env light color

    uniform float envB;

    uniform float envS;



    // ----- Functions (wrapper specific to env light coloring) -----

    vec3 EdgeLightVibrancy(vec3 c01)

    {

        return ApplyKBS(c01, envK, envB, envS, 1.0, 1.7, 1.5, 3.0, 2.0, 1.0);

    }



    // ============================================================================

    // 4) DIAGONAL EDGE HIGHLIGHT (band ∩ fan mask)

    //    Parameters first, then functions used only by highlight.

    // ============================================================================



    // ----- Params -----

    uniform float highLightAngleDeg;

    uniform float highLightFeatherDeg;

    uniform float highLightWidthPx;

    uniform float highLightFeatherPx;

    uniform float highLightShiftPx;

    uniform vec2 highLightDirection;

    uniform float hlK;  // K/B/S for highlight color

    uniform float hlB;

    uniform float hlS;



    // ----- Functions (highlight-specific) -----

    float DiagonalFanMask(vec2 posFromCenter, vec2 dir, float angleDeg, float featherDeg)

    {

        vec2 p = normalize(posFromCenter);

        vec2 d = normalize(dir);

        float angle = angleDeg * (PI / 180.0);

        float feather = max(1e-4, featherDeg * (PI / 180.0));

        float c1 = clamp(dot(d, p), -1.0, 1.0);

        float c2 = clamp(dot(-d, p), -1.0, 1.0);

        float theta1 = acos(c1);

        float theta2 = acos(c2);

        float lobe1 = 1.0 - smoothstep(angle * 0.5, angle * 0.5 + feather, theta1);

        float lobe2 = 1.0 - smoothstep(angle * 0.5, angle * 0.5 + feather, theta2);

        return clamp(lobe1 + lobe2, 0.0, 1.0);

    }



    float EdgeBandAA(float sd, float widthPx, float featherPx, float shiftPx)

    {

        float a = max(featherPx, N_EPS);

        float coverOuter = smoothstep(a, -a, sd + shiftPx);

        float coverInner = smoothstep(a, -a, sd + shiftPx + max(widthPx, 0.0));

        return clamp(coverOuter - coverInner, 0.0, 1.0);

    }



    vec3 EdgeHighlightVibrancy(vec3 c01)

    {

        return ApplyKBS(c01, hlK, hlB, hlS, 1.0, 1.7, 1.5, 3.0, 2.0, 1.0);

    }



    // ============================================================================

    // 5) MAIN (you already split logic per-effect; kept intact, only depends on above)

    // ============================================================================

    vec4 main(vec2 fragCoord)

    {

        // Primary & inner-offset SDFs

        vec4 sdfNormal = sdfNormalImg.eval(fragCoord);

        float sd = sdfNormal.a;

        float sdBlack = sd + offset;

        // Two-sided "border" masks → signed emboss pair (pos/neg) bands

        float border =

            smoothstep(-1.0 + ANTI_ALIASING, max(1.0, borderWidth * ANTI_ALIASING * 0.5), -sd * ANTI_ALIASING) -

            smoothstep(min(-borderWidth * ANTI_ALIASING * 0.5, -1.), 1.0 - ANTI_ALIASING,

                       (-sd - borderWidth) * ANTI_ALIASING);

        float borderBlack =

            smoothstep(-1.0 + ANTI_ALIASING, max(1.0, borderWidth * ANTI_ALIASING * 0.5), -sdBlack * ANTI_ALIASING) -

            smoothstep(min(-borderWidth * ANTI_ALIASING * 0.5, -1.), 1.0 - ANTI_ALIASING,

                       (-sdBlack - borderWidth) * ANTI_ALIASING);

        // ------------------------------- BACKGROUND -------------------------------

        vec4 blurredBgColor = BaseBlur(fragCoord) * bgFactor;

        blurredBgColor.rgb = BlurVibrancy(blurredBgColor.rgb);



        // ------------------------------- INNER SHADOW -----------------------------

        float embossNeg = exp(innerShadowExp * (sdBlack - 1.0 + innerShadowWidth));

        if (embossNeg > 0.0) {

            // Map to blur-buffer pixel grid

            vec2 tileSize = iResolution.xy / max(downSampleFactor, 1e-6);

            vec2 uvInTile = fragCoord / iResolution.xy;

            vec2 pixelDS = uvInTile * (tileSize - 1.0) + 0.5;

            // Inward refraction sample

            vec2 nOut = sdfNormal.xy;

            vec2 deltaInDS = ToDownsamplePx(nOut * innerShadowRefractPx, downSampleFactor);

            vec2 negCoord = pixelDS + deltaInDS;

            vec4 refractionNeg = edgeBlurredImg.eval(negCoord) * bgFactor;

            refractionNeg.rgb = BlurVibrancy(refractionNeg.rgb);

            refractionNeg.rgb = CompareBlend(blurredBgColor.rgb, refractionNeg.rgb);

            refractionNeg.rgb = InnerShadowVibrancy(refractionNeg.rgb);

            blurredBgColor = mix(blurredBgColor, refractionNeg, clamp(embossNeg, 0.0, 1.0));

        }



        // ------------------------------- ENV LIGHT --------------------------------

        float embossPos = (border - borderBlack + 1.0) * 0.5 * clamp(border + borderBlack, 0.0, 1.0);

        if (embossPos > 0.0) {

            // Map to blur-buffer pixel grid

            vec2 tileSize = iResolution.xy / max(downSampleFactor, 1e-6);

            vec2 uvInTile = fragCoord / iResolution.xy;

            vec2 pixelDS = uvInTile * (tileSize - 1.0) + 0.5;

            // Outward refraction sample

            vec2 nOut = sdfNormal.xy;

            vec2 deltaOutDS = ToDownsamplePx(nOut * refractOutPx, downSampleFactor);

            vec2 posCoord = pixelDS + deltaOutDS;

            vec4 refractionPos = edgeBlurredImg.eval(posCoord) * bgFactor;

            refractionPos.rgb = CompareBlend(blurredBgColor.rgb, refractionPos.rgb);

            refractionPos.rgb = EdgeLightVibrancy(refractionPos.rgb);

            blurredBgColor = mix(blurredBgColor, refractionPos, clamp(embossPos, 0.0, 1.0));

        }



        // ------------------------------- HIGHLIGHT --------------------------------

        // Centered space for highlight

        vec2 uv = (fragCoord + fragCoord - iResolution.xy) * 0.5;

        float widthClamped = min(highLightWidthPx, max(borderWidth, 0.0));

        float edgeBand = EdgeBandAA(sd, widthClamped, highLightFeatherPx, highLightShiftPx);

        float diagMask = DiagonalFanMask(uv, normalize(highLightDirection), highLightAngleDeg,

            highLightFeatherDeg);

        float edge = edgeBand * diagMask;

        vec3 hlBase = EdgeHighlightVibrancy(blurredBgColor.rgb);

        blurredBgColor = mix(blurredBgColor, vec4(hlBase, 1.0), edge);



        // ------------------------------- BACKGROUND MIX ---------------------------

        blurredBgColor = mix(image.eval(fragCoord), blurredBgColor, clamp(-min(sd, sdBlack), 0.0, 1.0));

        return blurredBgColor.rgb1;

    }

)";



GEFrostedGlassEffect::GEFrostedGlassEffect(const Drawing::GEFrostedGlassEffectParams& params)

{

    frostedGlassEffectParams_ = params;

}



bool GEFrostedGlassEffect::IsValidParam(float width, float height)

{

    if (width < 1e-6 || height < 1e-6) {

        GE_LOGE("GEFrostedGlassEffect::MakeDrawingShader width or height less than 1e-6");

        return false;

    }

    return true;

}



void GEFrostedGlassEffect::MakeDrawingShader(Drawing::Canvas& canvas, const Drawing::Rect& rect, float progress)

{

    drShader_ = nullptr;

    if (!IsValidParam(rect.GetWidth(), rect.GetHeight())) {

        return;

    }



    std::shared_ptr<Drawing::Image> cachedBlurImage = nullptr;

    cachedBlurImage = frostedGlassEffectParams_.blurImage.lock();

    if (cachedBlurImage == nullptr) {

        GE_LOGE("GEFrostedGlassEffect::MakeDrawingShader cachedBlurImage is nullptr");

        return;

    }



    auto shader = Drawing::ShaderEffect::CreateImageShader(*cachedBlurImage, Drawing::TileMode::CLAMP,

        Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), Drawing::Matrix());

    if (shader == nullptr) {

        GE_LOGE("GEFrostedGlassEffect::create shader failed.");

        return;

    }



    auto builder = MakeFrostedGlassShader(canvas, shader, rect);

    if (builder == nullptr) {

        GE_LOGE("GEFrostedGlassEffect::OnProcessImage builder is null");

        return;

    }



    auto frostedGlassShader = builder->MakeShader(nullptr, false);

    if (frostedGlassShader == nullptr) {

        GE_LOGE("GEFrostedGlassEffect::MakeDrawingShader frostedGlassShader is nullptr");

        return;

    }

    drShader_ = frostedGlassShader;

}



bool GEFrostedGlassEffect::InitFrostedGlassEffect()

{

    if (g_frostedGlassShaderEffect == nullptr) {

        g_frostedGlassShaderEffect = GECreateRuntimeEffectForShader(MAIN_SHADER_PROG);

        if (g_frostedGlassShaderEffect == nullptr) {

            GE_LOGE("InitFrostedGlassEffect::RuntimeShader effect error\n");

            return false;

        }

    }

    return true;

}



std::shared_ptr<Drawing::ShaderEffect> GEFrostedGlassEffect::MakeSDFNormalShader(Drawing::Canvas& canvas,

    float width, float height)

{

    if (auto shape = frostedGlassEffectParams_.sdfShape) {

        return shape->GenerateDrawingShaderHasNormal(canvas, width, height);

    }

    return nullptr;

}



std::shared_ptr<Drawing::RuntimeShaderBuilder> GEFrostedGlassEffect::MakeFrostedGlassShader(Drawing::Canvas& canvas,

    std::shared_ptr<Drawing::ShaderEffect> imageShader, const Drawing::Rect& rect)

{

    float imageWidth = rect.GetWidth();

    float imageHeight = rect.GetHeight();



    if (g_frostedGlassShaderEffect == nullptr) {

        if (!InitFrostedGlassEffect()) {

            GE_LOGE("GEFrostedGlassEffect::failed when initializing MagnifierEffect.");

            return nullptr;

        }

    }

    FrostedGlassEffect::InterpolateAdaptiveParams(frostedGlassEffectParams_);



    auto sdfNormalShader = MakeSDFNormalShader(canvas, imageWidth, imageHeight);

    if (!sdfNormalShader) {

        GE_LOGE("GEFrostedGlassEffect::MakeFrostedGlassShader sdfShapeShader is null");

        return nullptr;

    }



    std::shared_ptr<Drawing::RuntimeShaderBuilder> builder =

        std::make_shared<Drawing::RuntimeShaderBuilder>(g_frostedGlassShaderEffect);

    // Common inputs

    builder->SetChild("image", imageShader);

    builder->SetChild("edgeBlurredImg", imageShader);

    builder->SetChild("bgBlurredImg", imageShader);

    builder->SetUniform("iResolution", imageWidth, imageHeight);

    builder->SetChild("sdfNormalImg", sdfNormalShader);

    builder->SetUniform("borderWidth", frostedGlassEffectParams_.envLightParams[NUM_0]);

    builder->SetUniform("offset", 0.0f);

    builder->SetUniform("downSampleFactor", 1.0f);

    // Background darken parameter

    builder->SetUniform("bgFactor", 1.0f);

    // Inner shadow parameters

    builder->SetUniform("innerShadowRefractPx", frostedGlassEffectParams_.sdParams[NUM_0]);

    builder->SetUniform("innerShadowWidth", frostedGlassEffectParams_.sdParams[NUM_1]);

    builder->SetUniform("sdK", frostedGlassEffectParams_.sdKBS[NUM_0]);

    builder->SetUniform("sdB", frostedGlassEffectParams_.sdKBS[NUM_1]);

    builder->SetUniform("sdS", frostedGlassEffectParams_.sdKBS[NUM_2]);

    // Env refraction parameters

    builder->SetUniform("refractOutPx", frostedGlassEffectParams_.refractOutPx);

    builder->SetUniform("envK", frostedGlassEffectParams_.envLightKBS[NUM_0]);

    builder->SetUniform("envB", frostedGlassEffectParams_.envLightKBS[NUM_1]);

    builder->SetUniform("envS", frostedGlassEffectParams_.envLightKBS[NUM_2]);

    // Edge highlights parameters

    builder->SetUniform("highLightAngleDeg", frostedGlassEffectParams_.edLightAngles[NUM_0]);

    builder->SetUniform("highLightFeatherDeg", frostedGlassEffectParams_.edLightAngles[NUM_1]);

    builder->SetUniform("highLightWidthPx", frostedGlassEffectParams_.edLightParams[NUM_0]);

    builder->SetUniform("highLightFeatherPx", frostedGlassEffectParams_.edLightParams[NUM_1]);

    builder->SetUniform("highLightShiftPx", 0.0f);

    builder->SetUniform("highLightDirection", frostedGlassEffectParams_.edLightDir[NUM_0],

                        frostedGlassEffectParams_.edLightDir[NUM_1]);

    builder->SetUniform("hlK", frostedGlassEffectParams_.edLightKBS[NUM_0]);

    builder->SetUniform("hlB", frostedGlassEffectParams_.edLightKBS[NUM_1]);

    builder->SetUniform("hlS", frostedGlassEffectParams_.edLightKBS[NUM_2]);

    return builder;

}

} // namespace Rosen

} // namespace OHOS