using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NetSDKCS;
using System.Runtime.InteropServices;
using System.Windows.Threading;

namespace NetClient
{
    /// <summary>
    /// realplay client class,inherit baseclient.
    /// </summary>
    public class RealPlayClient : BaseClient
    {
        private RealPlayManager m_RealPlayManager;
        private readonly object m_Lock = new object();
        private List<IntPtr> m_RealPlayHandleList = new List<IntPtr>();
        /// <summary>
        /// realplayhandle list property.only can get.
        /// </summary>
        public List<IntPtr> RealPlayHandleList
        {
            get { return m_RealPlayHandleList; }
        }

        /// <summary>
        /// realplay data callback.
        /// </summary>
        private readonly fRealDataCallBackEx m_RealDataCallBack;
        /// <summary>
        /// realplay disconnect callback.
        /// </summary>
        private readonly fRealPlayDisConnectCallBack m_RealPlayDisConnectCallBack;

        /// <summary>
        /// realplay data received event.
        /// </summary>
        public event RealPlayDataRevHandler RealPlayDataReceived;
        /// <summary>
        /// realplay disconnect event.
        /// </summary>
        public event RealDisConnectHandler RealDisConnected;

        /// <summary>
        /// realplay data received evnent trigger.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="args">realplay data event args</param>
        private void OnRealPlayDataReceived(object sender, RealDataEventArgs args)
        {
            if (RealPlayDataReceived != null)
            {
                if (args.LoginID == LoginID)
                {
                    RealPlayDataReceived(this, args);
                }
            }
        }

        /// <summary>
        /// realplay disconnect event trigger.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnRealDisConnected(object sender, RealDisConnectEventArgs args)
        {
            if (RealDisConnected != null)
            {
                if (args.LoginID == LoginID)
                {
                    RealDisConnected(this, args);
                }
            }
        }

        public RealPlayClient()
        {
            m_RealPlayManager = RealPlayManager.GetInstance();
            m_RealDataCallBack = m_RealPlayManager.GetRealDataCallBack();
            m_RealPlayDisConnectCallBack = m_RealPlayManager.GetRealDisConnectCallBack();
        }

        ~RealPlayClient()
        {
            lock (m_Lock)
            {
                if (m_RealPlayHandleList.Count > 0)
                {
                    Stop();
                }
            }
        }

        /// <summary>
        /// start realplay.
        /// </summary>
        /// <param name="nChannelID">channel id</param>
        /// <param name="hWnd">show vedio handle</param>
        /// <param name="rType">realply type</param>
        /// <returns>realplay handle</returns>
        public IntPtr Start(int nChannelID, IntPtr hWnd, EM_RealPlayType rType)
        {
            IntPtr handle = IntPtr.Zero;
            if(m_RealPlayHandleList.Count == 0)
            {
                m_RealPlayManager.AddDataReceivedEvent(OnRealPlayDataReceived);
                m_RealPlayManager.AddDisConnectedEvent(OnRealDisConnected);
            }
            //call netsdk startrealplay function.
            handle = NETClient.StartRealPlay(LoginID, nChannelID, hWnd, (NetSDKCS.EM_RealPlayType)rType, m_RealDataCallBack, m_RealPlayDisConnectCallBack, LoginID, WaitTime);
            lock (m_Lock)
            {
                m_RealPlayHandleList.Add(handle);
            }
            return handle;
        }

        /// <summary>
        /// stop one realplay.
        /// </summary>
        /// <param name="RealHandle">realpaly handle,start returns value.</param>
        public void Stop(IntPtr RealHandle)
        {
            //call netsdk stoprealplay function.
            NETClient.StopRealPlay(RealHandle);
            lock (m_Lock)
            {
                m_RealPlayHandleList.Remove(RealHandle);
            }
            if (m_RealPlayHandleList.Count == 0)
            {
                m_RealPlayManager.RemoveDataReceivedEvent(OnRealPlayDataReceived);
                m_RealPlayManager.RemoveDisConnectedEvent(OnRealDisConnected);
                RealPlayDataReceived = null;
                RealDisConnected = null;
            }
        }

        /// <summary>
        /// stop all realplay.
        /// </summary>
        public void Stop()
        {
            lock (m_Lock)
            {
                foreach (var item in m_RealPlayHandleList)
                {
                    //call netsdk stoprealplay function.
                    NETClient.StopRealPlay(item);
                }
                m_RealPlayHandleList.Clear();
            }
            m_RealPlayManager.RemoveDataReceivedEvent(OnRealPlayDataReceived);
            m_RealPlayManager.RemoveDisConnectedEvent(OnRealDisConnected);
            RealPlayDataReceived = null;
            RealDisConnected = null;
        }

