using System.Collections;
using UnityEngine;
using Unity.Barracuda;
using TMPro;
using UnityEngine.UI; // Untuk komponen Image

public class OrganikClassifier : MonoBehaviour
{
    [Header("Model Barracuda")]
    public NNModel onnxModel;
    private Model runtimeModel;
    private IWorker worker;

    [Header("UI Text")]
    public TextMeshProUGUI outputText;
    public TextMeshProUGUI scoreText;

    [Header("UI Image")]
    public Image displayImage; // Menampilkan foto hasil kamera

    [Header("Feedback Images")]
    public Image feedbackCorrectImage; // Image checklist
    public Image feedbackWrongImage;   // Image silang
    public float feedbackDelay = 1.5f;   // Delay sebelum muncul canvas (dalam detik)

    [Header("Canvas")]
    public GameObject canvasBerhasil;
    public GameObject canvasGagal;

    [Header("Audio")]
    [SerializeField] private AudioSource audioSource;
    [SerializeField] private AudioClip correctClip;
    [SerializeField] private AudioClip wrongClip;
    [SerializeField] private AudioClip awardClip; // Pastikan penamaan konsisten (gunakan huruf kecil)

    // Urutan kelas (sesuai training)
    private string[] classes = { "anorganik", "bukansampah", "organik" };

    private int inputWidth = 224;
    private int inputHeight = 224;

    private int currentScore = 0;
    private int maxScore = 5;

    void Start()
    {
        // Muat model dari asset
        runtimeModel = ModelLoader.Load(onnxModel);
        // Buat worker untuk melakukan inferensi (auto memilih antara CPU/GPU)
        worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Auto, runtimeModel);

        // Pastikan feedback image tersembunyi awalnya
        if (feedbackCorrectImage != null)
            feedbackCorrectImage.gameObject.SetActive(false);
        if (feedbackWrongImage != null)
            feedbackWrongImage.gameObject.SetActive(false);

