/**********************************************************************************
* Blueprint Reality Inc. CONFIDENTIAL
* 2021 Blueprint Reality Inc.
* All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains, the property of
* Blueprint Reality Inc. and its suppliers, if any.  The intellectual and
* technical concepts contained herein are proprietary to Blueprint Reality Inc.
* and its suppliers and may be covered by Patents, pending patents, and are
* protected by trade secret or copyright law.
*
* Dissemination of this information or reproduction of this material is strictly
* forbidden unless prior written permission is obtained from Blueprint Reality Inc.
***********************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

namespace BlueprintReality.Interprocess.Textures
{
    [Serializable]
    public class SharedSurfaceReader : IDisposable
    {
        private int readerIndex;

        public Texture2D Texture { get; protected set; }
        public bool ReadInProgress { get; protected set; }
        private bool readOnce;

        public ulong SyncTime { get; protected set; }
        public ulong RenderTime { get; protected set; }
        public ulong FrameIndex { get; protected set; }

        IntPtr insertFenceFunc;

        public SharedSurfaceReader(string frameId)
        {
            insertFenceFunc = GetInsertFenceForReadCompleteFunc();

            readerIndex = CreateSharedTextureReader(frameId);
            AcquireSharedTexture();
        }
        public void Dispose()
        {
            ReleaseSharedTexture();
            DisposeSharedTextureReader(readerIndex);
        }

        void AcquireSharedTexture()
        {
            IntPtr sharedTexSrv;
            int texWidth, texHeight, texFmtVal;
            GetReaderTextureInfo(readerIndex, out sharedTexSrv, out texWidth, out texHeight, out texFmtVal);

            if (sharedTexSrv == IntPtr.Zero)
                return;

            TextureFormat engineFmt = WatchedUnitySharedTexture.GetUnityFormatFromDirectXFormat((uint)texFmtVal);
            Texture = Texture2D.CreateExternalTexture(texWidth, texHeight, engineFmt, false, true, sharedTexSrv);
        }
        void ReleaseSharedTexture()
        {
            if (Texture == null)
                return;
            Texture2D.Destroy(Texture);
        }

        public bool WaitUntilReadyForRead(int timeout)
        {
            if (!WaitForReadSignal(readerIndex, timeout))
                return false;

            ReadInProgress = true;
            ulong newSyncTime, newRenderTime, newFrameIndex;
            bool texChange = UpdateSharedTextureReaderState(readerIndex, out newSyncTime, out newRenderTime, out newFrameIndex);

            if (readOnce && newFrameIndex == FrameIndex)
                Debug.LogError("Didn't get an updated frame index! " + newFrameIndex);

            if (texChange)
            {
                ReleaseSharedTexture();
                AcquireSharedTexture();
            }

            SyncTime = newSyncTime;
            RenderTime = newRenderTime;
            FrameIndex = newFrameIndex;

            readOnce = true;

            return true;
        }
        public void SignalReadyForWrite()
        {
            ReadInProgress = false;
            SignalReadComplete(readerIndex);
        }

        public void InsertFenceForReadCompletion()
        {
            ReadInProgress = false; //not exactly true
            GL.IssuePluginEvent(insertFenceFunc, readerIndex);
        }

        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern int CreateSharedTextureReader([MarshalAs(UnmanagedType.LPWStr)] string frameId);

        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern void DisposeSharedTextureReader(int readerIndex);

        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern bool GetReaderTextureInfo(int readerIndex, out IntPtr outTexSrv, out int outTexWidth, out int outTexHeight, out int outTexFmt);

        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern bool WaitForReadSignal(int readerIndex, int timeout);
        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern bool UpdateSharedTextureReaderState(int readerIndex, out ulong syncTime, out ulong renderTime, out ulong frameIndex);
        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern void SignalReadComplete(int readerIndex);

        [DllImport(MixCastInteropPlugin.DllName)]
        private static extern IntPtr GetInsertFenceForReadCompleteFunc();
    }
}
