using System;
using UnityEngine;
using UI = UnityEngine.UI;
using Klak.TestTools;
using System.Linq;
using System.Collections.Generic;
using Zenject;

namespace UltraFace
{
    public sealed class Visualizer : MonoBehaviour
    {
        #region Editable attributes

        [SerializeField] ImageSource _source = null;
        [SerializeField, Range(0, 1)] float _threshold = 0.5f;
        [SerializeField] ResourceSet _resources = null;
        [SerializeField] Shader _visualizer = null;
        [SerializeField] Texture2D _texture = null;
        [SerializeField] UI.RawImage _previewUI = null;

        [Inject] private DetectionSetup _detectionSetup;
 
        public int faceCounter => _detector.Detections.Count();
        public float minFaceHeight
        {
            get
            {
                if(_detector.Detections.Count() == 0)
                    return 0f;
                else
                    return _detector.Detections.Min(face => face.y2 - face.y1);
            }
        }
        public float maxFaceHeight
        {
            get
            {
                if(_detector.Detections.Count() == 0)
                    return 0f;
                else
                    return _detector.Detections.Max(face => face.y2 - face.y1);
            }
        }

        public Action<bool> OnDetectionStatusChanged;
        private List<bool> _detectedCounter;
        private bool _isDetected;

        #endregion

        #region Private objects

        FaceDetector _detector;
        Material _material;
        ComputeBuffer _drawArgs;

        #endregion

        #region MonoBehaviour implementation

        void Start()
        {
            _detector = new FaceDetector(_resources);
            _material = new Material(_visualizer);
            _drawArgs = new ComputeBuffer(4, sizeof(uint), ComputeBufferType.IndirectArguments);
            _drawArgs.SetData(new [] {6, 0, 0, 0});
            _detectedCounter = new List<bool>();
        }

        public void Init()
        {
            OnDetectionStatusChanged?.Invoke(false);
        }

        void Update()
        {
            _detector.ProcessImage(_source.Texture, _threshold);
            _previewUI.texture = _source.Texture;

            if(!_detectionSetup.isValidate) return;
            Debug.Log(_detectedCounter.Count);

            _detectedCounter.Add(maxFaceHeight > _detectionSetup.minFaceHeight ? true : false);
            if(_detectedCounter.Count == 128)
            {
                var trueCounter = _detectedCounter.Count(x => x == true);

                if(!_isDetected && trueCounter >= 64)
                {
                    if(maxFaceHeight < _detectionSetup.minFaceHeight) return;
                    _isDetected = true;
                    Debug.LogWarning("лицо определено");
                }
                else if(_isDetected && trueCounter < 64)
                {
                    _isDetected = false;
                    Debug.LogWarning("лицо потеряно");
                }

                OnDetectionStatusChanged?.Invoke(_isDetected);
                _detectedCounter = new List<bool>();
            }
        }

        void OnRenderObject()
        {
            return;

            _detector.SetIndirectDrawCount(_drawArgs);
            _material.SetFloat("_Threshold", _threshold);
            _material.SetTexture("_Texture", _texture);
            _material.SetBuffer("_Detections", _detector.DetectionBuffer);
            _material.SetPass(_texture == null ? 0 : 1);
            Graphics.DrawProceduralIndirectNow(MeshTopology.Triangles, _drawArgs, 0);
        }

        void OnDestroy()
        {
            _detector?.Dispose();
            Destroy(_material);
            _drawArgs?.Dispose();
        }

        #endregion
    }
}