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

namespace NetClient
{
    /// <summary>
    /// support multithreading.
    /// </summary>
    public class PlayBackClient : BaseClient
    {
        /// <summary>
        /// manager playback and download callback.
        /// </summary>
        private PlayBackAndDownloadManager m_PlayBackAndDownloadManager;
        
        /// <summary>
        /// the latest playback handle.
        /// </summary>
        private IntPtr m_PlayHandle = IntPtr.Zero;

        /// <summary>
        /// the latest download handle.
        /// </summary>
        private IntPtr m_FileHandle = IntPtr.Zero;

        /// <summary>
        /// the lock for playback multithreading.
        /// </summary>
        private object m_PlayBackLock = new object();

        /// <summary>
        /// lock for download multithreading.
        /// </summary>
        private object m_DownloadLock = new object();

        /// <summary>
        /// download handle list.
        /// </summary>
        private List<IntPtr> m_FileHandleList = new List<IntPtr>();

        /// <summary>
        /// download handle list property.only can get.
        /// </summary>
        public List<IntPtr> FileHandleList
        {
            get { return m_FileHandleList; }
        }

        /// <summary>
        /// playback handle list.
        /// </summary>
        private List<IntPtr> m_PlayHandleList = new List<IntPtr>();

        /// <summary>
        /// playback handle list proerty.only can get.
        /// </summary>
        public List<IntPtr> PlayHandleList
        {
            get { return m_PlayHandleList; }
        }

        /// <summary>
        /// stream type.
        /// </summary>
        private EM_STREAM_TYPE m_StreamType;

        /// <summary>
        /// stream type property.
        /// </summary>
        public EM_STREAM_TYPE StreamType
        {
            get { return m_StreamType; }
            set { m_StreamType = value; }
        }

        /// <summary>
        /// record type.
        /// </summary>
        private EM_RECORD_TYPE m_RecordType;

        /// <summary>
        /// record tyoe property.
        /// </summary>
        public EM_RECORD_TYPE RecordType
        {
            get { return m_RecordType; }
            set { m_RecordType = value; }
        }

        /// <summary>
        /// download schedule callback.
        /// </summary>
        private readonly fTimeDownLoadPosCallBack m_DownloadCallBack;

        /// <summary>
        /// playback data callback.
        /// </summary>
        private readonly fDataCallBack m_DataCallBack;
        
        /// <summary>
        /// playback schedule callback.
        /// </summary>
        private readonly fDownLoadPosCallBack m_PlayBackPosCallBack;

        /// <summary>
        /// download progress event.user can subscribe this event for received download progress information.
        /// </summary>
        public event DownloadHandler DownloadPosReceived;

