using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Helper;
using System.Collections.Concurrent;
using NetSDKCS;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace AlarmDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //manange waiting for connect device list.
        private List<Device> m_WaitDeviceList = new List<Device>();
        //manange connected device list.
        private List<Device> m_ConnectedDeviceList = new List<Device>();
        //manange connect failed device list.
        private List<Device> m_FailedDeviceList = new List<Device>();
        //manange failed to wait device list.
        private List<Device> m_FailedToWaitList = new List<Device>();
        //manange alarm information list.
        private List<AlarmInfo> m_ManangeAlarmInfo = new List<AlarmInfo>();
        //manange connected device loginhandle,realloadpicturehandle,ip,channel,etc.
        public ConcurrentDictionary<IntPtr, Device> m_DeviceDic = new ConcurrentDictionary<IntPtr, Device>();

        //event count.
        private int alarmCount = 1;
        //error count.
        private int errorCount = 1;

        //check for is cancel.
        private bool m_IsCancel = false;

        //cancellation token source.
        private CancellationTokenSource m_CancellationTokenSource;

        //task factory.
        private TaskFactory m_Factory;

        //reconnect callback
        private fHaveReConnectCallBack m_ReConnectCallBack;
        //alarm callback
        private fMessCallBackEx m_AlarmCallBack;

        //login and subscibe device arlarm task.
        private Task m_StartTask;
        //analysis device event thread.
        private Thread m_ProcessAlarmThread;
        //stop device connected task
        private Task m_StopTask;

        //timer for get queue length.
        private DispatcherTimer dispatcherTimer = new DispatcherTimer();
        //event queue.thread is safe.
        private ConcurrentQueue<AlarmMsg> m_AlarmMsgQueue = new ConcurrentQueue<AlarmMsg>();

        private enum EM_ALARM_STATUS
        {
            STOP = 0,
            START,
        }

        //error list for update error ui.
        private ObservableCollection<ErrorInfo> ErrorList
        {
            get;
            set;
        }
        //event message information list for update device event ui.
        private ObservableCollection<AlarmInfo> AlarmInfoList
        {
            get;
            set;
        }
        //performance list for update performance ui.
        private ObservableCollection<PerformanceInfo> PerformanceList
        {
            get;
            set;
        }

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            towait_btn.IsEnabled = false;
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            connected_header.Header = string.Format((string)App.Current.Resources["ID_CONNECTED"], connected_listbox.Items.Count);
            failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);
            m_AlarmCallBack=new fMessCallBackEx(AlarmCallBackEx);
            m_ReConnectCallBack = new fHaveReConnectCallBack(ReConnectCallBack);
            NETClient.SetDVRMessCallBack(m_AlarmCallBack, IntPtr.Zero);
            NETClient.SetAutoReconnect(m_ReConnectCallBack, IntPtr.Zero); //set reconnect callback.
            ErrorList = new ObservableCollection<ErrorInfo>();
            AlarmInfoList = new ObservableCollection<AlarmInfo>();
            PerformanceList = new ObservableCollection<PerformanceInfo>();
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
            LogHelper.WriteWithMethod("Leave");
        }
        private bool AlarmCallBackEx(int lCommand, IntPtr lLoginID, IntPtr pBuf, uint dwBufLen, IntPtr pchDVRIP, int nDVRPort, bool bAlarmAckFlag, int nEventID, IntPtr dwUser)
        {
            LogHelper.WriteWithMethod("Enter");
            AlarmMsg alarmMsg = new AlarmMsg();
            alarmMsg.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            alarmMsg.LoginID = lLoginID;
            alarmMsg.AlarmType = (EM_ALARM_TYPE)lCommand;
            alarmMsg.IP = Marshal.PtrToStringAnsi(pchDVRIP);
            alarmMsg.AlarmInfo = GetObject(lCommand, pBuf, (int)dwBufLen);
            alarmMsg.Length = (int)dwBufLen;
            m_AlarmMsgQueue.Enqueue(alarmMsg);
            LogHelper.WriteWithMethod("Leave");
            return true;
        }
        private object GetObject(int type, IntPtr buffer, int len)
        {
            LogHelper.WriteWithMethod("Enter");
            Object obj = null;
            switch ((EM_ALARM_TYPE)type)
            {
                case EM_ALARM_TYPE.ALARM_ALARM_EX:
                case EM_ALARM_TYPE.MOTION_ALARM_EX:
                case EM_ALARM_TYPE.VIDEOLOST_ALARM_EX:
                case EM_ALARM_TYPE.SHELTER_ALARM_EX:
                case EM_ALARM_TYPE.DISKFULL_ALARM_EX:
                case EM_ALARM_TYPE.DISKERROR_ALARM_EX:
                    byte[] data=new byte[len];
                    Marshal.Copy(buffer, data, 0, len);
                    obj = data;
                    break;
                case EM_ALARM_TYPE.ALARM_AUDIO_ANOMALY:
                    {
                        NET_ALARM_AUDIO_ANOMALY info = new NET_ALARM_AUDIO_ANOMALY();
                        info = (NET_ALARM_AUDIO_ANOMALY)Marshal.PtrToStructure(buffer, typeof(NET_ALARM_AUDIO_ANOMALY));
                        obj = info;
                        break;
                    }
                case EM_ALARM_TYPE.ALARM_AUDIO_MUTATION:
                    {
                        NET_ALARM_AUDIO_MUTATION info = new NET_ALARM_AUDIO_MUTATION();
                        info = (NET_ALARM_AUDIO_MUTATION)Marshal.PtrToStructure(buffer, typeof(NET_ALARM_AUDIO_MUTATION));
                        obj = info;
                        break;
                    }
                default:
                    break;
            }
            LogHelper.WriteWithMethod("Leave");
            return obj;
        }
        private void ReConnectCallBack(IntPtr lLoginID, IntPtr pchDVRIP, int nDVRPort, IntPtr dwUser)
        {
            LogHelper.WriteWithMethod("Enter");
            string ip = Marshal.PtrToStringAnsi(pchDVRIP);
            Dispatcher.BeginInvoke((Action<string>)UpdateReConnectUI, ip);
            try
            {
                NETClient.StartListen(lLoginID);
            }
            catch (Exception ex)
            {
                if (ex is NETClientExcetion)
                {
                    Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "StartListen", ip);
                }
                else
                {
                    LogHelper.WriteException(ex);
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //update device reconnect status.
        private void UpdateReConnectUI(string ip)
        {
            LogHelper.WriteWithMethod("Enter");
            foreach (Device item in connected_listbox.Items)
            {
                if (item.IP == ip)
                {
                    item.DeviceStatus = Status.OnLine;
                    connected_listbox.Items.Refresh();
                    break;
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //timer event for show current queue length.
        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            SavePerformance();
            DateTime nowTime = DateTime.Now;
            PerformanceInfo performance = new PerformanceInfo();
            performance.Time = nowTime.ToString("yyyy-MM-dd HH:mm:ss");
            performance.Count = m_AlarmMsgQueue.Count.ToString();
            PerformanceList.Add(performance);
            performance_listView.ItemsSource = null;
            performance_listView.ItemsSource = PerformanceList;
        }

        private void SavePerformance()
        {
            if (PerformanceList.Count > 2000)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "SavePerformance";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                path = path + "\\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt";
                FileStream fileStream = new FileStream(path, FileMode.Create);
                using (StreamWriter sw = new StreamWriter(fileStream))
                {
                    foreach (PerformanceInfo item in PerformanceList)
                    {
                        sw.WriteLine(string.Format("Time:{0},Count:{1}", item.Time, item.Count));
                    }
                    sw.Flush();
                    sw.Close();
                }
                PerformanceList.Clear();
                performance_listView.ItemsSource = null;
                performance_listView.Items.Refresh();
            }
        }
        private void Port_Input(object sender, TextCompositionEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            try
            {
                Convert.ToUInt32(e.Text);
                e.Handled = false;
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
                e.Handled = true;
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //check ip,port,username
        private bool CheckDeviceInfo()
        {
            LogHelper.WriteWithMethod("Enter");
            if (ip_tb.Text.Trim() == "" || ip_tb.Text == null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_IP_ERROR"]);
                return false;
            }
            if (port_tb.Text.Trim() == "" || port_tb.Text == null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_PORT_ERROR"]);
                return false;
            }
            else
            {
                try
                {
                    Convert.ToUInt16(port_tb.Text.Trim());
                }
                catch (Exception ex)
                {
                    LogHelper.WriteException(ex);
                    MessageBox.Show(this, (string)App.Current.Resources["ID_PORT_ERROR1"]);
                    return false;
                }
            }
            if (username_tb.Text.Trim() == "" || username_tb.Text == null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_USERNAME_ERROR"]);
                return false;
            }
            LogHelper.WriteWithMethod("Leave");
            return true;
        }

        private void AddDevice_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            bool ret = CheckDeviceInfo();
            if (!ret)
            {
                return;
            }
            Device item = m_WaitDeviceList.Find(p => p.IP == ip_tb.Text.Trim());
            if (item != null)
            {
                MessageBox.Show(this, (string)App.Current.Resources["ID_IP_ERROR1"]);
                return;
            }
            ushort port = 0;
            try
            {
                port = Convert.ToUInt16(port_tb.Text.Trim());
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
                MessageBox.Show(this, (string)App.Current.Resources["ID_PORT_ERROR1"]);
                return;
            }
            Device dev = new Device();
            dev.IP = ip_tb.Text.Trim();
            dev.Port = Convert.ToUInt16(port_tb.Text.Trim());
            dev.UserName = username_tb.Text.Trim();
            dev.Password = password_tb.Password;
            dev.DeviceStatus = Status.UnKnow;
            m_WaitDeviceList.Add(dev);
            wait_listbox.Items.Add(dev);
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        private void DelDevice_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            if (wait_listbox.SelectedItem == null)
            {
                LogHelper.WriteWithMethod("Leave null");
                return;
            }
            m_WaitDeviceList.Remove((Device)wait_listbox.SelectedItem);
            wait_listbox.Items.Remove(wait_listbox.SelectedItem);
            wait_listbox.SelectedItem = null;
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        private void Start_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            m_IsCancel = false;
            if (m_WaitDeviceList.Count == 0)
            {
                LogHelper.WriteWithMethod("Leave");
                return;
            }

            add_device_bt.IsEnabled = false;
            del_device_bt.IsEnabled = false;
            start_bt.IsEnabled = false;
            import_bt.IsEnabled = false;
            export_bt.IsEnabled = false;

            
            m_CancellationTokenSource = new CancellationTokenSource();
            m_Factory = new TaskFactory(m_CancellationTokenSource.Token);
            m_StartTask = m_Factory.StartNew(new Action<object>(f =>
                {
                    LogHelper.WriteWithMethod("Enter");
                    CancellationToken ct = (f as TaskFactory).CancellationToken;
                    foreach (Device item in m_WaitDeviceList)
                    {
                        try
                        {
                            if (ct.IsCancellationRequested)
                            {
                                m_IsCancel = true;
                                LogHelper.WriteWithMethod("Stop");
                                break;
                            }
                            NET_DEVICEINFO_Ex deviceInfo = new NET_DEVICEINFO_Ex();
                            IntPtr loginID = NETClient.Login(item.IP, item.Port, item.UserName, item.Password, EM_LOGIN_SPAC_CAP_TYPE.TCP, IntPtr.Zero, ref deviceInfo);
                            item.LoginID = loginID;
                            item.DeviceStatus = Status.OnLine;
                            item.ChannelCount = deviceInfo.nChanNum;
                            Device item1 = m_ConnectedDeviceList.Find(p => p.IP == item.IP);
                            if (item1 == null)
                            {
                                m_ConnectedDeviceList.Add(item);
                            }
                            bool ret = m_DeviceDic.TryAdd(loginID, item);
                            if (!ret)
                            {
                                LogHelper.WriteWithMethod(loginID.ToString());
                            }
                            if (ct.IsCancellationRequested)
                            {
                                m_IsCancel = true;
                                LogHelper.WriteWithMethod("Stop");
                                break;
                            }
                            Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, item);
                            Dispatcher.BeginInvoke((Action<Device>)UpdateConnectedUI, item);
                            if (ct.IsCancellationRequested)
                            {
                                m_IsCancel = true;
                                LogHelper.WriteWithMethod("Stop");
                                break;
                            }
                           
                            try
                            {
                                if (ct.IsCancellationRequested)
                                {
                                    m_IsCancel = true;
                                    LogHelper.WriteWithMethod("Stop");
                                    break;
                                }
                                NETClient.StartListen(loginID);
                            }
                            catch (Exception ex)
                            {
                                if (ex is NETClientExcetion)
                                {
                                    Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "StartListen", item.IP);
                                }
                                else
                                {
                                    LogHelper.WriteException(ex);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            if (ex is NETClientExcetion)
                            {
                                Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, item);
                                Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "Login", item.IP);
                                Dispatcher.BeginInvoke((Action<Device>)UpdateConnectFailedUI, item);
                            }
                            else
                            {
                                LogHelper.WriteException(ex);
                            }
                        }
                    }
                    if (m_IsCancel == false)
                    {
                        Dispatcher.BeginInvoke((Action)UpdateBtn);
                    }
                    LogHelper.WriteWithMethod("Leave");
                }), m_Factory, m_CancellationTokenSource.Token);

            ThreadStart processTS = new ThreadStart(ProcessAlarm);
            m_ProcessAlarmThread = new Thread(processTS);
            m_ProcessAlarmThread.IsBackground = true;
            m_ProcessAlarmThread.Start();

            PerformanceList.Clear();
            dispatcherTimer.Start();
            LogHelper.WriteWithMethod("Leave");
        }

        //update wait ui.
        private void UpdateWaitUI(Device dev)
        {
            LogHelper.WriteWithMethod("Enter");
            wait_listbox.Items.Remove(dev);
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        //update connected ui.
        private void UpdateConnectedUI(Device dev)
        {
            LogHelper.WriteWithMethod("Enter");
            connected_listbox.Items.Add(dev);
            connected_header.Header = string.Format((string)App.Current.Resources["ID_CONNECTED"], connected_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        //update failed ui.
        private void UpdateConnectFailedUI(Device dev)
        {
            LogHelper.WriteWithMethod("Enter");
            Device item = m_FailedDeviceList.Find(p => p.IP == dev.IP);
            if (item == null)
            {
                m_FailedDeviceList.Add(dev);
                faild_listbox.Items.Add(dev);
                failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);
            }
            LogHelper.WriteWithMethod("Leave");
        }

        //update error ui.
        public void UpdateError(NETClientExcetion ex, string type, string ip)
        {
            LogHelper.WriteWithMethod("Enter");
            ErrorInfo errorInfo = new ErrorInfo();
            errorInfo.ID = errorCount++.ToString();
            errorInfo.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            errorInfo.IP = ip;
            errorInfo.ErrorType = type;
            errorInfo.ErrorCode = ex.ErrorCode.ToString("X");
            errorInfo.ErrorMessage = ex.Message;
            ErrorList.Add(errorInfo);
            error_listView.ItemsSource = null;
            error_listView.ItemsSource = ErrorList;
            LogHelper.WriteWithMethod("Leave");
        }

        private void ProcessAlarm()
        {
            while (true)
            {
                Thread.Sleep(20);
                if (m_AlarmMsgQueue.Count <= 0)
                {
                    continue;
                }

                AlarmMsg msg=null;
                m_AlarmMsgQueue.TryDequeue(out msg);
                if (null == msg)
                {
                    continue;
                }

                LogHelper.WriteWithMethod("Enter");
                switch (msg.AlarmType)
                {
                    case EM_ALARM_TYPE.ALARM_ALARM_EX:
                    case EM_ALARM_TYPE.MOTION_ALARM_EX:
                    case EM_ALARM_TYPE.VIDEOLOST_ALARM_EX:
                    case EM_ALARM_TYPE.SHELTER_ALARM_EX:
                    case EM_ALARM_TYPE.DISKFULL_ALARM_EX:
                    case EM_ALARM_TYPE.DISKERROR_ALARM_EX:
                        {
                            byte[] data = (byte[])msg.AlarmInfo;
                            for (int i = 0; i < data.Length; i++)
                            {
                                if (data[i] == (int)EM_ALARM_STATUS.START)
                                {
                                    AlarmInfo alarmInfo = new AlarmInfo();
                                    alarmInfo.LoginID = msg.LoginID;
                                    alarmInfo.ID = alarmCount++.ToString();
                                    alarmInfo.IP = msg.IP;
                                    alarmInfo.Time = msg.Time;
                                    switch (msg.AlarmType)
                                    {
                                        case EM_ALARM_TYPE.ALARM_ALARM_EX:
                                            alarmInfo.Type = (string)App.Current.Resources["ID_ALARM_ALARM_EX"];
                                            alarmInfo.AlarmType = EM_ALARM_TYPE.ALARM_ALARM_EX;
                                            break;
                                        case EM_ALARM_TYPE.MOTION_ALARM_EX:
                                            alarmInfo.Type = (string)App.Current.Resources["ID_MOTION_ALARM_EX"];
                                            alarmInfo.AlarmType = EM_ALARM_TYPE.MOTION_ALARM_EX;
                                            break;
                                        case EM_ALARM_TYPE.VIDEOLOST_ALARM_EX:
                                            alarmInfo.Type = (string)App.Current.Resources["ID_VIDEOLOST_ALARM_EX"];
                                            alarmInfo.AlarmType = EM_ALARM_TYPE.VIDEOLOST_ALARM_EX;
                                            break;
                                        case EM_ALARM_TYPE.SHELTER_ALARM_EX:
                                            alarmInfo.Type = (string)App.Current.Resources["ID_SHELTER_ALARM_EX"];
                                            alarmInfo.AlarmType = EM_ALARM_TYPE.SHELTER_ALARM_EX;
                                            break;
                                        case EM_ALARM_TYPE.DISKFULL_ALARM_EX:
                                            alarmInfo.Type = (string)App.Current.Resources["ID_DISKFULL_ALARM_EX"];
                                            alarmInfo.AlarmType = EM_ALARM_TYPE.DISKFULL_ALARM_EX;
                                            break;
                                        case EM_ALARM_TYPE.DISKERROR_ALARM_EX:
                                            alarmInfo.Type = (string)App.Current.Resources["ID_DISKERROR_ALARM_EX"];
                                            alarmInfo.AlarmType = EM_ALARM_TYPE.DISKERROR_ALARM_EX;
                                            break;
                                    }
                                    alarmInfo.Channel = i.ToString();
                                    var item = m_ManangeAlarmInfo.Find(p => (p.LoginID == msg.LoginID) && (p.Channel == i.ToString()) && (p.AlarmType == msg.AlarmType));
                                    if (item == null)
                                    {
                                        m_ManangeAlarmInfo.Add(alarmInfo);
                                        Dispatcher.BeginInvoke((Action)SaveInfo);
                                        Dispatcher.BeginInvoke((Action<AlarmInfo>)UpdateMessageUI, alarmInfo);
                                    }
                                }
                                else
                                {
                                    var item = m_ManangeAlarmInfo.Find(p => (p.LoginID == msg.LoginID) && (p.Channel == i.ToString()) && (p.AlarmType == msg.AlarmType));
                                    if (item != null)
                                    {
                                        AlarmInfo alarmInfo = new AlarmInfo();
                                        alarmInfo.LoginID = msg.LoginID;
                                        alarmInfo.ID = alarmCount++.ToString();
                                        alarmInfo.IP = msg.IP;
                                        alarmInfo.Time = msg.Time;
                                        switch (msg.AlarmType)
                                        {
                                            case EM_ALARM_TYPE.ALARM_ALARM_EX:
                                                alarmInfo.Type = (string)App.Current.Resources["ID_ALARM_ALARM_EX_END"];
                                                break;
                                            case EM_ALARM_TYPE.MOTION_ALARM_EX:
                                                alarmInfo.Type = (string)App.Current.Resources["ID_MOTION_ALARM_EX_END"];
                                                break;
                                            case EM_ALARM_TYPE.VIDEOLOST_ALARM_EX:
                                                alarmInfo.Type = (string)App.Current.Resources["ID_VIDEOLOST_ALARM_EX_END"];
                                                break;
                                            case EM_ALARM_TYPE.SHELTER_ALARM_EX:
                                                alarmInfo.Type = (string)App.Current.Resources["ID_SHELTER_ALARM_EX_END"];
                                                break;
                                            case EM_ALARM_TYPE.DISKFULL_ALARM_EX:
                                                alarmInfo.Type = (string)App.Current.Resources["ID_DISKFULL_ALARM_EX_END"];
                                                break;
                                            case EM_ALARM_TYPE.DISKERROR_ALARM_EX:
                                                alarmInfo.Type = (string)App.Current.Resources["ID_DISKERROR_ALARM_EX_END"];
                                                break;
                                        }
                                        alarmInfo.Channel = i.ToString();
                                        m_ManangeAlarmInfo.Remove(item);
                                        Dispatcher.BeginInvoke((Action)SaveInfo);
                                        Dispatcher.BeginInvoke((Action<AlarmInfo>)UpdateMessageUI, alarmInfo);
                                    }
                                }
                            }
                            break;
                        }
                    case EM_ALARM_TYPE.ALARM_AUDIO_ANOMALY:
                        {
                            Dispatcher.BeginInvoke((Action)SaveInfo);
                            NET_ALARM_AUDIO_ANOMALY info = (NET_ALARM_AUDIO_ANOMALY)msg.AlarmInfo;
                            if (info.dwAction == (uint)EM_ALARM_STATUS.STOP)
                            {
                                AlarmInfo alarmInfo = new AlarmInfo();
                                alarmInfo.LoginID = msg.LoginID;
                                alarmInfo.ID = alarmCount++.ToString();
                                alarmInfo.IP = msg.IP;
                                alarmInfo.Time = msg.Time;
                                alarmInfo.Type = (string)App.Current.Resources["ID_ALARM_AUDIO_ANOMALY"];
                                alarmInfo.Channel = info.dwChannelID.ToString();
                                Dispatcher.BeginInvoke((Action<AlarmInfo>)UpdateMessageUI, alarmInfo);
                            }
                            else if (info.dwAction == (uint)EM_ALARM_STATUS.START)
                            {
                                AlarmInfo alarmInfo = new AlarmInfo();
                                alarmInfo.LoginID = msg.LoginID;
                                alarmInfo.ID = alarmCount++.ToString();
                                alarmInfo.IP = msg.IP;
                                alarmInfo.Time = msg.Time;
                                alarmInfo.Type = (string)App.Current.Resources["ID_ALARM_AUDIO_ANOMALY_END"];
                                alarmInfo.Channel = info.dwChannelID.ToString();
                                Dispatcher.BeginInvoke((Action<AlarmInfo>)UpdateMessageUI, alarmInfo);
                            }
                            break;
                        }
                    case EM_ALARM_TYPE.ALARM_AUDIO_MUTATION:
                        {
                            Dispatcher.BeginInvoke((Action)SaveInfo);
                            NET_ALARM_AUDIO_MUTATION info = (NET_ALARM_AUDIO_MUTATION)msg.AlarmInfo;
                            if (info.dwAction == (uint)EM_ALARM_STATUS.STOP)
                            {
                                AlarmInfo alarmInfo = new AlarmInfo();
                                alarmInfo.LoginID = msg.LoginID;
                                alarmInfo.ID = alarmCount++.ToString();
                                alarmInfo.IP = msg.IP;
                                alarmInfo.Time = msg.Time;
                                alarmInfo.Type = (string)App.Current.Resources["ID_ALARM_AUDIO_MUTATION"];
                                alarmInfo.Channel = info.dwChannelID.ToString();
                                Dispatcher.BeginInvoke((Action<AlarmInfo>)UpdateMessageUI, alarmInfo);
                            }
                            else if (info.dwAction == (uint)EM_ALARM_STATUS.START)
                            {
                                AlarmInfo alarmInfo = new AlarmInfo();
                                alarmInfo.LoginID = msg.LoginID;
                                alarmInfo.ID = alarmCount++.ToString();
                                alarmInfo.IP = msg.IP;
                                alarmInfo.Time = msg.Time;
                                alarmInfo.Type = (string)App.Current.Resources["ID_ALARM_AUDIO_MUTATION_END"];
                                alarmInfo.Channel = info.dwChannelID.ToString();
                                Dispatcher.BeginInvoke((Action<AlarmInfo>)UpdateMessageUI, alarmInfo);
                            }
                            break;
                        }
                    default:
                        LogHelper.WriteWithMethod(Enum.GetName(typeof(EM_ALARM_TYPE), msg.AlarmType));
                        break;
                }
                LogHelper.WriteWithMethod("Leave");
            }
        }

        private void SaveInfo()
        {
            if (AlarmInfoList.Count > 2000)
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + "SaveInfo";
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                path = path + "\\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt";
                FileStream fileStream = new FileStream(path, FileMode.Create);
                using (StreamWriter sw=new StreamWriter(fileStream))
                {
                    foreach (AlarmInfo item in AlarmInfoList)
                    {
                        sw.WriteLine(string.Format("ID:{0},Time:{1},IP:{2},Channel:{3},AlarmType:{4}",item.ID,item.Time,item.IP,item.Channel,item.Type));
                    }
                    sw.Flush();
                    sw.Close();
                }
                AlarmInfoList.Clear();
                event_listview.ItemsSource = null;
                event_listview.Items.Refresh();
            }
        }

        //update event message ui.
        private void UpdateMessageUI(AlarmInfo info)
        {
            LogHelper.WriteWithMethod("Enter");
            AlarmInfoList.Add(info);
            event_listview.ItemsSource = null;
            event_listview.ItemsSource = AlarmInfoList;
            LogHelper.WriteWithMethod("Leave");
        }

        private void Stop_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            stop_bt.IsEnabled = false;
            dispatcherTimer.Stop();

            m_StopTask = new Task(() =>
                {
                    m_ProcessAlarmThread.Abort();
                    if (m_StartTask.Status == TaskStatus.Running)
                    {
                        m_CancellationTokenSource.Cancel();

                        while (true)
                        {
                            if (m_IsCancel == true)
                            {
                                break;
                            }
                            if (m_StartTask.Status == TaskStatus.RanToCompletion)
                            {
                                break;
                            }
                            Thread.Sleep(100);
                        }
                        m_IsCancel = false;
                    }
                   
                    Stop();
                    LogHelper.WriteWithMethod("Leave");
                });
            m_StopTask.Start();
        }

        //update failed and button ui.
        private void UpdateUI()
        {
            LogHelper.WriteWithMethod("Enter");
            m_ConnectedDeviceList.Clear();
            connected_listbox.Items.Clear();
            connected_header.Header = string.Format((string)App.Current.Resources["ID_CONNECTED"], connected_listbox.Items.Count);
            m_FailedDeviceList.Clear();
            faild_listbox.Items.Clear();
            failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);

            add_device_bt.IsEnabled = true;
            del_device_bt.IsEnabled = true;
            start_bt.IsEnabled = true;
            stop_bt.IsEnabled = true;
            import_bt.IsEnabled = true;
            export_bt.IsEnabled = true;
            towait_btn.IsEnabled = false;

            wait_listbox.Items.Clear();
            foreach (Device item in m_WaitDeviceList)
            {
                item.DeviceStatus = Status.UnKnow;
                wait_listbox.Items.Add(item);
            }
            wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            LogHelper.WriteWithMethod("Leave");
        }

        private void Stop()
        {
            LogHelper.WriteWithMethod("Enter");
            foreach (var item in m_DeviceDic)
            {
                IntPtr loginID = item.Key;
                Device dev = item.Value;
                try
                {
                    NETClient.StopListen(loginID);
                }
                catch (Exception ex)
                {
                    if (ex is NETClientExcetion)
                    {
                        Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "StopListen", dev.IP);
                    }
                    else
                    {
                        LogHelper.WriteException(ex);
                    }
                }
                try
                {
                    NETClient.Logout(dev.LoginID);
                }
                catch (Exception ex)
                {
                    if (ex is NETClientExcetion)
                    {
                        Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "Logout", dev.IP);
                    }
                    else
                    {
                        LogHelper.WriteException(ex);
                    }
                }
            }
            m_DeviceDic.Clear();
            Dispatcher.BeginInvoke((Action)UpdateUI);
            LogHelper.WriteWithMethod("Leave");
        }

        // decode import device list.
        private void DecodeString(string str)
        {
            LogHelper.WriteWithMethod("Enter");
            if (str == null)
            {
                LogHelper.WriteWithMethod("Leave");
                return;
            }
            try
            {
                Device dev = new Device();
                dev.IP = str.Substring(3, str.IndexOf(",port=") - 3);
                var item = m_WaitDeviceList.Find(p => p.IP == dev.IP);
                if (item != null)
                {
                    LogHelper.WriteWithMethod("Leave");
                    return;
                }
                string port = str.Substring(str.IndexOf(",port=") + 6, str.IndexOf(",username=") - str.IndexOf(",port=") - 6);
                dev.Port = Convert.ToUInt16(port);
                dev.UserName = str.Substring(str.IndexOf(",username=") + 10, str.IndexOf(",password=") - str.IndexOf(",username=") - 10);
                dev.Password = str.Substring(str.IndexOf(",password=") + 10);
                dev.DeviceStatus = Status.UnKnow;
                m_WaitDeviceList.Add(dev);
                wait_listbox.Items.Add(dev);
                wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
            }
            LogHelper.WriteWithMethod("Leave");
        }

        private void Import_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
            var res = openFileDialog.ShowDialog();
            if (res == System.Windows.Forms.DialogResult.OK)
            {
                m_WaitDeviceList.Clear();
                wait_listbox.Items.Clear();
                wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
                using (StreamReader sr = new StreamReader(openFileDialog.FileName, Encoding.Unicode))
                {
                    string str = sr.ReadLine();
                    DecodeString(str);
                    while (str != null)
                    {
                        str = sr.ReadLine();
                        DecodeString(str);
                    }
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        private void Export_Clicked(object sender, RoutedEventArgs e)
        {
            LogHelper.WriteWithMethod("Enter");
            System.Windows.Forms.SaveFileDialog saveFileDialog = new System.Windows.Forms.SaveFileDialog();
            saveFileDialog.FileName = "IP";
            saveFileDialog.Filter = "|*.txt";
            var res = saveFileDialog.ShowDialog();
            if (res == System.Windows.Forms.DialogResult.OK)
            {
                using (StreamWriter sw = new StreamWriter(saveFileDialog.FileName, false, Encoding.Unicode))
                {
                    foreach (Device item in m_WaitDeviceList)
                    {
                        string lineString = string.Format("ip={0},port={1},username={2},password={3}", item.IP, item.Port, item.UserName, item.Password);
                        sw.WriteLine(lineString);
                    }
                    sw.Flush();
                    sw.Dispose();
                }
            }
            LogHelper.WriteWithMethod("Leave");
        }

        private void ToWait_Click(object sender, RoutedEventArgs e)
        {
            m_FailedToWaitList.Clear();
            foreach (var item in m_FailedDeviceList)
            {
                m_FailedToWaitList.Add(item);
                wait_listbox.Items.Add(item);
                wait_header.Header = string.Format((string)App.Current.Resources["ID_WAITING"], wait_listbox.Items.Count);
            }
            m_FailedDeviceList.Clear();
            faild_listbox.Items.Clear();
            failed_header.Header = string.Format((string)App.Current.Resources["ID_FAILED"], faild_listbox.Items.Count);
            towait_btn.IsEnabled = false;

            m_CancellationTokenSource = new CancellationTokenSource();
            m_Factory = new TaskFactory(m_CancellationTokenSource.Token);
            m_StartTask = m_Factory.StartNew(new Action<object>(f =>
            {
                LogHelper.WriteWithMethod("Enter");
                CancellationToken ct = (f as TaskFactory).CancellationToken;
                foreach (Device dev in m_FailedToWaitList)
                {
                    try
                    {
                        if (ct.IsCancellationRequested)
                        {
                            m_IsCancel = true;
                            LogHelper.WriteWithMethod("Stop");
                            break;
                        }
                        NET_DEVICEINFO_Ex deviceInfo = new NET_DEVICEINFO_Ex();
                        IntPtr loginID = NETClient.Login(dev.IP, dev.Port, dev.UserName, dev.Password, EM_LOGIN_SPAC_CAP_TYPE.TCP, IntPtr.Zero, ref deviceInfo);
                        dev.LoginID = loginID;
                        dev.DeviceStatus = Status.OnLine;
                        dev.ChannelCount = deviceInfo.nChanNum;
                        Device item = m_ConnectedDeviceList.Find(p => p.IP == dev.IP);
                        if (item == null)
                        {
                            m_ConnectedDeviceList.Add(dev);
                        }
                        bool ret = m_DeviceDic.TryAdd(loginID, dev);
                        if (!ret)
                        {
                            LogHelper.WriteWithMethod(loginID.ToString());
                        }
                        if (ct.IsCancellationRequested)
                        {
                            m_IsCancel = true;
                            LogHelper.WriteWithMethod("Stop");
                            break;
                        }
                        Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, dev);
                        Dispatcher.BeginInvoke((Action<Device>)UpdateConnectedUI, dev);
                        if (ct.IsCancellationRequested)
                        {
                            m_IsCancel = true;
                            LogHelper.WriteWithMethod("Stop");
                            break;
                        }

                        try
                        {
                            if (ct.IsCancellationRequested)
                            {
                                m_IsCancel = true;
                                LogHelper.WriteWithMethod("Stop");
                                break;
                            }
                            NETClient.StartListen(loginID);
                        }
                        catch (Exception ex)
                        {
                            if (ex is NETClientExcetion)
                            {
                                Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "StartListen", dev.IP);
                            }
                            else
                            {
                                LogHelper.WriteException(ex);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ex is NETClientExcetion)
                        {
                            Dispatcher.BeginInvoke((Action<Device>)UpdateWaitUI, dev);
                            Dispatcher.BeginInvoke((Action<NETClientExcetion, string, string>)UpdateError, ex as NETClientExcetion, "Login", dev.IP);
                            Dispatcher.BeginInvoke((Action<Device>)UpdateConnectFailedUI, dev);
                        }
                        else
                        {
                            LogHelper.WriteException(ex);
                        }
                    }
                }
                if (m_IsCancel == false)
                {
                    Dispatcher.BeginInvoke((Action)UpdateBtn);
                }
                LogHelper.WriteWithMethod("Leave");
            }), m_Factory, m_CancellationTokenSource.Token);
        }

        void UpdateBtn()
        {
            LogHelper.WriteWithMethod("Enter");
            if (m_FailedDeviceList.Count > 0)
            {
                towait_btn.IsEnabled = true;
            }
            LogHelper.WriteWithMethod("Leave");
        }
    }

    public class AlarmMsg
    {
        public string IP { get; set; }
        public IntPtr LoginID { get; set; }
        public EM_ALARM_TYPE AlarmType { get; set; }
        public object AlarmInfo { get; set; }
        public int Length { get; set; }
        public string Time { get; set; }
    }
    public class Device
    {
        public string IP { get; set; }
        public ushort Port { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public Status DeviceStatus { get; set; }
        public int ChannelCount { get; set; }
        public IntPtr LoginID { get; set; }

        public override string ToString()
        {
            switch (DeviceStatus)
            {
                case Status.OffLine:
                    return IP + (string)App.Current.Resources["ID_OFFLINE"];
                case Status.OnLine:
                    return IP + (string)App.Current.Resources["ID_ONLINE"];
                default:
                    return IP;
            }
        }
    }

    public enum Status : int
    {
        OffLine = -1,
        UnKnow = 0,
        OnLine = 1,
    }

    public class ErrorInfo
    {
        public string ID { get; set; }
        public string Time { get; set; }
        public string IP { get; set; }
        public string ErrorType { get; set; }
        public string ErrorCode { get; set; }
        public string ErrorMessage { get; set; }
    }

    public class AlarmInfo
    {
        public string ID { get; set; }
        public string Time { get; set; }
        public string IP { get; set; }
        public string Channel { get; set; }
        public string Type { get; set; }
        public EM_ALARM_TYPE AlarmType { get; set; }
        public IntPtr LoginID { get; set; }
    }

    public class PerformanceInfo
    {
        public string Time { get; set; }
        public string Count { get; set; }
    }
}
