using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using Klak.Spout;
using Klak.Ndi;
using System;

// 출력 방식 열거형
public enum OutputMethod
{
    None,
    Spout,
    NDI,
    Both
}

public class RenderStreamOutput : MonoBehaviour
{
    [Header("출력 설정")]
    [Tooltip("출력 방식 선택 (Spout, NDI, 또는 둘 다)")]
    public OutputMethod outputMethod = OutputMethod.Spout;
    [Tooltip("알파 채널 유지")]
    public bool keepAlpha = true;

    [Header("카메라 설정")]
    [Tooltip("렌더링 소스로 사용할 카메라")]
    public Camera MainCam;

    [Header("Spout 설정")]
    [Tooltip("Spout 송신 컴포넌트")]
    public SpoutSender Sender;
    [Tooltip("Spout 송신 이름")]
    public string spoutSenderName = "Streamingle Spout Output";

    [Header("NDI 설정")]
    [Tooltip("NDI 송신 컴포넌트")]
    public NdiSender NdiSender;
    [Tooltip("NDI 송신 이름")]
    public string ndiSenderName = "Streamingle NDI Output";

    [Header("쉐이더 설정")]
    [Tooltip("렌더링 결과를 처리할 쉐이더 머티리얼")]
    public Material ShaderContral;

    [Header("텍스처 설정")]
    [Tooltip("텍스처 포맷 설정")]
    public RenderTextureFormat TextureFormat = RenderTextureFormat.DefaultHDR;
    [Tooltip("깊이 버퍼 비트 수")]
    public int DepthBuffer = 24;
    [Tooltip("안티앨리어싱 레벨 (1, 2, 4, 8)")]
    [Range(1, 8)]
    public int AntiAliasing = 1;

    [HideInInspector] public CustomRenderTexture ShaderTexture = null;
    [HideInInspector] public RenderTexture ShaderCameraTexture = null;

    private AlphaRecodingRenderPass m_ScriptablePass;

    // 화면 크기 체크를 위한 변수
    private int screenWidth;
    private int screenHeight;

    // 이전 출력 방식 저장
    private OutputMethod previousOutputMethod;
    // 이전 알파 채널 설정 저장
    private bool previousKeepAlpha;

    private void Awake()
    {
        // 컴포넌트 자동 할당
        MainCam = MainCam == null ? Camera.main : MainCam;

        // 초기 설정 저장
        previousOutputMethod = outputMethod;
        previousKeepAlpha = keepAlpha;

        // 출력 컴포넌트 초기화
        InitializeOutputComponents();

        InitializeTextures();
    }

    private void InitializeOutputComponents()
    {
        // Spout 송신 컴포넌트 초기화
        if (outputMethod == OutputMethod.Spout || outputMethod == OutputMethod.Both)
        {
            if (Sender == null)
            {
                Sender = GetComponent<SpoutSender>();
                if (Sender == null)
                {
                    Sender = gameObject.AddComponent<SpoutSender>();
                    Sender.spoutName = spoutSenderName;
                    Sender.keepAlpha = keepAlpha;
                    Sender.captureMethod = Klak.Spout.CaptureMethod.Texture;
                }
            }
        }
        // NDI 송신 컴포넌트 초기화
        if (outputMethod == OutputMethod.NDI || outputMethod == OutputMethod.Both)
        {
            if (NdiSender == null)
            {
                NdiSender = GetComponent<NdiSender>();
                if (NdiSender == null)
                {
                    Debug.Log("NDI 송신 컴포넌트를 생성합니다.");
                    NdiSender = gameObject.AddComponent<NdiSender>();
                    if (NdiSender != null)
                    {
                        NdiSender.ndiName = ndiSenderName;
                        NdiSender.keepAlpha = keepAlpha;
                        NdiSender.captureMethod = Klak.Ndi.CaptureMethod.Texture;
                    }
                    else
                    {
                        Debug.LogError("NDI 송신 컴포넌트 생성 실패.");
                    }
                }
            }
        }
    }

    private void OnEnable()
    {
        InitializeScriptablePass();
        RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
    }

    private void OnDisable()
    {
        RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
    }

