using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;

namespace NetworkDiscovery
{
    public partial class Form_Main : Form
    {
        #region Constants

        private const int _UDP_TX_PORT = 49049;   // порт передачи сообщений
        private const int _UDP_RX_PORT = 49049;   // порт приема сообщений
        private const int _delay = 500; // интервал между отсылками сообщений
        private const int _timeOut = 5000; //время тайм-аута по приёму сообщений
        private const int _timeOutAfterApply = 10000;   //время тайм-аута по приему сообщений после
        //изменения IP
        #endregion

        #region Fields

        private List<string> _messages = new List<string>();
        protected UdpClient _listener;
        private List<string> _sendMessages = new List<string>() { "partno;serno;MAC;sw version;;;stm32id;;prodate;status;;" };
        private bool _isAfterApply = false;

        #endregion

        public Form_Main()
        {
            InitializeComponent();
        }

        private void Button_Start_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(UpdateDeviceList);
            t.Start();
        }


        private void Button_About_Click(object sender, EventArgs e)
        {
            Form_About inf_form = new Form_About();
            inf_form.ShowDialog();
        }

        /// <summary>
        /// Обновление списка устройств.
        /// </summary>
        private void UpdateDeviceList()
        {
            Invoke((Action)(() => Button_Start.Enabled = false));
            Invoke((Action)(() => label.Visible = false));
            Invoke((Action)(() => progressBar.Style = ProgressBarStyle.Marquee));
            dataGridView.Rows.Clear();
            StartListener();
            UpdateDataGridView();
            Invoke((Action)(() => Button_Start.Enabled = true));
            Invoke((Action)(() => progressBar.Style = ProgressBarStyle.Blocks));
            try 
            {
                Invoke((Action)(() => label.Text = "Найдено " + (dataGridView.RowCount))); 
            }
            catch { }

            Invoke((Action)(() => label.Visible = true));
        }

        /// <summary>
        /// Обновление таблицы
        /// </summary>
        private void UpdateDataGridView()
        {
            for (int i = 0; i < _messages.Count; i++)
            {
                String[] substring = Regex.Split(_messages[i], ";");
                if (substring.Length == 12)
                {
                    string[] row = { substring[1], substring[11], substring[2], substring[0], substring[3] };
                    dataGridView.Rows.Add(row);
                }
            }

            _messages.Clear();
        }

        /// <summary>
        /// Запуск слушателя upd-запросов
        /// </summary>
        protected void StartListener()
        {
            Thread sender = new Thread(Sender);
            sender.Start();

            try
            {
                if (CheckPortFree(_UDP_RX_PORT))
                    _listener = new UdpClient(_UDP_RX_PORT);
                else return;

                IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _UDP_RX_PORT);

                List<IPAddress> myIpList = GetMyIpList();

                DateTime startTime = DateTime.Now;

                while (true)
                {
                    if (_listener.Available > 0)
                    {
                        byte[] bytes = _listener.Receive(ref groupEP);

                        if ((!_messages.Contains(Encoding.UTF8.GetString(bytes, 0, bytes.Length) + groupEP.Address.ToString())) && (!myIpList.Contains(groupEP.Address)))
                            _messages.Add(Encoding.UTF8.GetString(bytes, 0, bytes.Length) + groupEP.Address.ToString());
                    }

                    int timeOut = _isAfterApply ? _timeOutAfterApply : _timeOut;

                    if (DateTime.Now.Subtract(startTime) >= new TimeSpan(0, 0, 0, 0, timeOut))
                        break;
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("Неудачная попытка использования порта: закройте другие экземляры программы и попробуйте заново", "Порт Занят",
                MessageBoxButtons.OK, MessageBoxIcon.Error);

            }
            finally
            {
                sender.Abort();
                _listener.Close();
            }

        }

        /// <summary>
        /// Проверка доступности порта
        /// </summary>
        /// <param name="nPortNum">Номер порта</param>
        /// <returns>Истина, если порт доступен</returns>
        public static bool CheckPortFree(int nPortNum)
        {
            bool bCheckResult = true;
            Socket s = null;

            try
            {
                s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                s.Bind(new IPEndPoint(IPAddress.Loopback, nPortNum));

            }


            catch (Exception e)
            {
                MessageBox.Show("Порт занят сторонним приложением", "Порт Занят",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                bCheckResult = false;


            }
            finally
            {
                s.Close();
            }


            return bCheckResult;
        }


        protected static List<IPAddress> GetMyIpList()
        {
            List<IPAddress> MyIpList = new List<IPAddress>();
            String strHostName = Dns.GetHostName();
            IPHostEntry iphostentry = Dns.GetHostEntry(strHostName);
            foreach (IPAddress ipaddress in iphostentry.AddressList)
            {
                MyIpList.Add(ipaddress);
            }
            return MyIpList;
        }

        private void Sender()
        {
            try
            {
                while (true)
                {
                    _messages.ForEach(m => SendBroadcast(m));
                    Thread.Sleep(_delay);

                }
            }
            catch (ThreadAbortException e)
            {
                Thread.ResetAbort();
                return;
            }

        }

        protected void SendBroadcast(String message)
        {
            UdpClient client = new UdpClient();
            IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, _UDP_TX_PORT);
            Byte[] bytes = Encoding.ASCII.GetBytes(message);
            try
            {
                client.Send(bytes, bytes.Length, ip);
            }

            catch (Exception e)
            {
                MessageBox.Show("Порт занят, попробуйте еще раз", "Порт отправки занят",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                client.Close();
            }
        }

        private void dataGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            string ip = dataGridView.Rows[dataGridView.CurrentCell.RowIndex].Cells[1].Value.ToString();
            string serno = dataGridView.Rows[dataGridView.CurrentCell.RowIndex].Cells[0].Value.ToString();

            Form_IpConfig form = new Form_IpConfig(ip, serno);
            form.ShowDialog();
            _isAfterApply = form.isChanged;

            if (!_isAfterApply) return;

            Thread t = new Thread(UpdateDeviceList);
            t.Start();
        }



    }
}
