using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Inspectron.HawkEye.Protocol.Discovery
{
    public class DiscoveryClient
    {
        public ReadOnlyCollection<CameraInfo> Discovered => new ReadOnlyCollection<CameraInfo>(_discovered);
        private readonly int _port;
        private readonly List<CameraInfo> _discovered = new List<CameraInfo>();

        private readonly object _discoveryLock = new object();

        public DiscoveryClient(int port)
        {
            _port = port;
        }

        public event Action<CameraInfo> CameraFound = delegate { };

        public void Discover()
        {
            var allInterfaces = NetworkInterface
                .GetAllNetworkInterfaces()
                .Where(nic => nic.OperationalStatus == OperationalStatus.Up);


            Parallel.ForEach(allInterfaces, DiscoverOnInterface);
            //foreach (NetworkInterface i in allInterfaces)
            //{
            //    DiscoverOnInterface(i);
            //}
        }

        private void DiscoverOnInterface(NetworkInterface iface)
        {
            var address = iface.GetIPProperties().UnicastAddresses
                .First(x => x.Address.AddressFamily == AddressFamily.InterNetwork).Address;
            UdpClient client;
            lock (_discoveryLock)
            {
                client = new UdpClient(new IPEndPoint(address, 0));
                var requestData = Encoding.ASCII.GetBytes("discovery");
                client.Client.ReceiveTimeout = 2000;

                var s = client.Client;
                s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
                client.EnableBroadcast = true;
                client.Send(requestData, requestData.Length, new IPEndPoint(IPAddress.Broadcast, _port));
                client.Send(requestData, requestData.Length, new IPEndPoint(IPAddress.Broadcast, _port));
                client.Send(requestData, requestData.Length, new IPEndPoint(IPAddress.Broadcast, _port));
            }

            var serverEp = new IPEndPoint(IPAddress.Any, 0);

            byte[] serverResponseData;
            try
            {
                serverResponseData = client.Receive(ref serverEp);
                var serverResponse = Encoding.ASCII.GetString(serverResponseData);
                Console.WriteLine("Recived {0} from {1}", serverResponse, serverEp.Address);
                var found = new CameraInfo {Mac = serverResponse, Address = serverEp.Address,AdapterAddress = address};
                lock (_discoveryLock)
                {
                    if (_discovered.Any(x => x.Mac == found.Mac)) return;
                }
                
                CameraFound(found);
                _discovered.Add(found);
            }
            catch
            {
            }
        }
    }
}