#if UNITY_EDITOR_WIN || UNITY_EDITOR_OSX || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_ANDROID
using System;
using System.Runtime.InteropServices;
using UnityEngine;

namespace Agora.Rtc
{
    internal class TextureManager : MonoBehaviour
    {
        // texture identity
        private int _videoPixelWidth = 0;
        private int _videoPixelHeight = 0;
        private uint _uid = 0;
        private string _channelId = "";
        private VIDEO_SOURCE_TYPE _sourceType = VIDEO_SOURCE_TYPE.VIDEO_SOURCE_CAMERA_PRIMARY;

        private bool _needResize = false;
        private bool _needUpdateInfo = true;
        private bool isFresh = false;

        private IVideoStreamManager _videoStreamManager;
        private IrisVideoFrame _cachedVideoFrame = new IrisVideoFrame();

        // reference count
        private int _refCount = 0;
        private bool _canAttach = false;

        //texture width and height
        public int Width = 0;
        public int Height = 0;

        private Texture2D _texture;
        public Texture2D Texture
        {
            get
            {
                _refCount++;
                AgoraLog.Log("TextureManager refCount Add, Now is: " + _refCount);
                return _texture;
            }
        }

        private void Awake()
        {
            InitTexture();
            InitIrisVideoFrame();
        }

        private void Update()
        {
            if (_needUpdateInfo) return;
            ReFreshTexture();
        }

        private void OnDestroy()
        {
            AgoraLog.Log(string.Format("VideoSurface channel: ${0}, user:{1} destroy", _channelId, _uid));

            if (_videoStreamManager != null)
            {
                _videoStreamManager.DisableVideoFrameBuffer(_sourceType, _uid, _channelId);
                _videoStreamManager.Dispose();
                _videoStreamManager = null;
            }

            FreeMemory();
            DestroyTexture();
        }

        private void InitTexture()
        {
            try
            {
                _texture = new Texture2D(_videoPixelWidth, _videoPixelHeight, TextureFormat.RGBA32, false);
                _texture.Apply();
            }
            catch (Exception e)
            {
                AgoraLog.LogError("Exception e = " + e);
            }
        }

        private void InitIrisVideoFrame()
        {
            _cachedVideoFrame = new IrisVideoFrame
            {
                type = VIDEO_OBSERVER_FRAME_TYPE.FRAME_TYPE_RGBA,
                y_stride = _videoPixelWidth * 4,
                height = _videoPixelHeight,
                width = _videoPixelWidth,
                y_buffer = Marshal.AllocHGlobal(_videoPixelWidth * _videoPixelHeight * 4)
            };
        }

        internal int GetRefCount()
        {
            return _refCount;
        }

        internal bool CanTextureAttach()
        {
            return _canAttach;
        }

        internal void EnableVideoFrameWithIdentity()
        {
            var engine = RtcEngineImpl.Get();
            if (engine != null)
            {
                if (_videoStreamManager == null)
                {
                    _videoStreamManager = ((RtcEngineImpl)engine).GetVideoStreamManager();
                }

                if (_videoStreamManager != null)
                {
                    _videoStreamManager.EnableVideoFrameBuffer(_sourceType, _uid, _channelId);
                    _needUpdateInfo = false;
                }
            }
        }

        internal void ReFreshTexture()
        {
            var ret = _videoStreamManager.GetVideoFrame(ref _cachedVideoFrame, ref isFresh, _sourceType, _uid, _channelId);
            this.Width = _cachedVideoFrame.width;
            this.Height = _cachedVideoFrame.height;

            if (ret == IRIS_VIDEO_PROCESS_ERR.ERR_BUFFER_EMPTY || ret == IRIS_VIDEO_PROCESS_ERR.ERR_NULL_POINTER)
            {
                _canAttach = false;
                //AgoraLog.LogWarning(string.Format("no video frame for user channel: {0} uid: {1}", _channelId, _uid));
                return;
            }
            else if (ret == IRIS_VIDEO_PROCESS_ERR.ERR_SIZE_NOT_MATCHING)
            {
                _needResize = true;
                _videoPixelWidth = _cachedVideoFrame.width;
                _videoPixelHeight = _cachedVideoFrame.height;
                FreeMemory();

                _cachedVideoFrame.type = VIDEO_OBSERVER_FRAME_TYPE.FRAME_TYPE_RGBA;
                _cachedVideoFrame.y_stride = _videoPixelWidth * 4;
                _cachedVideoFrame.height = _videoPixelHeight;
                _cachedVideoFrame.width = _videoPixelWidth;
                _cachedVideoFrame.y_buffer = Marshal.AllocHGlobal(_videoPixelWidth * _videoPixelHeight * 4);
            }
            else
            {
                _canAttach = true;
            }

            if (isFresh)
            {
                try
                {
                    if (_needResize)
                    {
                        _texture.Reinitialize(_videoPixelWidth, _videoPixelHeight);
                        _texture.Apply();
                        _needResize = false;
                    }
                    else
                    {
                        _texture.LoadRawTextureData(_cachedVideoFrame.y_buffer,
                            (int)_videoPixelWidth * (int)_videoPixelHeight * 4);
                        _texture.Apply();
                    }
                }
                catch (Exception e)
                {
                    AgoraLog.Log("Exception e = " + e);
                }
            }
        }

        internal void SetVideoStreamIdentity(uint uid = 0, string channelId = "",
            VIDEO_SOURCE_TYPE source_type = VIDEO_SOURCE_TYPE.VIDEO_SOURCE_CAMERA_PRIMARY)
        {
            _uid = uid;
            _channelId = channelId;
            _sourceType = source_type;
        }

        internal void Detach()
        {
            if (_refCount > 0)
            {
                _refCount--;
                AgoraLog.Log("TextureManager refCount Minus, Now is: " + _refCount);
            }
            return;
        }

        private void DestroyTexture()
        {
            if (_texture != null)
            {
                GameObject.Destroy(_texture);
                _texture = null;
            }
        }

        private void FreeMemory()
        {
            if (_cachedVideoFrame.y_buffer != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(_cachedVideoFrame.y_buffer);
            }
        }

    }
}

#endif