        /// <summary>
        /// override logout,call stop all realplay before logout device.
        /// </summary>
        public override void Logout()
        {
            Stop();
            base.Logout();
        }


        /// <summary>
        /// override dispose,call realplay logout function.
        /// </summary>
        public override void Dispose()
        {
            Logout();
        }
    }

    public sealed class RealPlayManager
    {
        private readonly fRealDataCallBackEx m_RealDataCallBack;
        private readonly fRealPlayDisConnectCallBack m_RealPlayDisConnectCallBack;

        private event RealPlayDataRevHandler RealPlayDataReceived;
        private void OnRealPlayDataReceived(object sender, RealDataEventArgs arg)
        {
            if (RealPlayDataReceived != null)
            {
                RealPlayDataReceived(sender, arg);
            }
        }
        private event RealDisConnectHandler RealDisConnected;
        private void OnRealDisConnected(object sender, RealDisConnectEventArgs arg)
        {
            if (RealDisConnected != null)
            {
                RealDisConnected(sender, arg);
            }
        }

        private RealPlayManager()
        {
            m_RealDataCallBack = new fRealDataCallBackEx(RealDataCallBack);
            m_RealPlayDisConnectCallBack = new fRealPlayDisConnectCallBack(RealPlayDisConnectCallBack);
        }

        public static RealPlayManager GetInstance()
        {
            if (ClientManager.RealPlayManagerInstance == null)
            {
                lock (ClientManager.RealPlayLock)
                {
                    if (ClientManager.RealPlayManagerInstance == null)
                    {
                        ClientManager.RealPlayManagerInstance = new RealPlayManager();
                    }
                }
            }
            return ClientManager.RealPlayManagerInstance;
        }

        private void RealDataCallBack(IntPtr lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, int param, IntPtr dwUser)
        {
            byte[] buffer = new byte[dwBufSize];
            Marshal.Copy(pBuffer, buffer, 0, (int)dwBufSize);
            OnRealPlayDataReceived(this, new RealDataEventArgs(lRealHandle, dwDataType, buffer, param, dwUser));
        }

        private void RealPlayDisConnectCallBack(IntPtr lRealHandle, EM_REALPLAY_DISCONNECT_EVENT_TYPE dwEventType, IntPtr param, IntPtr dwUser)
        {
            OnRealDisConnected(this, new RealDisConnectEventArgs(lRealHandle, (EM_REALPLAY_DISCONNECT_TYPE)dwEventType, param, dwUser));
        }

        public fRealDataCallBackEx GetRealDataCallBack()
        {
            return m_RealDataCallBack;
        }

        public fRealPlayDisConnectCallBack GetRealDisConnectCallBack()
        {
            return m_RealPlayDisConnectCallBack;
        }

        public void AddDataReceivedEvent(RealPlayDataRevHandler handler)
        {
            RealPlayDataReceived += handler;
        }

        public void RemoveDataReceivedEvent(RealPlayDataRevHandler handler)
        {
            RealPlayDataReceived -= handler;
        }

        public void AddDisConnectedEvent(RealDisConnectHandler handler)
        {
            RealDisConnected += handler;
        }

        public void RemoveDisConnectedEvent(RealDisConnectHandler handler)
        {
            RealDisConnected -= handler;
        }
    }

    public enum EM_RealPlayType
    {
        Realplay = 0,						                                // Real-time preview
        Multiplay,							                                // Multiple-channel preview 
        Realplay_0,				    		                                // Real-time monitor-main stream. It is the same as EM_RType_Realplay
        Realplay_1,					    	                                // Real-time monitor -- extra stream 1
        Realplay_2,					    	                                // Real-time monitor -- extra stream 2
        Realplay_3,					    	                                // Real-time monitor -- extra stream 3
        Multiplay_1,						                                // Multiple-channel preview--1-window 
        Multiplay_4,						                                // Multiple-channel preview--4-window
        Multiplay_8,						                                // Multiple-channel preview--8-window
        Multiplay_9,						                                // Multiple-channel preview--9-window
        Multiplay_16,						                                // Multiple-channel preview--16-window
        Multiplay_6,						                                // Multiple-channel preview--6-window
        Multiplay_12,						                                // Multiple-channel preview--12-window
        Multiplay_25,                                                       // Multiple-channel preview--25-window
        Multiplay_36,                                                       // Multiple-channel preview--36-window
    }

    public enum EM_REALPLAY_DISCONNECT_TYPE
    {
        REAVE,                                                              // resources is taked by advanced user
        NETFORBID,                                                          // forbidden
        SUBCONNECT,                                                         // sublink disconnect
    }
}