        /// <summary>
        /// send download progress information to user. 
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="args">download progress information</param>
        private void OnDownloadPosReceived(object sender, DownloadEventArgs args)
        {
            if (DownloadPosReceived != null)
            {
                lock (m_DownloadLock)
                {
                    foreach (var item in m_FileHandleList)
                    {
                        if (args.FileHandle == item)
                        {
                            if (args.DownloadSize == args.TotalSize)
                            {
                                StopDownload(args.FileHandle);
                            }
                            DownloadPosReceived(this, args);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// playback progress event.user can subscribe this event for received playback progress information.
        /// </summary>
        public event DownloadHandler PlayBackPosReceived;

        /// <summary>
        /// send playback progress information to user.
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="args">playback progress information</param>
        private void OnPlayBackPosReceived(object sender, DownloadEventArgs args)
        {
            if (PlayBackPosReceived != null)
            {
                lock (m_PlayBackLock)
                {
                    foreach (var item in m_PlayHandleList)
                    {
                        if (args.FileHandle == item)
                        {
                            if (args.DownloadSize == args.TotalSize)
                            {
                                StopPlayBack(args.FileHandle);
                            }
                            //support user can process event in main thread
                            Dispatcher.BeginInvoke(PlayBackPosReceived,this, args);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// playback data event.user can subscribe this event for received data information.
        /// </summary>
        public event PlayBackDataHandler DataReceived;

        /// <summary>
        /// send playback data information to user.
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="args">playback data information</param>
        private void OnDataReceived(object sender, PlayBackDataEventArgs args)
        {
            if (DataReceived != null)
            {
                lock (m_PlayBackLock)
                {
                    foreach (var item in m_PlayHandleList)
                    {
                        if (args.PlayBackHandle == item)
                        {
                            DataReceived(this, args);
                            break;
                        }
                    }
                }
            }
        }

        
        /// <summary>
        /// playbackanddownload constructor.call manager singleton.
        /// </summary>
        public PlayBackClient()
        {
            WaitTime = 5000;
            m_PlayBackAndDownloadManager = PlayBackAndDownloadManager.GetInstance();
            m_DownloadCallBack = m_PlayBackAndDownloadManager.GetDownLoadCallBack();
            m_PlayBackPosCallBack = m_PlayBackAndDownloadManager.GetPlayBackDownLoadPosCallBack();
            m_DataCallBack = m_PlayBackAndDownloadManager.GetDataCallBack();
        }

        /// <summary>
        /// set stream type and record type model for netsdk communication with device. 
        /// </summary>
        private void SetDeviceModel()
        {
            IntPtr pStream = IntPtr.Zero;
            IntPtr pRecordType = IntPtr.Zero;
            try
            {
                //set streamType
                pStream = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));
                Marshal.StructureToPtr((int)m_StreamType, pStream, true);
                NETClient.SetDeviceMode(LoginID, EM_USEDEV_MODE.RECORD_STREAM_TYPE, pStream);

                //set recordType
                pRecordType = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));
                Marshal.StructureToPtr((int)m_RecordType, pRecordType, true);
                NETClient.SetDeviceMode(LoginID, EM_USEDEV_MODE.RECORD_TYPE, pRecordType);
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }
            finally
            {
                Marshal.FreeHGlobal(pStream);
                Marshal.FreeHGlobal(pRecordType);
            }
        }

        /// <summary>
        /// query record files.
        /// </summary>
        /// <param name="startTime">start time</param>
        /// <param name="endTime">end time</param>
        /// <param name="channelID">channel id</param>
        /// <param name="maxCount">max files count</param>
        /// <returns>record file info list</returns>
        public List<NET_RECORDFILE_INFO> QueryRecordFiles(DateTime startTime, DateTime endTime, int channelID,int maxCount)
        {
            List<NET_RECORDFILE_INFO> lInfo = new List<NET_RECORDFILE_INFO>();
            if (startTime >= endTime)
            {
                return null;
            }
            SetDeviceModel();
            try
            {
                NetSDKCS.NET_RECORDFILE_INFO[] array_RecordFile_Info = new NetSDKCS.NET_RECORDFILE_INFO[maxCount];
                EM_QUERY_RECORD_TYPE query_Record_Type;
                switch (m_RecordType)
                {
                    case EM_RECORD_TYPE.ALL:
                        query_Record_Type = EM_QUERY_RECORD_TYPE.ALL;
                        break;
                    case EM_RECORD_TYPE.ALARM:
                        query_Record_Type = EM_QUERY_RECORD_TYPE.ALARM;
                        break;
                    case EM_RECORD_TYPE.MOTION:
                        query_Record_Type = EM_QUERY_RECORD_TYPE.MOTION_DETECT;
                        break;
                    default:
                        query_Record_Type = EM_QUERY_RECORD_TYPE.ALL;
                        break;
                }
                int fileCount = 0;
                NETClient.QueryRecordFile(LoginID, channelID, query_Record_Type, startTime, endTime, null, ref array_RecordFile_Info, ref fileCount, (int)WaitTime, false);
                int nFileCount = Math.Min(fileCount, array_RecordFile_Info.Length);
                NET_RECORDFILE_INFO[] aInfo = new NET_RECORDFILE_INFO[nFileCount];
                for (int i = 0; i < nFileCount; i++)
                {
                    aInfo[i] = CopyRecordInfo(array_RecordFile_Info[i]);
                }
                lInfo = aInfo.ToList();
                return lInfo;
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }
        }

        /// <summary>
        /// change netsdk record file to NetDeviceClient record file.
        /// </summary>
        /// <param name="info">netsdk record file</param>
        /// <returns>NetDeviceClient record file</returns>
        private NET_RECORDFILE_INFO CopyRecordInfo(NetSDKCS.NET_RECORDFILE_INFO info)
        {
            NET_RECORDFILE_INFO stuInfo = new NET_RECORDFILE_INFO();
            stuInfo.ch = info.ch;
            stuInfo.filename = info.filename;
            stuInfo.framenum = info.framenum;
            stuInfo.size = info.size;
            stuInfo.starttime = info.starttime.ToDateTime();
            stuInfo.endtime = info.endtime.ToDateTime();
            stuInfo.driveno = info.driveno;
            stuInfo.startcluster = info.startcluster;
            stuInfo.nRecordFileType = info.nRecordFileType;
            stuInfo.bImportantRecID = info.bImportantRecID;
            stuInfo.bHint = info.bHint;
            stuInfo.bRecType = info.bRecType;
            return stuInfo;
        }

        /// <summary>
        /// start playback.
        /// </summary>
        /// <param name="startTime">start time</param>
        /// <param name="endTime">end time</param>
        /// <param name="channelID">channel id</param>
        /// <param name="hwnd">show video handle</param>
        /// <returns>failed return false, successful return true<</returns>
        public bool StartPlayBack(DateTime startTime, DateTime endTime, int channelID, IntPtr hwnd)
        {
            if (startTime >= endTime)
            {
                return false;
            }
            lock (m_PlayBackLock)
            {
                if (m_PlayHandleList.Count == 0)
                {
                    m_PlayBackAndDownloadManager.AddPlayBackDataEvent(OnDataReceived);
                    m_PlayBackAndDownloadManager.AddPlayBackDownLoadEvent(OnPlayBackPosReceived);
                }
            }
            m_PlayHandle = IntPtr.Zero;
            SetDeviceModel();
            try
            {
                NET_IN_PLAY_BACK_BY_TIME_INFO stuInfo = new NET_IN_PLAY_BACK_BY_TIME_INFO();
                NET_OUT_PLAY_BACK_BY_TIME_INFO stuOut = new NET_OUT_PLAY_BACK_BY_TIME_INFO();
                stuInfo.stStartTime = NET_TIME.FromDateTime(startTime);
                stuInfo.stStopTime = NET_TIME.FromDateTime(endTime);
                stuInfo.hWnd = hwnd;
                stuInfo.cbDownLoadPos = m_PlayBackPosCallBack;
                stuInfo.dwPosUser = IntPtr.Zero;
                stuInfo.fDownLoadDataCallBack = m_DataCallBack;
                stuInfo.dwDataUser = IntPtr.Zero;
                stuInfo.nPlayDirection = 0;
                stuInfo.nWaittime = (int)WaitTime;
                m_PlayHandle = NETClient.PlayBackByTime(LoginID, channelID, stuInfo, ref stuOut);
                lock (m_PlayBackLock)
                {
                    m_PlayHandleList.Add(m_PlayHandle);
                }
                return true;
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                lock (m_PlayBackLock)
                {
                    if (m_PlayHandleList.Count == 0)
                    {
                        m_PlayBackAndDownloadManager.RemovePlayBackDataEvent(OnDataReceived);
                        m_PlayBackAndDownloadManager.RemovePlayBackDownLoadEvent(OnPlayBackPosReceived);
                    }
                }
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }

        }

        /// <summary>
        /// stop playback.
        /// </summary>
        /// <param name="PlayHandle">playback handle.</param>
        public void StopPlayBack(IntPtr PlayHandle)
        {
            bool bResult = false;
            if (PlayHandle == IntPtr.Zero)
            {
                return;
            }
            try
            {
                
                bResult = NETClient.PlayBackControl(PlayHandle, NetSDKCS.PlayBackType.Stop);
                lock (m_PlayBackLock)
                {
                    m_PlayHandleList.Remove(PlayHandle);
                    if (m_PlayHandleList.Count == 0)
                    {
                        m_PlayBackAndDownloadManager.RemovePlayBackDataEvent(OnDataReceived);
                        m_PlayBackAndDownloadManager.RemovePlayBackDownLoadEvent(OnPlayBackPosReceived);
                    }
                }
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }
        }

        /// <summary>
        /// stop all playback.
        /// </summary>
        public void StopPlayBackAll()
        {
            lock (m_PlayBackLock)
            {
                foreach (var item in m_PlayHandleList)
                {
                    if (item != IntPtr.Zero)
                    {
                        try
                        {
                            NETClient.PlayBackControl(item, NetSDKCS.PlayBackType.Stop);
                        }
                        catch
                        {
                            continue;
                        }
                    }
                }
                m_PlayHandleList.Clear();
            }
            m_PlayBackAndDownloadManager.RemovePlayBackDataEvent(OnDataReceived);
            m_PlayBackAndDownloadManager.RemovePlayBackDownLoadEvent(OnPlayBackPosReceived);
        }

        /// <summary>
        /// control playback.
        /// </summary>
        /// <param name="PlayHandle">playback handle</param>
        /// <param name="type">control type</param>
        public void PlayBackControl(IntPtr PlayHandle, PlayBackType type)
        {
            if (PlayHandle == IntPtr.Zero)
            {
                return;
            }
            try
            {
                NETClient.PlayBackControl(PlayHandle, (NetSDKCS.PlayBackType)type);
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }
        }

        /// <summary>
        /// start download file.
        /// </summary>
        /// <param name="startTime">start time</param>
        /// <param name="endTime">end time</param>
        /// <param name="channelID">channel id</param>
        /// <param name="path">save the file path</param>
        /// <returns>failed return false, successful return true</returns>
        public bool StartDownload(DateTime startTime, DateTime endTime, int channelID, string path)
        {
            if (startTime >= endTime)
            {
                return false;
            }
            lock (m_DownloadLock)
            {
                if (m_FileHandleList.Count == 0)
                {
                    m_PlayBackAndDownloadManager.AddDownLoadEvent(OnDownloadPosReceived);
                }
            }
            SetDeviceModel();
            try
            {
                m_FileHandle = NETClient.DownloadByTime(LoginID, channelID, EM_QUERY_RECORD_TYPE.ALL, startTime, endTime, path, m_DownloadCallBack, IntPtr.Zero, null, IntPtr.Zero, IntPtr.Zero);
                lock (m_DownloadLock)
                {
                    m_FileHandleList.Add(m_FileHandle);
                }
                return true;
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                lock (m_DownloadLock)
                {
                    if (m_FileHandleList.Count == 0)
                    {
                        m_PlayBackAndDownloadManager.RemoveDownLoadEvent(OnDownloadPosReceived);
                    }
                }
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }
        }

        /// <summary>
        /// stop download file.
        /// </summary>
        /// <param name="FileHandle">download handle</param>
        public void StopDownload(IntPtr FileHandle)
        {
            if (IntPtr.Zero == FileHandle)
            {
                return;
            }
            try
            {
                NETClient.StopDownload(FileHandle);
                lock (m_DownloadLock)
                {
                    m_FileHandleList.Remove(FileHandle);
                    if (m_FileHandleList.Count == 0)
                    {
                        m_PlayBackAndDownloadManager.RemoveDownLoadEvent(OnDownloadPosReceived);
                    }
                }
            }
            catch (NetSDKCS.NETClientExcetion ex)
            {
                throw new NETClientExcetion(ex.ErrorCode, ex.Message);
            }
        }

        /// <summary>
        /// stop all download.
        /// </summary>
        public void StopDownloadAll()
        {
            lock (m_DownloadLock)
            {
                foreach (var item in m_FileHandleList)
                {
                    if (item != IntPtr.Zero)
                    {
                        try
                        {
                            NETClient.StopDownload(item);
                        }
                        catch
                        {
                            continue;
                        }
                    }
                }
                m_FileHandleList.Clear();
            }
            m_PlayBackAndDownloadManager.RemoveDownLoadEvent(OnDownloadPosReceived);
        }

        /// <summary>
        /// override logout.
        /// </summary>
        public override void Logout()
        {
            StopPlayBackAll();
            StopDownloadAll();
            base.Logout();
        }

        /// <summary>
        /// override dispose.
        /// </summary>
        public override void Dispose()
        {
            StopPlayBackAll();
            StopDownloadAll();
            base.Dispose();
        }
    }

    /// <summary>
    /// manage playback and download callback.this is a singleton object.
    /// </summary>
    public sealed class PlayBackAndDownloadManager : DispatcherObject
    {
        /// <summary>
        /// download complete.
        /// </summary>
        private const int DOWNLOAD_END = -1;
        /// <summary>
        /// download failed.
        /// </summary>
        private const int DOWNLOAD_FAILED = -2;

        /// <summary>
        /// download progress callback.
        /// </summary>
        private readonly fTimeDownLoadPosCallBack m_DownloadCallBack;
        /// <summary>
        /// playback data callback.
        /// </summary>
        private readonly fDataCallBack m_DataCallBack;
        /// <summary>
        /// playback progress callback.
        /// </summary>
        private readonly fDownLoadPosCallBack m_PlayBackPosCallBack;

        /// <summary>
        /// download progress event.
        /// </summary>
        private event DownloadHandler DownloadPos;
        /// <summary>
        /// send download progress information to user.
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="args">download progress information</param>
        private void OnDownloadPosReceived(object sender, DownloadEventArgs args)
        {
            if (DownloadPos != null)
            {
                DownloadPos(this, args);
            }
        }
        /// <summary>
        /// playback progress event.
        /// </summary>
        private event DownloadHandler PlayBackPos;
        /// <summary>
        /// send playback progress information to user.
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="args">playback progress information</param>
        private void OnPlayBackPosReceived(object sender, DownloadEventArgs args)
        {
            if (PlayBackPos != null)
            {
                PlayBackPos(this, args);
            }
        }
        /// <summary>
        /// playback data event.
        /// </summary>
        private event PlayBackDataHandler PlayBackData;
        /// <summary>
        /// send playback data information to user.
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="args">playback data information</param>
        private void OnPlayBackDataReceived(object sender, PlayBackDataEventArgs args)
        {
            if(PlayBackData != null)
            {
                PlayBackData(this,args);
            }
        }

        /// <summary>
        /// private constructor for singleton.
        /// </summary>
        private PlayBackAndDownloadManager()
        {
            m_DownloadCallBack = new fTimeDownLoadPosCallBack(DownLoadCallBack);
            m_DataCallBack = new fDataCallBack(DataCallBack);
            m_PlayBackPosCallBack = new fDownLoadPosCallBack(PlayBackPosCallBack);
        }

        /// <summary>
        /// get manager instance.
        /// </summary>
        /// <returns>manager</returns>
        public static PlayBackAndDownloadManager GetInstance()
        {
            if (ClientManager.PlayBackAndDownloadInstance == null)
            {
                lock (ClientManager.PlayBackAndDownloadLock)
                {
                    if (ClientManager.PlayBackAndDownloadInstance == null)
                    {
                        ClientManager.PlayBackAndDownloadInstance = new PlayBackAndDownloadManager();
                    }
                }
            }
            return ClientManager.PlayBackAndDownloadInstance;
        }

        /// <summary>
        /// download progress callback function.
        /// </summary>
        /// <param name="lPlayHandle">download handle</param>
        /// <param name="dwTotalSize">total size</param>
        /// <param name="dwDownLoadSize">download size</param>
        /// <param name="index">index</param>
        /// <param name="recordfileinfo">record file information</param>
        /// <param name="dwUser">user data</param>
        private void DownLoadCallBack(IntPtr lPlayHandle, uint dwTotalSize, uint dwDownLoadSize, int index, NetSDKCS.NET_RECORDFILE_INFO recordfileinfo, IntPtr dwUser)
        {
            DownloadEventArgs arg = null;
            if ((int)dwDownLoadSize == DOWNLOAD_END)
            {
                arg = new DownloadEventArgs(lPlayHandle, dwTotalSize, dwTotalSize, false);
            }
            else if ((int)dwDownLoadSize == DOWNLOAD_FAILED)
            {
                arg = new DownloadEventArgs(lPlayHandle, dwTotalSize, dwDownLoadSize, true);
            }
            else
            {
                arg = new DownloadEventArgs(lPlayHandle, dwTotalSize, dwDownLoadSize, false);
            }
            Dispatcher.BeginInvoke((Action<object, DownloadEventArgs>)OnDownloadPosReceived, this, arg);
        }

        /// <summary>
        /// playback data callback function.
        /// </summary>
        /// <param name="lRealHandle">playback handle</param>
        /// <param name="dwDataType">data type</param>
        /// <param name="pBuffer">buffer pointer</param>
        /// <param name="dwBufSize">buffer size</param>
        /// <param name="dwUser">user data</param>
        /// <returns>0</returns>
        private int DataCallBack(IntPtr lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr dwUser)
        {
            byte[] data = new byte[dwBufSize];
            Marshal.Copy(pBuffer, data, 0, (int)dwBufSize);
            PlayBackDataEventArgs arg = new PlayBackDataEventArgs(lRealHandle, data);
            OnPlayBackDataReceived(this, arg);
            return 0;
        }
        /// <summary>
        /// palyback progress callback function.
        /// </summary>
        /// <param name="lPlayHandle">playback handle</param>
        /// <param name="dwTotalSize">total size</param>
        /// <param name="dwDownLoadSize">download size</param>
        /// <param name="dwUser">user data</param>
        private void PlayBackPosCallBack(IntPtr lPlayHandle, uint dwTotalSize, uint dwDownLoadSize, IntPtr dwUser)
        {
            DownloadEventArgs arg = null;
            if ((int)dwDownLoadSize == DOWNLOAD_END)
            {
                arg = new DownloadEventArgs(lPlayHandle, dwTotalSize, dwTotalSize, false);
            }
            else if ((int)dwDownLoadSize == DOWNLOAD_FAILED)
            {
                arg = new DownloadEventArgs(lPlayHandle, dwTotalSize, dwDownLoadSize, true);
            }
            else
            {
                arg = new DownloadEventArgs(lPlayHandle, dwTotalSize, dwDownLoadSize, false);
            }
            OnPlayBackPosReceived(this, arg);
        }

        /// <summary>
        /// get current download progress callback.
        /// </summary>
        /// <returns></returns>
        public fTimeDownLoadPosCallBack GetDownLoadCallBack()
        {
            return m_DownloadCallBack;
        }
        /// <summary>
        /// get current playback data callback.
        /// </summary>
        /// <returns></returns>
        public fDataCallBack GetDataCallBack()
        {
            return m_DataCallBack;
        }
        /// <summary>
        /// get current playback progress callback.
        /// </summary>
        /// <returns></returns>
        public fDownLoadPosCallBack GetPlayBackDownLoadPosCallBack()
        {
            return m_PlayBackPosCallBack;
        }
        /// <summary>
        /// subscribe download progress event.
        /// </summary>
        /// <param name="handler"></param>
        public void AddDownLoadEvent(DownloadHandler handler)
        {
            DownloadPos += handler;
        }
        /// <summary>
        /// unsubscribe download progress event.
        /// </summary>
        /// <param name="handler"></param>
        public void RemoveDownLoadEvent(DownloadHandler handler)
        {
            DownloadPos -= handler;
        }
        /// <summary>
        /// subscribe playback progress event.
        /// </summary>
        /// <param name="handler"></param>
        public void AddPlayBackDownLoadEvent(DownloadHandler handler)
        {
            PlayBackPos += handler;
        }
        /// <summary>
        /// unsubscribe playback progress event.
        /// </summary>
        /// <param name="handler"></param>
        public void RemovePlayBackDownLoadEvent(DownloadHandler handler)
        {
            PlayBackPos -= handler;
        }
        /// <summary>
        /// subscribe playback data event.
        /// </summary>
        /// <param name="handler"></param>
        public void AddPlayBackDataEvent(PlayBackDataHandler handler)
        {
            PlayBackData += handler;
        }
        /// <summary>
        /// unsubscribe playback data event.
        /// </summary>
        /// <param name="handler"></param>
        public void RemovePlayBackDataEvent(PlayBackDataHandler handler)
        {
            PlayBackData -= handler;
        }
    }

    /// <summary>
    /// stream type enum.
    /// </summary>
    public enum EM_STREAM_TYPE
    {
        ALL = 0,                                                            // both main and extra stream
        MAIN = 1,                                                           // main stream
        EXTRA = 2,                                                          // extra stream
    }
    /// <summary>
    /// record type enum.
    /// </summary>
    public enum EM_RECORD_TYPE
    {
        ALL,                                                                // All the video
        NORMAL,                                                             // common  video
        ALARM,                                                              // External alarm video
        MOTION,                                                             // DM alarm video
    }
    /// <summary>
    /// playback control type enum.
    /// </summary>
    public enum PlayBackType
    {
        Play,                                                               // play
        Pause,                                                              // pause
        Stop,                                                               // stop
        Fast,                                                               // fast
        Slow,                                                               // slow
        Normal,                                                             // normal
    }
    /// <summary>
    /// record file information strcut.
    /// </summary>
    public struct NET_RECORDFILE_INFO
    {
        public uint ch;						                    // Channel number
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 124)]
        public string filename;		    	                    // File name 
        public uint framenum;                                   // the total number of file frames
        public uint size;					                    // File length 
        public DateTime starttime;				                // Start time 
        public DateTime endtime;				                // End time 
        public uint driveno;				                    // HDD number, 0－127 is the local record. 128-network record
        public uint startcluster;			                    // Start cluster number 
        public byte nRecordFileType;		                    // Recorded file type  0:general record;1:alarm record ;2:motion detection;3:card number record ;4:image ;255:all
        public byte bImportantRecID;		                    // 0:general record 1:Important record
        public byte bHint;					                    // Document Indexing
        public byte bRecType;                                   // 0-main stream record 1-sub1 stream record 2-sub2 stream record 3-sub3 stream record
    }

}
