#ifndef TAKEN_FUNCTIONS_DEFINED
#define TAKEN_FUNCTIONS_DEFINED

// Leg gradient
float GetSimplex4DLegs(float3 uv, float3 scale, float speed){
    float4 c = float4(uv * scale, _Time.y * speed);
    float n = 0.5 * Simplex4D(c);
    c *= 2;
    n += 0.25 * Simplex4D(c);
    return (n + 1) * 0.5;
}

// Patches
float GetSimplex3DPatch(float2 coords, float2 scale){
    float3 c = float3(coords * scale, _Time.y*0.5);
    float n = 0;
    float a = 0.5;

    [unroll(3)]
    for (int i = 0; i < 3; i++){
        n += a*Simplex3D(c);
        c = c * 2 + float(i)*0.01;
        a *= 0.5;
    }
    
    return (n + 1) * 0.5;
}

float3 GetBaseColor(v2f i, float3 texCol){
    float3 col = texCol;
    col = lerp(col, 1-col, _Invert);
    if (_Smoothstep == 1){
        col.r = smootherstep(0,1,smootherstep(0,1,col.r));
        col.g = smootherstep(0,1,smootherstep(0,1,col.g));
        col.b = smootherstep(0,1,smootherstep(0,1,col.b));
    }
    return col;
}

void ApplyRim(v2f i, lighting l, inout float3 col, float glowPos){
    float rimMask = tex2D(_RimMask, i.uv).r;
    _RimWidth = lerp(_RimWidth, 0.95, glowPos);
    float rim = pow((1-l.VdotL), (1-_RimWidth) * 10);
    rim = smootherstep(_RimEdge, (1-_RimEdge), rim) * rimMask;
    rim = lerp(rim, lerp(rim*0.1, rim, glowPos), _RimGradMask);
    col += rim * _RimBrightness;
}

void ApplyAOEmiss(v2f i, lighting l, inout float3 col, inout float3 aoEmiss, float glowPos){
    aoEmiss = tex2D(_EmissTex, i.uv).g;
    if (_EmissInvert == 1)
        aoEmiss = 1-aoEmiss;
    if (_Smoothstep == 1)
        aoEmiss = smootherstep(0,1,aoEmiss);
    aoEmiss = saturate(pow(aoEmiss, _EmissPow));
    float emissGradMask = tex2D(_EmissGradMask, i.uv);
    aoEmiss = lerp(aoEmiss, lerp(aoEmiss*0.01, aoEmiss, glowPos), emissGradMask*_EmissGradMasking);
    col += aoEmiss * _EmissStr;
    float patternEmiss0 = tex2D(_NoiseEmission0, i.uvNoiseEmiss0) * _NoiseEmissStrength0;
    patternEmiss0 = lerp(patternEmiss0, lerp(patternEmiss0*0.01, patternEmiss0 * _NoiseEmissionGradStrength0, glowPos), emissGradMask*_NoiseEmissionGradMasking0);
    float patternEmiss1 = tex2D(_NoiseEmission1, i.uvNoiseEmiss1) * _NoiseEmissStrength1;
    patternEmiss1 = lerp(patternEmiss1, lerp(patternEmiss1*0.01, patternEmiss1 * _NoiseEmissionGradStrength1, glowPos), emissGradMask*_NoiseEmissionGradMasking1);
    col += patternEmiss0 + patternEmiss1;
}

void ApplySimplexPatches(v2f i, inout float3 col, float glowPos){
    float noiseMask = tex2D(_NoiseMask, i.uv).r;
    if (noiseMask > 0.00001){
        float noise = GetSimplex3DPatch(i.uv, _NoiseScale);
        noise = smootherstep(_NoiseSmooth,0,noise-(1-_NoiseCutoff));
        noise = lerp(noise*0.5, noise*1.5, glowPos);
        float3 noiseCol = noise * _NoiseBrightness;
        col = lerp(col, noiseCol, noise * noiseMask);
    }
}

void ApplyGradient(v2f i, inout float3 col, inout float glowPos){
    float gradMask = tex2D(_GradientMask, i.uv).r;
    if (gradMask > 0.00001){
        float3 gradPos = 0;
        float glowPosInterp = 0;
        float gradNoise = 1;

        if (_GradientAxis == 0){
            gradPos = float3(i.localPos.x - (_Time.y*_GradientSpeed), i.localPos.yz);
            glowPosInterp = lerp(i.localPos.x, 1-i.localPos.x, _GradientInvert);
        }
        else if (_GradientAxis == 1){
            gradPos = float3(i.localPos.x, i.localPos.y - (_Time.y*_GradientSpeed), i.localPos.z);
            glowPosInterp = lerp(i.localPos.y, 1-i.localPos.y, _GradientInvert);
        }
        else {
            gradPos = float3(i.localPos.xy, i.localPos.z - (_Time.y*_GradientSpeed));
            glowPosInterp = lerp(i.localPos.z, 1-i.localPos.z, _GradientInvert);
        }
        
        glowPos = smootherstep(_GradientHeightMax, _GradientHeightMin, glowPosInterp);
        
        #if !OUTLINE_PASS
            if (_GradientContrast > 0)
                gradNoise = GetSimplex4DLegs(gradPos, _GradientNoiseScale, 0.5);
            gradNoise = lerp(lerp(0.5,gradNoise,_GradientContrast),gradNoise,gradPos);
            gradNoise *= glowPos;
            gradNoise = saturate(gradNoise *_GradientBrightness);
            col += gradNoise;
        #endif
    }
}

void ApplyDissolve(v2f i, inout float3 col){
    #if NON_OPAQUE_ENABLED
        float dissolveTex = tex2D(_DissolveTex, i.uvDis).r;
        float dissolveStr = dissolveTex - _DissolveAmt;
        clip(dissolveStr);
        float rim = dissolveStr;
        float rimWidth = abs(_DissolveRimWidth) * 0.005;
        float3 rimCol = max(0,(rimWidth-rim)/rimWidth) * _Color.rgb * _DissolveRimBrightness;
        col += rimCol;
    #endif
}

void ApplyTransparency(v2f i, float alpha){
    #if NON_OPAQUE_ENABLED
        float2 screenUVs = i.screenPos.xy/(i.screenPos.w+0.0000000001);
        #if UNITY_SINGLE_PASS_STEREO || defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
            screenUVs.x *= 2;
        #endif

        #if CUTOUT_ENABLED
            clip(alpha - _Cutoff);
        #elif DITHER_ENABLED
            clip(Dither(screenUVs, alpha));
        #endif
        
        #if SHADOW_PASS && (FADE_ENABLED || TRANSPARENT_ENABLED)
            clip(Dither(screenUVs, alpha));
        #endif

        #if !FORWARD_PASS && DISSOLVE_ENABLED
            clip(tex2D(_DissolveTex, i.uvDis).r - _DissolveAmt);
        #endif
    #endif
}

#endif