        UpdateScoreText();
    }

    /// <summary>
    /// Fungsi yang dipanggil oleh tombol untuk membuka kamera bawaan dan mengambil gambar.
    /// </summary>
    public void TakePicture()
    {
        NativeCamera.Permission permission = NativeCamera.CheckPermission(true);
        if (permission == NativeCamera.Permission.Denied || permission == NativeCamera.Permission.ShouldAsk)
        {
            NativeCamera.RequestPermission(true);
        }

        NativeCamera.TakePicture((path) =>
        {
            if (!string.IsNullOrEmpty(path))
            {
                // Load gambar dengan ukuran target
                Texture2D texture = NativeCamera.LoadImageAtPath(path, inputWidth);
                if (texture == null)
                {
                    Debug.LogError("Gagal memuat gambar dari path: " + path);
                    return;
                }
                // Pastikan texture bisa diakses (readable)
                Texture2D readableTexture = MakeTextureReadable(texture);
                // Koreksi orientasi gambar dari kamera (sesuaikan rotasinya jika perlu)
                Texture2D correctedTexture = CorrectCameraImage(readableTexture);
                
                // Tampilkan gambar di UI Image (opsional)
                if (displayImage != null)
                {
                    Sprite newSprite = Sprite.Create(correctedTexture, 
                        new Rect(0, 0, correctedTexture.width, correctedTexture.height), 
                        new Vector2(0.5f, 0.5f));
                    displayImage.sprite = newSprite;
                }
                
                // Proses gambar yang diambil dengan inferensi
                RunInference(correctedTexture);
            }
        }, maxSize: inputWidth);
    }

    /// <summary>
    /// Melakukan inferensi pada gambar yang diberikan.
    /// </summary>
    public void RunInference(Texture2D inputTexture)
    {
        if (inputTexture == null)
        {
            Debug.LogError("Gambar tidak tersedia untuk inferensi!");
            return;
        }

        // Resize texture ke ukuran input model
        Texture2D resized = ResizeTexture(inputTexture, inputWidth, inputHeight);

        // Dapatkan data piksel dan lakukan normalisasi ke rentang [-1, 1]
        Color32[] pixels = resized.GetPixels32();
        float[] floatValues = new float[inputWidth * inputHeight * 3];

        for (int i = 0; i < pixels.Length; i++)
        {
            floatValues[i * 3 + 0] = (pixels[i].r / 127.5f) - 1.0f;
            floatValues[i * 3 + 1] = (pixels[i].g / 127.5f) - 1.0f;
            floatValues[i * 3 + 2] = (pixels[i].b / 127.5f) - 1.0f;
        }

        // Buat tensor input dengan dimensi [1, inputHeight, inputWidth, 3]
        Tensor inputTensor = new Tensor(1, inputHeight, inputWidth, 3, floatValues);
        worker.Execute(inputTensor);
        Tensor outputTensor = worker.PeekOutput();

        float[] scores = outputTensor.ToReadOnlyArray();
        int predictedIndex = ArgMax(scores);
        float confidence = scores[predictedIndex];
        string predictedClass = classes[predictedIndex];

        Debug.Log($"Predicted Class: {predictedClass}\nConfidence: {confidence:F2}");

        if (outputText != null)
        {
            outputText.text = $"Predicted Class: {predictedClass}\nConfidence: {confidence:F2}";
        }

        // Logika skor dan feedback
        if (predictedClass == "organik")
        {
            currentScore++;
            UpdateScoreText();
            if (audioSource != null && correctClip != null)
            {
                audioSource.PlayOneShot(correctClip);
            }
            // Jika sudah mencapai target (5 kali)
            if (currentScore >= maxScore)
            {
                StartCoroutine(ShowFeedback(true, true)); // benar dan final
            }
            else
            {
                StartCoroutine(ShowFeedback(true, false)); // benar tapi belum final
            }
        }
        else
        {
            if (audioSource != null && wrongClip != null)
            {
                audioSource.PlayOneShot(wrongClip);
            }
            // Jika salah, tampilkan feedback dan canvas gagal (final)
            StartCoroutine(ShowFeedback(false, true));
        }

        inputTensor.Dispose();
        outputTensor.Dispose();
    }

    /// <summary>
    /// Coroutine untuk menampilkan image feedback (checklist atau silang) selama delay tertentu,
    /// kemudian jika isFinal true, akan menampilkan canvas hasil dan (jika benar) memutar awardClip.
    /// </summary>
    private IEnumerator ShowFeedback(bool isCorrect, bool isFinal)
    {
        // Tampilkan image feedback
        if (isCorrect)
        {
            if (feedbackCorrectImage != null)
                feedbackCorrectImage.gameObject.SetActive(true);
        }
        else
        {
            if (feedbackWrongImage != null)
                feedbackWrongImage.gameObject.SetActive(true);
        }

        // Tunggu selama feedbackDelay
        yield return new WaitForSeconds(feedbackDelay);

        // Sembunyikan image feedback
        if (isCorrect)
        {
            if (feedbackCorrectImage != null)
                feedbackCorrectImage.gameObject.SetActive(false);
        }
        else
        {
            if (feedbackWrongImage != null)
                feedbackWrongImage.gameObject.SetActive(false);
        }

        // Jika isFinal true, tampilkan canvas hasil dan putar awardClip (jika benar)
        if (isFinal)
        {
            if (isCorrect)
            {
                if (audioSource != null && awardClip != null)
                {
                    audioSource.PlayOneShot(awardClip);
                }
                if (canvasBerhasil != null)
                    canvasBerhasil.SetActive(true);
            }
            else
            {
                if (canvasGagal != null)
                    canvasGagal.SetActive(true);
            }
        }
    }

    /// <summary>
    /// Mengembalikan indeks elemen dengan nilai maksimum pada array.
    /// </summary>
    private int ArgMax(float[] array)
    {
        int index = 0;
        float max = array[0];
        for (int i = 1; i < array.Length; i++)
        {
            if (array[i] > max)
            {
                max = array[i];
                index = i;
            }
        }
        return index;
    }

    /// <summary>
    /// Meresize Texture2D ke ukuran yang diinginkan.
    /// </summary>
    private Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
    {
        RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight);
        RenderTexture.active = rt;
        Graphics.Blit(source, rt);
        Texture2D newTexture = new Texture2D(newWidth, newHeight);
        newTexture.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0);
        newTexture.Apply();
        RenderTexture.active = null;
        RenderTexture.ReleaseTemporary(rt);
        return newTexture;
    }

    /// <summary>
    /// Memperbarui tampilan skor pada UI.
    /// </summary>
    private void UpdateScoreText()
    {
        if (scoreText != null)
        {
            scoreText.text = $"{currentScore}/{maxScore}";
        }
    }

    /// <summary>
    /// Membuat salinan texture yang dapat diakses (readable) dengan RenderTexture.
    /// </summary>
    private Texture2D MakeTextureReadable(Texture2D tex)
    {
        RenderTexture tmp = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
        Graphics.Blit(tex, tmp);
        RenderTexture previous = RenderTexture.active;
        RenderTexture.active = tmp;
        Texture2D readableTex = new Texture2D(tex.width, tex.height);
        readableTex.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0);
        readableTex.Apply();
        RenderTexture.active = previous;
        RenderTexture.ReleaseTemporary(tmp);
        return readableTex;
    }

    /// <summary>
    /// Mengoreksi orientasi gambar yang diambil dari NativeCamera.
    /// Contoh: memutar gambar 90° searah jarum jam.
    /// (Jika perlu, sesuaikan rotasinya sesuai kondisi perangkat.)
    /// </summary>
    private Texture2D CorrectCameraImage(Texture2D original)
    {
        int width = original.width;
        int height = original.height;
        Texture2D rotated = new Texture2D(height, width, original.format, false);

        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                rotated.SetPixel(j, width - i - 1, original.GetPixel(i, j));
            }
        }
        rotated.Apply();
        return rotated;
    }

    private void OnDestroy()
    {
        worker?.Dispose();
    }
}