    private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        if (camera == MainCam)
        {
            var data = camera.GetUniversalAdditionalCameraData();
            data.scriptableRenderer.EnqueuePass(m_ScriptablePass);
        }
    }

    private void InitializeTextures()
    {
        // 카메라가 null인 경우 체크
        if (MainCam == null)
        {
            Debug.LogError("MainCam이 설정되지 않았습니다.");
            return;
        }

        // ShaderContral이 null인 경우 체크
        if (ShaderContral == null)
        {
            Debug.LogError("ShaderContral 머티리얼이 설정되지 않았습니다.");
            return;
        }

        // 카메라의 실제 해상도 사용
        screenWidth = MainCam.pixelWidth;
        screenHeight = MainCam.pixelHeight;

        // ShaderTexture 초기화 - 메모리 누수 방지를 위한 확실한 정리
        if (ShaderTexture != null)
        {
            ShaderTexture.Release();
            DestroyImmediate(ShaderTexture); // Destroy 대신 DestroyImmediate 사용
        }
        ShaderTexture = new CustomRenderTexture(screenWidth, screenHeight, TextureFormat);
        ShaderTexture.material = ShaderContral;
        ShaderTexture.updateMode = CustomRenderTextureUpdateMode.Realtime;
        ShaderTexture.antiAliasing = AntiAliasing;

        // ShaderCameraTexture 초기화
        if (ShaderCameraTexture != null)
        {
            ShaderCameraTexture.Release();
            DestroyImmediate(ShaderCameraTexture); // Destroy 대신 DestroyImmediate 사용
        }
        ShaderCameraTexture = new RenderTexture(screenWidth, screenHeight, DepthBuffer, TextureFormat);
        ShaderCameraTexture.Create();
        ShaderCameraTexture.antiAliasing = AntiAliasing;

        // ShaderCameraTexture를 ShaderContral의 _MainTex에 할당
        if (ShaderContral != null)
        {
            ShaderContral.SetTexture("_MainTex", ShaderCameraTexture);
            UpdateShaderScreenSize();
        }

        // 출력 방식에 따라 텍스처 할당
        UpdateOutputSources();

        // m_ScriptablePass 재초기화
        InitializeScriptablePass();
    }

    private void UpdateOutputSources()
    {
        // Spout 출력 설정
        if (outputMethod == OutputMethod.Spout || outputMethod == OutputMethod.Both)
        {
            if (Sender != null)
            {
                Sender.enabled = true;
                Sender.sourceTexture = ShaderTexture;
                Sender.spoutName = spoutSenderName;
                Sender.keepAlpha = keepAlpha;
                Sender.captureMethod = Klak.Spout.CaptureMethod.Texture;
            }
        }
        else if (Sender != null)
        {
            Sender.enabled = false;
        }

        // NDI 출력 설정
        if (outputMethod == OutputMethod.NDI || outputMethod == OutputMethod.Both)
        {
            if (NdiSender != null)
            {
                NdiSender.enabled = true;
                NdiSender.sourceTexture = ShaderTexture;
                NdiSender.ndiName = ndiSenderName;
                NdiSender.keepAlpha = keepAlpha;
                NdiSender.captureMethod = Klak.Ndi.CaptureMethod.Texture;
            }
            else
            {
                // NDI 송신 컴포넌트가 없으면 다시 초기화 시도
                InitializeOutputComponents();
            }
        }
        else if (NdiSender != null)
        {
            NdiSender.enabled = false;
        }
    }

    private void InitializeScriptablePass()
    {
        m_ScriptablePass = new AlphaRecodingRenderPass(ShaderCameraTexture, ShaderContral, ShaderTexture);
        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
    }

    private void Update()
    {
        // 화면 크기 변경 체크 - 매 프레임마다 Screen.width/height 접근은 비용이 있음
        // 변경이 자주 일어나지 않으므로 더 효율적인 방법으로 변경
        if (MainCam != null && (screenWidth != MainCam.pixelWidth || screenHeight != MainCam.pixelHeight))
        {
            screenWidth = MainCam.pixelWidth;
            screenHeight = MainCam.pixelHeight;
            InitializeTextures();
            UpdateShaderScreenSize();
        }

        // 설정이 변경되었는지 확인
        if (previousOutputMethod != outputMethod || previousKeepAlpha != keepAlpha)
        {
            previousOutputMethod = outputMethod;
            previousKeepAlpha = keepAlpha;
            InitializeOutputComponents();
            UpdateOutputSources();
        }
    }

    private void UpdateShaderScreenSize()
    {
        if (ShaderContral != null)
        {
            ShaderContral.SetVector("_Resolution", new Vector4(screenWidth, screenHeight, 0, 0));
        }
    }

    private void OnDestroy()
    {
        if (ShaderCameraTexture != null)
        {
            ShaderCameraTexture.Release();
            DestroyImmediate(ShaderCameraTexture);
            ShaderCameraTexture = null; // 참조 제거
        }
        if (ShaderTexture != null)
        {
            ShaderTexture.Release();
            DestroyImmediate(ShaderTexture);
            ShaderTexture = null; // 참조 제거
        }
    }
}

public class AlphaRecodingRenderPass : ScriptableRenderPass
{
    private RenderTexture m_ShaderCameraTexture;
    private Material m_ShaderContral;
    private CustomRenderTexture m_ShaderTexture;

    public AlphaRecodingRenderPass(RenderTexture shaderCameraTexture, Material shaderContral, CustomRenderTexture shaderTexture)
    {
        m_ShaderCameraTexture = shaderCameraTexture;
        m_ShaderContral = shaderContral;
        m_ShaderTexture = shaderTexture;
    }

    [Obsolete("This method is obsolete. Use the new Render Graph API instead.")]
    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        try
        {
            if (m_ShaderCameraTexture == null || m_ShaderContral == null || !m_ShaderCameraTexture.IsCreated())
                return;

            CommandBuffer cmd = CommandBufferPool.Get("Alpha Recoding Pass");

            try
            {
                // Update cameraColorTargetHandle usage
                var colorTarget = renderingData.cameraData.renderer.cameraColorTargetHandle;

                // 최종 렌더링 결과를 가져옵니다
                RenderTargetIdentifier source = colorTarget;
                cmd.Blit(source, m_ShaderCameraTexture);

                // ShaderCameraTexture를 ShaderContral의 _MainTex에 할당
                m_ShaderContral.SetTexture("_MainTex", m_ShaderCameraTexture);

                // ShaderTexture 업데이트 (필요한 경우)
                if (m_ShaderTexture != null)
                {
                    m_ShaderTexture.Update();
                }

                context.ExecuteCommandBuffer(cmd);
            }
            finally
            {
                CommandBufferPool.Release(cmd);
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"Alpha Recoding 렌더 패스 실행 중 오류 발생: {e.Message}");
        }
    }
}
