using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Net;
using System.Collections;
using NATUPNPLib;
using System.Runtime.InteropServices.CustomMarshalers;

namespace NATUPNPLib
{
    [ComImport, Guid("4FC80282-23B6-4378-9A27-CD8F17C9400C"), TypeLibType((short)0x1040)]
    public interface IDynamicPortMapping
    {
        [DispId(1)]
        string ExternalIPAddress { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }
        [DispId(2)]
        string RemoteHost { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] get; }
        [DispId(3)]
        int ExternalPort { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] get; }
        [DispId(4)]
        string Protocol { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)] get; }
        [DispId(5)]
        int InternalPort { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(5)] get; }
        [DispId(6)]
        string InternalClient { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(6)] get; }
        [DispId(7)]
        bool Enabled { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(7)] get; }
        [DispId(8)]
        string Description { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(8)] get; }
        [DispId(9)]
        int LeaseDuration { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(9)] get; }
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(10)]
        int RenewLease([In] int lLeaseDurationDesired);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
        void EditInternalClient([In, MarshalAs(UnmanagedType.BStr)] string bstrInternalClient);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(12)]
        void Enable([In] bool vb);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(13)]
        void EditDescription([In, MarshalAs(UnmanagedType.BStr)] string bstrDescription);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(14)]
        void EditInternalPort([In] int lInternalPort);
    }

    [ComImport, TypeLibType((short)0x1040), Guid("B60DE00F-156E-4E8D-9EC1-3A2342C10899")]
    public interface IDynamicPortMappingCollection : IEnumerable
    {
        [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "", MarshalTypeRef = typeof(EnumeratorToEnumVariantMarshaler), MarshalCookie = "")]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(-4), TypeLibFunc((short)0x41)]
        new IEnumerator GetEnumerator();
        [DispId(0)]
        IDynamicPortMapping this[string bstrRemoteHost, int lExternalPort, string bstrProtocol] { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0)] get; }
        [DispId(1)]
        int Count { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)]
        void Remove([In, MarshalAs(UnmanagedType.BStr)] string bstrRemoteHost, [In] int lExternalPort, [In, MarshalAs(UnmanagedType.BStr)] string bstrProtocol);
        [return: MarshalAs(UnmanagedType.Interface)]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)]
        IDynamicPortMapping Add([In, MarshalAs(UnmanagedType.BStr)] string bstrRemoteHost, [In] int lExternalPort, [In, MarshalAs(UnmanagedType.BStr)] string bstrProtocol, [In] int lInternalPort, [In, MarshalAs(UnmanagedType.BStr)] string bstrInternalClient, [In] bool bEnabled, [In, MarshalAs(UnmanagedType.BStr)] string bstrDescription, [In] int lLeaseDuration);
    }

    [ComImport, TypeLibType((short)0x1040), Guid("624BD588-9060-4109-B0B0-1ADBBCAC32DF")]
    public interface INATEventManager
    {
        [DispId(1)]
        object ExternalIPAddressCallback { [param: In, MarshalAs(UnmanagedType.IUnknown)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] set; }
        [DispId(2)]
        object NumberOfEntriesCallback { [param: In, MarshalAs(UnmanagedType.IUnknown)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] set; }
    }

    [ComImport, TypeLibType((short)0x1040), Guid("6F10711F-729B-41E5-93B8-F21D0F818DF1")]
    public interface IStaticPortMapping
    {
        [DispId(1)]
        string ExternalIPAddress { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }
        [DispId(2)]
        int ExternalPort { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] get; }
        [DispId(3)]
        int InternalPort { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] get; }
        [DispId(4)]
        string Protocol { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(4)] get; }
        [DispId(5)]
        string InternalClient { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(5)] get; }
        [DispId(6)]
        bool Enabled { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(6)] get; }
        [DispId(7)]
        string Description { [return: MarshalAs(UnmanagedType.BStr)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(7)] get; }
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(8)]
        void EditInternalClient([In, MarshalAs(UnmanagedType.BStr)] string bstrInternalClient);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(9)]
        void Enable([In] bool vb);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(10)]
        void EditDescription([In, MarshalAs(UnmanagedType.BStr)] string bstrDescription);
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
        void EditInternalPort([In] int lInternalPort);
    }

    [ComImport, Guid("CD1F3E77-66D6-4664-82C7-36DBB641D0F1"), TypeLibType((short)0x1040)]
    public interface IStaticPortMappingCollection : IEnumerable
    {
        [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "", MarshalTypeRef = typeof(EnumeratorToEnumVariantMarshaler), MarshalCookie = "")]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(-4), TypeLibFunc((short)0x41)]
        new IEnumerator GetEnumerator();
        [DispId(0)]
        IStaticPortMapping this[int lExternalPort, string bstrProtocol] { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0)] get; }
        [DispId(1)]
        int Count { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)]
        void Remove([In] int lExternalPort, [In, MarshalAs(UnmanagedType.BStr)] string bstrProtocol);
        [return: MarshalAs(UnmanagedType.Interface)]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)]
        IStaticPortMapping Add([In] int lExternalPort, [In, MarshalAs(UnmanagedType.BStr)] string bstrProtocol, [In] int lInternalPort, [In, MarshalAs(UnmanagedType.BStr)] string bstrInternalClient, [In] bool bEnabled, [In, MarshalAs(UnmanagedType.BStr)] string bstrDescription);
    }

    [ComImport, TypeLibType((short)0x1040), Guid("B171C812-CC76-485A-94D8-B6B3A2794E99")]
    public interface IUPnPNAT
    {
        [DispId(1)]
        IStaticPortMappingCollection StaticPortMappingCollection { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }
        [DispId(2)]
        IDynamicPortMappingCollection DynamicPortMappingCollection { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] get; }
        [DispId(3)]
        INATEventManager NATEventManager { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] get; }
    }

    [ComImport, Guid("B171C812-CC76-485A-94D8-B6B3A2794E99"), CoClass(typeof(UPnPNATClass))]
    public interface UPnPNAT : IUPnPNAT
    {
    }

    [ComImport, ClassInterface((short)0), TypeLibType((short)2), Guid("AE1E00AA-3FD5-403C-8A27-2BBDC30CD0E1")]
    public class UPnPNATClass : IUPnPNAT, UPnPNAT
    {
        // Properties
        [DispId(2)]
        extern IDynamicPortMappingCollection DynamicPortMappingCollection { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] get; }

        [DispId(3)]
        extern INATEventManager NATEventManager { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] get; }

        [DispId(2)]
        extern IDynamicPortMappingCollection NATUPNPLib.IUPnPNAT.DynamicPortMappingCollection { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(2)] get; }

        [DispId(3)]
        extern INATEventManager NATUPNPLib.IUPnPNAT.NATEventManager { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(3)] get; }

        [DispId(1)]
        extern IStaticPortMappingCollection NATUPNPLib.IUPnPNAT.StaticPortMappingCollection { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }

        [DispId(1)]
        extern IStaticPortMappingCollection StaticPortMappingCollection { [return: MarshalAs(UnmanagedType.Interface)] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(1)] get; }
    }
}

