using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.Events;
using System;
using System.IO;

namespace SimplifyXR
{

    public enum BasicResolutionOptions
    {
        highQuality,
        mediumQuality,
        lowQuality
    }

    [System.Serializable]
    public class CameraCaptureOptions
    {
        public string cameraCaptureID;
        //public bool enableViewFinderAndCountdown = true; //not implemented
        public BasicResolutionOptions photoQuality = BasicResolutionOptions.highQuality;
        public string viewFinderText = "Use a Pinch gesture or say Capture to take a photo";
        public int countdownTime = 3;
    }

    public class CameraCapturePanelFeatures : BasePanelFeatures
    {
        [Header("Camera Capture Panel Features")]
        public bool customizeCameraCapturePanelFeatures;

        [Conditional("customizeCameraCapturePanelFeatures", true, ComparisonType.Equals),
            Tooltip("Toggle the default behavior for the enable holograms buttons.")]
        public bool autoEnableHologramButton = true;
        [Conditional("customizeCameraCapturePanelFeatures", true, ComparisonType.Equals),
            Tooltip("Toggle the default behavior for the retake photo button.")]
        public bool autoRetakePhotoButton = true;
        [Conditional("customizeCameraCapturePanelFeatures", true, ComparisonType.Equals),
            Tooltip("Toggle the default behavior for the save photo button.")]
        public bool autoSavePhotoButton = true;
        [Conditional("customizeCameraCapturePanelFeatures", true, ComparisonType.Equals),
            Tooltip("Automatically set the title text from photo filename.")]
        public bool autoImageTitleText = true;

        //manual for now, we should check preprocessor and device/toolkit to determine if voice command capable and what backend to use
        [Conditional("customizeCameraCapturePanelFeatures", true, ComparisonType.Equals),
           Tooltip("Enables photo capture through a Capture voice command.")]
        public bool enableVoiceCommandCapture = true;

        public UnityEvent OnCameraCaptureDisplayed;
        public UnityEvent<string, Sprite> OnPhotoCaptured;
        public UnityEvent<string, Sprite> OnPhotoCaptureSaved;

        Image imageContent => baseRoot.GetElementAsType<Image>("Image Content");
        Button retakeButton => baseRoot.GetElementAsType<Button>("Retake Photo Button");
        Button saveButton => baseRoot.GetElementAsType<Button>("Save Photo Button");
        TextMeshProUGUI viewFinderTMP => baseRoot.GetElementAsType<TextMeshProUGUI>("ViewFinder Text");
        TextMeshProUGUI countdownText => baseRoot.GetElementAsType<TextMeshProUGUI>("Countdown Text");
        GameObject viewFinder => baseRoot.GetElementAsType<GameObject>("ViewFinder");
        GameObject hologramsPrompt => baseRoot.GetElementAsType<GameObject>("Holograms Prompt");
        Button hologramsOnButton => baseRoot.GetElementAsType<Button>("Holograms On Button");
        Button hologramsOffButton => baseRoot.GetElementAsType<Button>("Holograms Off Button");
        GameObject postCaptureDisplay => baseRoot.GetElementAsType<GameObject>("Post Capture Display");

        public IPhotoCapture photoCapture;
        private simpleARInstructionFeatures instructionPanel;

        private bool captureReady = false;
        private bool waitingForCapture = false;
        private bool captureStarted = false;

        //dict for photo count per step, will be moved to instruction report when implemented
        private Dictionary<int,int> photosPerStep = new Dictionary<int,int>();

        private Texture2D currentImageTexture;

        System.IO.DirectoryInfo cameraCaptureDir;
        private string currentPhotoFilename = "";
        private bool useDateFilename = false;
        private int currentStep = 1;
        private int currentPhotoCount = 1;

        private string cameraCaptureID;
        private bool enableHolograms = false;
        //private bool enableViewFinderAndCountdown = true; 
        private BasicResolutionOptions photoQuality = BasicResolutionOptions.highQuality;
        private string viewFinderText = "Use a Pinch gesture or say Capture to take a photo";
        private int countdownTime = 3;
        private float timeRemaining;

        private IGlobalTapMapping globalAirTap;
        private ISpeechHandlerMapping speechHandler;

        private void SelectHolograms(bool enable)
        {
            enableHolograms = enable;
       
            hologramsPrompt.SetActive(false);
            viewFinder.SetActive(true);

            globalAirTap.AirTap.AddListener(HandleAirTapAndSpeech);
            if (enableVoiceCommandCapture)
            speechHandler.RegisterHandler("Capture", HandleAirTapAndSpeech);
        }

