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

namespace NetClient
{
    public class TalkClient : BaseClient
    {
        private readonly fAudioDataCallBack m_AudioDataCallBack;
        private const int SampleRate = 8000;
        private const int AudioBit = 16;
        private const int PacketPeriod = 25;
        private const int Talk = 0;
        private const int SendAudio = 0;
        private const int ReviceAudio = 1;
        private IntPtr m_TalkHandle = IntPtr.Zero;

        public TalkClient()
        {
            m_AudioDataCallBack = new fAudioDataCallBack(AudioDataCallBack);
        }

        ~TalkClient()
        {
            Stop();
        }

        /// <summary>
        /// start talk
        /// </summary>
        /// <param name="mode">transfer mode</param>
        /// <param name="channel">device channel</param>
        public void Start(EM_TALK_TRANSFER_MODE mode, int channel)
        {
            if (LoginID == IntPtr.Zero || m_TalkHandle != IntPtr.Zero)
            {
                return;
            }
            IntPtr talkEncodePointer = IntPtr.Zero;
            IntPtr talkSpeakPointer = IntPtr.Zero;
            IntPtr talkTransferPointer = IntPtr.Zero;
            IntPtr channelPointer = IntPtr.Zero;

            NET_DEV_TALKDECODE_INFO talkCodeInfo = new NET_DEV_TALKDECODE_INFO();
            talkCodeInfo.encodeType = EM_TALK_CODING_TYPE.PCM;
            talkCodeInfo.dwSampleRate = SampleRate;
            talkCodeInfo.nAudioBit = AudioBit;
            talkCodeInfo.nPacketPeriod = PacketPeriod;
            talkCodeInfo.reserved = new byte[60];

            try
            {
                talkEncodePointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NET_DEV_TALKDECODE_INFO)));
                Marshal.StructureToPtr(talkCodeInfo, talkEncodePointer, true);
                // set talk encode type.
                NETClient.SetDeviceMode(LoginID, EM_USEDEV_MODE.TALK_ENCODE_TYPE, talkEncodePointer);

                NET_SPEAK_PARAM speak = new NET_SPEAK_PARAM();
                speak.dwSize = (uint)Marshal.SizeOf(typeof(NET_SPEAK_PARAM));
                speak.nMode = 0;
                speak.bEnableWait = false;
                speak.nSpeakerChannel = 0;
                talkSpeakPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NET_SPEAK_PARAM)));
                Marshal.StructureToPtr(speak, talkSpeakPointer, true);
                //set talk speak mode.
                NETClient.SetDeviceMode(LoginID, EM_USEDEV_MODE.TALK_SPEAK_PARAM, talkSpeakPointer);

                NET_TALK_TRANSFER_PARAM transfer = new NET_TALK_TRANSFER_PARAM();
                transfer.dwSize = (uint)Marshal.SizeOf(typeof(NET_TALK_TRANSFER_PARAM));
                if (mode == EM_TALK_TRANSFER_MODE.YES)
                {
                    transfer.bTransfer = true;
                    channelPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));
                    Marshal.WriteInt32(channelPointer, channel);
                    NETClient.SetDeviceMode(LoginID, EM_USEDEV_MODE.TALK_TALK_CHANNEL, channelPointer);
                }
                else
                {
                    transfer.bTransfer = false;
                }
                talkTransferPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NET_TALK_TRANSFER_PARAM)));
                Marshal.StructureToPtr(transfer, talkTransferPointer, true);
                //set talk transfer mode.
                NETClient.SetDeviceMode(LoginID, EM_USEDEV_MODE.TALK_TRANSFER_MODE, talkTransferPointer);

                try
                {
                    //call netsdk starttalk function.
                    m_TalkHandle = NETClient.StartTalk(LoginID, m_AudioDataCallBack, IntPtr.Zero);
                }
                catch (NetSDKCS.NETClientExcetion ex)
                {
                    throw new NETClientExcetion(ex.ErrorCode, ex.Message);
                }
                try
                {
                    //call netsdk recordstart function.
                    NETClient.RecordStart(LoginID);
                }
                catch (NetSDKCS.NETClientExcetion ex)
                {
                    NETClient.StopTalk(m_TalkHandle);
                    m_TalkHandle = IntPtr.Zero;
                    throw new NETClientExcetion(ex.ErrorCode, ex.Message);
                }
            }
            finally
            {
                //free pointer.
                Marshal.FreeHGlobal(talkEncodePointer);
                Marshal.FreeHGlobal(talkSpeakPointer);
                Marshal.FreeHGlobal(talkTransferPointer);
                Marshal.FreeHGlobal(channelPointer);
            }
        }

        //audio data callback
        private void AudioDataCallBack(IntPtr lTalkHandle, IntPtr pDataBuf, uint dwBufSize, byte byAudioFlag, IntPtr dwUser)
        {
            if (lTalkHandle == m_TalkHandle)
            {
                if (SendAudio == byAudioFlag)
                {
                    //call netsdk senddata.
                    try
                    {
                        NETClient.TalkSendData(lTalkHandle, pDataBuf, dwBufSize);
                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                else if (ReviceAudio == byAudioFlag)
                {
                    //call netsdk decode audio.
                    try
                    {
                        NETClient.AudioDec(pDataBuf, dwBufSize);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

        /// <summary>
        /// stop talk
        /// </summary>
        public void Stop()
        {
            if (m_TalkHandle == IntPtr.Zero || LoginID == IntPtr.Zero)
            {
                return;
            }
            try
            {
                NETClient.RecordStop(LoginID);
                NETClient.StopTalk(m_TalkHandle);
            }
            catch
            {
            }
            finally
            {
                m_TalkHandle = IntPtr.Zero;
            }

        }

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

        public override void Dispose()
        {
            Logout();
        }
    }

    //enum transfer mode.
    public enum EM_TALK_TRANSFER_MODE
    {
        NO = 0,
        YES,
    }
}