namespace WinnyProtocolAnalyzer
{
    static class UPnP
    {
        const string Description = "Winny Protocol Analyzer";

        static public int AddPortMapping(int Port)
        {
            try
            {
                // Initialize
                UPnPNAT NAT = new UPnPNAT();
                IStaticPortMappingCollection StaticPortCollection = NAT.StaticPortMappingCollection;

                // Get Internal Client
                string InternalClient = "";
                IPHostEntry Host = Dns.GetHostEntry(Dns.GetHostName());
                foreach (IPAddress IPAddress in Host.AddressList)
                {
                    if (IPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                    {
                        continue;
                    }

                    InternalClient = IPAddress.ToString();
                }

                // Check Static Port Mapping: TCP
                bool Enable = false;
                string Protocol = "TCP";
                foreach (IStaticPortMapping PortMap in StaticPortCollection)
                {
                    if (PortMap.ExternalPort == Port &&
                        PortMap.InternalPort == Port &&
                        PortMap.InternalClient == InternalClient &&
                        PortMap.Protocol == Protocol &&
                        PortMap.Description == Description + " " + "(" + Protocol + ")")
                    {
                        Enable = true;
                    }
                }
                // Add Port Mapping: TCP
                if (!Enable)
                {
                    IStaticPortMapping Result = StaticPortCollection.Add(Port, Protocol, Port, InternalClient, true, Description + " " + "(" + Protocol + ")");
                    if (Result.ExternalPort == Port &&
                    Result.InternalPort == Port &&
                    Result.InternalClient == InternalClient &&
                    Result.Protocol == Protocol &&
                    Result.Description == Description + " " + "(" + Protocol + ")")
                    {
                        Debug.WriteLine(string.Format("Pormapping Success ({0})", Protocol));
                    }
                }

                return 0;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (UPnP)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }

            return -1;
        }

        static public int RemovePortMapping(int Port)
        {
            try
            {
                // Initialize
                UPnPNAT NAT = new UPnPNAT();
                IStaticPortMappingCollection StaticPortCollection = NAT.StaticPortMappingCollection;

                // Get Internal Client
                string InternalClient = "";
                IPHostEntry Host = Dns.GetHostEntry(Dns.GetHostName());
                foreach (IPAddress IPAddress in Host.AddressList)
                {
                    if (IPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                    {
                        continue;
                    }

                    InternalClient = IPAddress.ToString();
                }

                // Check Static Port Mapping: TCP
                bool Enable = false;
                string Protocol = "TCP";
                foreach (IStaticPortMapping PortMap in StaticPortCollection)
                {
                    if (PortMap.ExternalPort == Port &&
                        PortMap.InternalPort == Port &&
                        PortMap.InternalClient == InternalClient &&
                        PortMap.Protocol == Protocol &&
                        PortMap.Description == Description + " " + "(" + Protocol + ")")
                    {
                        Enable = true;
                    }
                }
                if (!Enable)
                {
                    StaticPortCollection.Remove(Port, Protocol);
                }

                return 0;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (UPnP)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }

            return -1;
        }
    }
}
