// RunUO 2.0 Venus Düzenlemesi <admin@venus.gen.tr>

using System;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Server;
using Server.Network;

namespace Server.Misc
{
	public class ServerList
	{
		public static readonly string Address = null;

		public const string ServerName = Ayarlar.ServerName;

		public static readonly bool AutoDetect = true;

		public static void Initialize()
		{
			if ( Address == null ) {
				if ( AutoDetect )
					AutoDetection();
			}
			else
				Resolve( Address, out m_PublicAddress );

			EventSink.ServerList += new ServerListEventHandler( EventSink_ServerList );
		}

		private static IPAddress m_PublicAddress;

		private static void EventSink_ServerList( ServerListEventArgs e )
		{
			try
			{
				NetState ns = e.State;
				Socket s = ns.Socket;

				IPEndPoint ipep = (IPEndPoint)s.LocalEndPoint;

				IPAddress localAddress = ipep.Address;
				int localPort = ipep.Port;

				if ( IsPrivateNetwork( localAddress ) ) {
					ipep = (IPEndPoint)s.RemoteEndPoint;
					if ( !IsPrivateNetwork( ipep.Address ) && m_PublicAddress != null )
						localAddress = m_PublicAddress;
				}

				e.AddServer( ServerName, new IPEndPoint( localAddress, localPort ) );
			}
			catch
			{
				e.Rejected = true;
			}
		}

		private static void AutoDetection()
		{
			if ( !HasPublicIPAddress() ) {
				Console.Write( "ServerList: IP adresler saptanıyor..." );
				m_PublicAddress = FindPublicAddress();

				if ( m_PublicAddress != null )
					Console.WriteLine( "tamam ({0})", m_PublicAddress.ToString() );
				else
					Console.WriteLine( "hatalı" );
			}
		}

		private static void Resolve( string addr, out IPAddress outValue )
		{
			if ( IPAddress.TryParse( addr, out outValue ) )
				return;

			try {
				IPHostEntry iphe = Dns.GetHostEntry( addr );

				if ( iphe.AddressList.Length > 0 )
					outValue = iphe.AddressList[iphe.AddressList.Length - 1];
			}
			catch {
			}
		}

		private static bool HasPublicIPAddress()
		{
			NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();

			foreach ( NetworkInterface adapter in adapters ) {
				IPInterfaceProperties properties = adapter.GetIPProperties();

				foreach ( IPAddressInformation unicast in properties.UnicastAddresses ) {
					IPAddress ip = unicast.Address;

					if ( !IPAddress.IsLoopback( ip ) && ip.AddressFamily != AddressFamily.InterNetworkV6 && !IsPrivateNetwork( ip ) )
						return true;
				}
			}

			return false;


			/*
			IPHostEntry iphe = Dns.GetHostEntry( Dns.GetHostName() );

			IPAddress[] ips = iphe.AddressList;

			for ( int i = 0; i < ips.Length; ++i )
			{
				if ( ips[i].AddressFamily != AddressFamily.InterNetworkV6 && !IsPrivateNetwork( ips[i] ) )
					return true;
			}

			return false;
			*/
		}

		private static bool IsPrivateNetwork( IPAddress ip )
		{
			// 10.0.0.0/8
			// 172.16.0.0/12
			// 192.168.0.0/16

			if ( ip.AddressFamily == AddressFamily.InterNetworkV6 )
				return false;

			if ( Utility.IPMatch( "192.168.*", ip ) )
				return true;
			else if ( Utility.IPMatch( "10.*", ip ) )
				return true;
			else if ( Utility.IPMatch( "172.16-31.*", ip ) )
				return true;
			else
				return false;
		}

		private static IPAddress FindPublicAddress()
		{
			try {
				WebRequest req = HttpWebRequest.Create( "http://www.runuo.com/ip.php" );
				req.Timeout = 15000;

				WebResponse res = req.GetResponse();

				Stream s = res.GetResponseStream();

				StreamReader sr = new StreamReader( s );

				IPAddress ip = IPAddress.Parse( sr.ReadLine() );

				sr.Close();
				s.Close();
				res.Close();

				return ip;
			} catch {
				return null;
			}
		}
	}
}