        public void StartCapture()
        {
            //edge case for take photo directive
            if (hologramsPrompt.activeInHierarchy)
                SelectHolograms(enableHolograms);
  
            captureStarted = true;
            if (countdownTime > 0.0)
            {
                StartCameraCountdownTimer();
            }
            else
            {
                viewFinder.SetActive(false);
                countdownText.gameObject.SetActive(false);
                SetPhotoFilename();
                SetupPhoto(true);
            }
        }

        private void SetPhotoFilename()
        {
            CheckInstructionStep();

            if (useDateFilename)
            {
                DateTime dt = DateTime.Now;
                currentPhotoFilename = dt.ToString("yyyyMMdd_hhmmss") + ".jpg";
            }
            else
                currentPhotoFilename = "Step" + currentStep + "_" + currentPhotoCount.ToString("D3") + ".jpg";

            if (autoImageTitleText) titleText.text = currentPhotoFilename;
        }

        private void CheckInstructionStep()
        {
            if (instructionPanel != null)
            {
                currentStep = instructionPanel.currentStepNumber;

                if (photosPerStep.TryGetValue(currentStep, out currentPhotoCount))
                {
                    currentPhotoCount++;
                }
                else //no saved photos for this step
                { 
                    currentPhotoCount = 1;
                }
             
                useDateFilename = false;
            }
            else
            {
                useDateFilename = true;
            }
        }

        public void SetupPhoto(bool immediateCapture)
        {
            photoCapture.SetCameraOptions(photoQuality, enableHolograms);
            photoCapture.CreateCapture(immediateCapture);
        }

        public void CapturePhoto()
        {
            if (photoCapture != null)
            {
                photoCapture.SetCameraOptions(photoQuality, enableHolograms);
                photoCapture.CapturePhoto();
            }
        }

        public void RetakePhoto()
        {
            globalAirTap.AirTap.RemoveAllListeners();
            if (enableVoiceCommandCapture)
                speechHandler.UnregisterHandler("Capture", HandleAirTapAndSpeech);

            hologramsPrompt.SetActive(true);
            viewFinder.SetActive(false);
            postCaptureDisplay.SetActive(false);
            captureStarted = false;
        }

        public void SavePhoto()
        {
            string filePath = System.IO.Path.Combine(cameraCaptureDir.FullName, currentPhotoFilename);

            byte[] bytes = currentImageTexture.EncodeToJPG();

            File.WriteAllBytes(filePath, bytes);
            
            //update dict
            if (photosPerStep.ContainsKey(currentStep))
            {
                photosPerStep[currentStep] = currentPhotoCount;
            }
            else
            {
                photosPerStep.Add(currentStep, currentPhotoCount);
            }

            OnPhotoCaptureSaved?.Invoke(cameraCaptureID, imageContent.sprite);
            RetakePhoto(); //for now we go back to inital state
        }

        public void HandleCaptureReady()
        {
            captureReady = true;
            if (waitingForCapture)
            {
                CapturePhoto();
            }
        }

        public void HandlePhotoCaptured(Texture2D tex)
        {
            captureReady = false;
            waitingForCapture = false;

            currentImageTexture = tex;
            imageContent.sprite = Sprite.Create(currentImageTexture, new Rect(0.0f, 0.0f, currentImageTexture.width, currentImageTexture.height), new Vector2(0.5f, 0.5f), 100.0f);
            imageContent.preserveAspect = true;

            //in case we are already disabled discard photo
            if (gameObject.activeInHierarchy)
            {
              
                postCaptureDisplay.SetActive(true);
                OnPhotoCaptured?.Invoke(cameraCaptureID, imageContent.sprite);
            }
        }
          
        void DisplayTime(float timeToDisplay)
        {
            timeToDisplay += 1;
            float seconds = Mathf.FloorToInt(timeToDisplay % 60);
            countdownText.text = string.Format("{0}", seconds);
        }

        public void StartCameraCountdownTimer()
        {
            timeRemaining = countdownTime;
            countdownText.gameObject.SetActive(true);

            SetupPhoto(false);
            StartCoroutine("CameraCountdownTimer");
        }

