using UnityEngine;
using Concentus.Structs;
using Concentus.Enums;

public class OpusMicRecorder : MonoBehaviour
{
    [Header("Audio Settings")]
    public int sampleRate = 16000;
    public int frameSizeMs = 20; // 20 میلی‌ثانیه

    [Header("Bandpass Filter Frequencies (Hz)")]
    [Range(50f, 1000f)]
    public float lowFreq = 300f;

    [Range(1000f, 8000f)]
    public float highFreq = 3400f;

    private AudioClip micClip;
    private int frameSizeSamples;
    private int lastSamplePos = 0;

    private OpusEncoder encoder;
    private BandpassFilter voiceFilter;

    public delegate void EncodedAudioReady(byte[] data);
    public event EncodedAudioReady OnEncodedAudio;

    void Start()
    {
        frameSizeSamples = (sampleRate / 1000) * frameSizeMs;

        encoder = new OpusEncoder(sampleRate, 1, OpusApplication.OPUS_APPLICATION_VOIP);
        encoder.Bitrate = 16000;

        micClip = Microphone.Start(null, true, 1, sampleRate);

        // اعتبارسنجی ورودی فرکانس ها
        if (lowFreq >= highFreq)
        {
            Debug.LogWarning("lowFreq باید کمتر از highFreq باشد. تنظیم به مقادیر پیش‌فرض.");
            lowFreq = 300f;
            highFreq = 3400f;
        }

        voiceFilter = new BandpassFilter(sampleRate, lowFreq, highFreq);
    }

    void Update()
    {
        int micPos = Microphone.GetPosition(null);
        int available = micPos - lastSamplePos;
        if (available < 0) available += micClip.samples;
        if (available < frameSizeSamples) return;

        float[] samples = new float[frameSizeSamples];
        micClip.GetData(samples, lastSamplePos);
        lastSamplePos = (lastSamplePos + frameSizeSamples) % micClip.samples;

        // آپدیت فیلتر اگر کاربر در Inspector مقادیر را تغییر داد
        if (voiceFilter.LowFreq != lowFreq || voiceFilter.HighFreq != highFreq)
        {
            voiceFilter.SetFrequencies(lowFreq, highFreq);
        }

        voiceFilter.Process(samples);

        short[] pcm = new short[frameSizeSamples];
        for (int i = 0; i < frameSizeSamples; i++)
            pcm[i] = (short)Mathf.Clamp(samples[i] * short.MaxValue, short.MinValue, short.MaxValue);

        byte[] encoded = new byte[1275];
        int encodedLength = encoder.Encode(pcm, 0, frameSizeSamples, encoded, 0, encoded.Length);

        byte[] packet = new byte[encodedLength];
        System.Buffer.BlockCopy(encoded, 0, packet, 0, encodedLength);
        OnEncodedAudio?.Invoke(packet);
    }
}

// کلاس فیلتر میان‌گذر (Bandpass) با قابلیت تنظیم داینامیک فرکانس‌ها
public class BandpassFilter
{
    private float a0, a1, a2, b1, b2;
    private float z1, z2;

    private float sampleRate;
    public float LowFreq { get; private set; }
    public float HighFreq { get; private set; }

    public BandpassFilter(float sampleRate, float lowFreq, float highFreq)
    {
        this.sampleRate = sampleRate;
        SetFrequencies(lowFreq, highFreq);
        z1 = 0;
        z2 = 0;
    }

    public void SetFrequencies(float lowFreq, float highFreq)
    {
        // اعتبارسنجی ساده
        if (lowFreq <= 0) lowFreq = 50f;
        if (highFreq >= sampleRate / 2f) highFreq = sampleRate / 2f - 100;
        if (lowFreq >= highFreq)
        {
            Debug.LogWarning("BandpassFilter: lowFreq must be less than highFreq");
            return;
        }

        LowFreq = lowFreq;
        HighFreq = highFreq;

        float omegaL = 2.0f * Mathf.PI * LowFreq / sampleRate;
        float omegaH = 2.0f * Mathf.PI * HighFreq / sampleRate;

        float centerFreq = (omegaL + omegaH) / 2.0f;
        float bandwidth = omegaH - omegaL;
        float Q = centerFreq / bandwidth;

        float alpha = Mathf.Sin(centerFreq) / (2.0f * Q);
        float cosW0 = Mathf.Cos(centerFreq);
        float norm = 1.0f + alpha;

        a0 = alpha / norm;
        a1 = 0;
        a2 = -alpha / norm;
        b1 = -2.0f * cosW0 / norm;
        b2 = (1.0f - alpha) / norm;

        // ریست فیلتر برای جلوگیری از artifact بعد تغییر فرکانس
        z1 = 0;
        z2 = 0;
    }

    public void Process(float[] samples)
    {
        for (int i = 0; i < samples.Length; i++)
        {
            float input = samples[i];
            float output = a0 * input + a1 * z1 + a2 * z2 - b1 * z1 - b2 * z2;

            z2 = z1;
            z1 = output;

            samples[i] = output;
        }
    }
}
