using UnityEngine;
using UnityEngine.Rendering;

[RequireComponent(typeof(Camera))]
public sealed class OutlineFilter : MonoBehaviour {
    [SerializeField]
    private Color color = Color.white;
    [SerializeField, Range(0.1f, 10.0f)]
    private float width = 1.0f;
    [SerializeField, Range(0f, 1f)]
    private float cutOut = 0f;
    [SerializeField]
    private LayerMask outlineLayer = default;

    private Shader flatColorShader;
    private Material haloMaterial;
    private Material blurMaterial;
    private Camera _camera;
    private Camera renderTextureCamera;
    private int idOffsets = 0;
    private int idGlowTex = 0;
    private int idGlowColor = 0;
    private int idCutOut = 0;

    void Awake(){
        _camera = GetComponent<Camera>();
        flatColorShader = Shader.Find("Hidden/USAYALib/Outline/FlatColor");
        blurMaterial = Shader.Find("Hidden/USAYALib/Outline/Blur").CreateHiddenMaterial();
        haloMaterial = Shader.Find("Hidden/USAYALib/Outline/Halo").CreateHiddenMaterial();

        renderTextureCamera = new GameObject("OutlineRendererTextureCamera").AddComponent<Camera>();
        renderTextureCamera.transform.parent = _camera.gameObject.transform;
        renderTextureCamera.enabled = false;

        idOffsets = Shader.PropertyToID("_Offsets");
        idGlowTex = Shader.PropertyToID("_GlowTex");
        idGlowColor = Shader.PropertyToID("_GlowColor");
        idCutOut = Shader.PropertyToID("_CutOut");
    }

    void OnRenderImage(RenderTexture src, RenderTexture dst){
        Graphics.Blit(src, dst); // prepare main back framebuffer

        renderTextureCamera.CopyFrom(_camera);
        renderTextureCamera.clearFlags = CameraClearFlags.Color;
        renderTextureCamera.backgroundColor = Color.black; // r as mask, should be black here

        var renderTexture1 = RenderTexture.GetTemporary(src.width, src.height, 16, RenderTextureFormat.R8);
        renderTextureCamera.targetTexture = renderTexture1;

        renderTextureCamera.cullingMask = outlineLayer;
        renderTextureCamera.RenderWithShader(flatColorShader, "");

        // blur pass
        int shrink = 1; // shrink == 1 for best quality, try 2 or 4 for low quality but better performance
        var renderTexture2 = RenderTexture.GetTemporary(Screen.width / shrink, Screen.height / shrink, 0);
        var renderTexture3 = RenderTexture.GetTemporary(Screen.width / shrink, Screen.height / shrink, 0);
        DoBlurPass(renderTexture1, renderTexture2, Vector2.up);
        DoBlurPass(renderTexture2, renderTexture3, Vector2.right);

        // copy the temporary RT to the final image
        haloMaterial.SetTexture(idGlowTex, renderTexture1);
        haloMaterial.SetColor(idGlowColor, color);
        haloMaterial.SetFloat(idCutOut, cutOut);
        Graphics.Blit(renderTexture3, dst, haloMaterial);

        RenderTexture.ReleaseTemporary(renderTexture1);
        RenderTexture.ReleaseTemporary(renderTexture2);
        RenderTexture.ReleaseTemporary(renderTexture3);
    }
    private void DoBlurPass(RenderTexture input, RenderTexture output, Vector2 direction){
        var blurPixelOffset = width * input.height / 1920.0f;
        blurMaterial.SetVector(idOffsets, direction * blurPixelOffset);
        Graphics.Blit(input, output, blurMaterial);
    }
}
