using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using SharpPcap;
using System.Threading;
using PacketDotNet.Utils;
using PacketDotNet;
using SharpPcap.WinPcap;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;

namespace Skyffer
{
    internal static class PacketIPHelper
    {
        internal const int MAX_HISTORY = 1000;

        private static int _port = 0;
        private static string _localAddresses = "";

        private static CaptureDeviceList _devices;
        internal static CaptureDeviceList Devices { get { if (_devices == null) _devices = CaptureDeviceList.Instance; return _devices; } }

        private static ICaptureDevice device;

        internal static Dictionary<string, Location> _LastIPs = new Dictionary<string, Location>();
        internal static ObservableCollection<Location> _IPHistory = new ObservableCollection<Location>();

        public delegate void LocationEventHandler(Location loc);
        public static event LocationEventHandler LocationAdded;
        public static event LocationEventHandler LocationRemoved;

        internal static void Start(int deviceIndex, int port)
        {
            Thread t = new Thread(() => ClearIPs());
            t.IsBackground = true;
            t.Start();
            _port = port;
            device = Devices[deviceIndex];
            foreach (var add in ((WinPcapDevice)device).Addresses)
                if (add.Addr.type == SharpPcap.LibPcap.Sockaddr.Type.AF_INET_AF_INET6)
                    _localAddresses += add.Addr.ToString() + ";";

            device.OnPacketArrival += new PacketArrivalEventHandler(device_OnPacketArrival);
            device.Open(DeviceMode.Normal);
            device.Filter = "udp[8] & 0xC0 == 0x80";
            device.StartCapture();
        }

        private static object _lockStop = new object();
        internal static void Stop()
        {
            lock (_lockStop)
            {
                try
                {
                    if (device != null)
                        device.StopCapture();
                }
                catch { /*nic*/ }
                device = null;
            }
        }

        private static object ClearIPs()
        {
            while (true)
            {
                Thread.Sleep(1000);
                List<string> toRemove = new List<string>();
                lock (_LastIPs)
                {
                    foreach (var kvp in _LastIPs)

                        if ((DateTime.Now - kvp.Value.LastSeen).TotalSeconds > 60)
                            toRemove.Add(kvp.Key);
                    foreach (var x in toRemove)
                    {
                        //if (LocationRemoved != null)
                        //    LocationRemoved(_LastIPs[x]);
                        _LastIPs[x].IsSelected = false;
                        _LastIPs.Remove(x);
                    }
                }
            }
        }


        static void device_OnPacketArrival(object sender, CaptureEventArgs e)
        {
            var eth = TcpPacket.ParsePacket(LinkLayers.Ethernet, e.Packet.Data);
            if (eth.PayloadPacket == null)
                return;
            var ipv4 = eth.PayloadPacket as IPv4Packet;
            if (ipv4 != null)
            {
                if (!_localAddresses.Contains(ipv4.DestinationAddress.ToString()))
                    return;
                var udp = ipv4.PayloadPacket as UdpPacket;
                if (udp == null)
                    return;
                if (udp.DestinationPort != _port)
                    return;

                string add = ipv4.SourceAddress.ToString();
                lock (_LastIPs)
                {
                    if (_LastIPs.ContainsKey(add))
                        _LastIPs[add].LastSeen = DateTime.Now;
                    else
                    {
                        Location loc = new Location(add);
                            _LastIPs.Add(add, loc);
                    }
                }

            }
        }

        internal static void NotifyAdd(Location loc)
        {
            if (LocationAdded != null)
                LocationAdded(loc);
        }

        internal static void NotifyRemove(Location loc)
        {
            if (LocationRemoved != null)
                LocationRemoved(loc);
        }
    }
}
