﻿using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using System;

[Serializable, VolumeComponentMenu("Post-processing/Snapshot Pro (HDRP)/Outline")]
public sealed class Outline : CustomPostProcessVolumeComponent, IPostProcessComponent
{
    [Range(0.0f, 1.0f), Tooltip("Threshold for colour-based edge detection.")]
    public ClampedFloatParameter colorSensitivity = new ClampedFloatParameter(0.1f, 0.0f, 1.0f);

    [Range(0.0f, 1.0f), Tooltip("Strength of colour-based edges.")]
    public ClampedFloatParameter colorStrength = new ClampedFloatParameter(0.0f, 0.0f, 1.0f);

    [Range(0.0f, 1.0f), Tooltip("Threshold for depth-based edge detection.")]
    public ClampedFloatParameter depthSensitivity = new ClampedFloatParameter(0.01f, 0.0f, 1.0f);

    [Range(0.0f, 1.0f), Tooltip("Strength of depth-based edges.")]
    public ClampedFloatParameter depthStrength = new ClampedFloatParameter(0.0f, 0.0f, 1.0f);

    [Range(0.0f, 1.0f), Tooltip("Threshold for normal-based edge detection.")]
    public ClampedFloatParameter normalSensitivity = new ClampedFloatParameter(0.1f, 0.0f, 1.0f);

    [Range(0.0f, 1.0f), Tooltip("Strength of normal-based edges.")]
    public ClampedFloatParameter normalStrength = new ClampedFloatParameter(0.0f, 0.0f, 1.0f);

    [Tooltip("Pixels past this depth threshold will not be edge-detected.")]
    public ClampedFloatParameter depthThreshold = new ClampedFloatParameter(0.99f, 0.0f, 1.01f);

    Material m_Material;

    public bool IsActive() => m_Material != null && 
        (colorStrength.value > 0.0f || depthStrength.value > 0.0f || normalStrength.value > 0.0f);

    // Remember to add this post process in the Custom Post Process Orders list 
    // (Project Settings > HDRP Default Settings).
    public override CustomPostProcessInjectionPoint injectionPoint => 
        CustomPostProcessInjectionPoint.AfterPostProcess;

    const string kShaderName = "SnapshotProHDRP/Outline";

    public override void Setup()
    {
        if (Shader.Find(kShaderName) != null)
        {
            m_Material = new Material(Shader.Find(kShaderName));
        }
        else
        {
            Debug.LogError($"Unable to find shader '{kShaderName}' for the Outline effect.");
        }
    }

    public override void Render(CommandBuffer cmd, HDCamera camera, RTHandle source, RTHandle destination)
    {
        if (m_Material == null)
        {
            return;
        }

        m_Material.SetFloat("_ColorSensitivity", colorSensitivity.value);
        m_Material.SetFloat("_ColorStrength", colorStrength.value);
        m_Material.SetFloat("_DepthSensitivity", depthSensitivity.value);
        m_Material.SetFloat("_DepthStrength", depthStrength.value);
        m_Material.SetFloat("_NormalsSensitivity", normalSensitivity.value);
        m_Material.SetFloat("_NormalsStrength", normalStrength.value);

        m_Material.SetFloat("_DepthThreshold", depthThreshold.value);

        m_Material.SetTexture("_InputTexture", source);

        HDUtils.DrawFullScreen(cmd, m_Material, destination);
    }

    public override void Cleanup()
    {
        CoreUtils.Destroy(m_Material);
    }
}