        public void StopCameraCountdownTimer()
        {
            captureStarted = false;
            if (countdownText)
            countdownText.gameObject.SetActive(false);

            timeRemaining = countdownTime;
            DisplayTime(timeRemaining - 1);

            StopCoroutine("CameraCountdownTimer");
        }

        private IEnumerator CameraCountdownTimer()
        {
            yield return new WaitForSeconds(countdownTime + .1f);

            SetPhotoFilename();
            viewFinder.SetActive(false);
            countdownText.gameObject.SetActive(false);

            //dont re-enable captureStarted until when we retake photo  
            if (captureReady)
                CapturePhoto();
            else
                waitingForCapture = true;
        }

        public void DisplayCamerCapturePanel(CameraCaptureOptions options)
        {
            gameObject.SetActive(false);
            gameObject.SetActive(true);
            SetCameraCaptureOptions(options);

            OnCameraCaptureDisplayed.Invoke();
        }
        public void SetCameraCaptureOptions(CameraCaptureOptions options)
        {
            cameraCaptureID = options.cameraCaptureID;
            //enableViewFinderAndCountdown = options.enableViewFinderAndCountdown;
            photoQuality = options.photoQuality;
            viewFinderText = options.viewFinderText;
            countdownTime = options.countdownTime;
            viewFinderTMP.text = viewFinderText;
        }

        private void OnEnable()
        {
            //disable post-capture options
            postCaptureDisplay.SetActive(false);
            hologramsPrompt.SetActive(true);
            viewFinder.GetComponent<CameraCaptureViewFinder>().AttachAndEnable(Camera.main.gameObject, false);
        }

        private void OnDisable()
        {
            StopCameraCountdownTimer();
            globalAirTap.AirTap.RemoveAllListeners();
            if (enableVoiceCommandCapture)
                speechHandler.UnregisterHandler("Capture", HandleAirTapAndSpeech);
            viewFinder.GetComponent<CameraCaptureViewFinder>().AttachAndEnable(gameObject, true);
        }

        // Start is called before the first frame update
        void Start()
        {
            string folderName = "CameraCapture";
            string filePath = System.IO.Path.Combine(Application.persistentDataPath, folderName);
            cameraCaptureDir = System.IO.Directory.CreateDirectory(filePath);
            
            instructionPanel = FindObjectOfType<simpleARInstructionFeatures>(true);
        }

        private void HandleAirTapAndSpeech()
        {
            if (!captureStarted)
                StartCapture();
        }

        private new void Awake()
        {
            base.Awake();

            globalAirTap = GetComponent<IGlobalTapMapping>();
            photoCapture = GetComponentInChildren<IPhotoCapture>();

            photoCapture?.captureReady.AddListener(HandleCaptureReady);
            photoCapture?.photoCaptured.AddListener(HandlePhotoCaptured);

            instructionPanel = FindObjectOfType<simpleARInstructionFeatures>(true);
            speechHandler = GetComponent<ISpeechHandlerMapping>();
            if (speechHandler == null && enableVoiceCommandCapture)
            {
                Debug.LogError("Cannot enable voice commands as there is no valid handler");
                enableVoiceCommandCapture = false;
            }

            if (autoEnableHologramButton)
            {
                hologramsOnButton.onClick.AddListener(() => SelectHolograms(true)); 
                hologramsOffButton.onClick.AddListener(() => SelectHolograms(false));
            }
            if (autoRetakePhotoButton) retakeButton.onClick.AddListener(RetakePhoto);
            if (autoSavePhotoButton) saveButton.onClick.AddListener(SavePhoto);
        }

        private new void OnDestroy()
        {
            base.OnDestroy();
            photoCapture?.captureReady.RemoveAllListeners();
            photoCapture?.photoCaptured.RemoveAllListeners(); 

            hologramsOnButton.onClick.RemoveAllListeners();
            hologramsOffButton.onClick.RemoveAllListeners();

            retakeButton.onClick.RemoveAllListeners();
            saveButton.onClick.RemoveAllListeners();

            globalAirTap.AirTap.RemoveAllListeners();
            if (enableVoiceCommandCapture)
                speechHandler.UnregisterHandler("Capture", HandleAirTapAndSpeech);
        }

        // Update is called once per frame
        void Update()
        {
            if (captureStarted)
            {
                if (timeRemaining > 0)
                {
                    timeRemaining -= Time.deltaTime;
                }
                else
                {
                    timeRemaining = 0;
                }

                DisplayTime(timeRemaining);
            }
        }
    }
}
