/*

CLASS {NATIVE_Socket|_CSharp},
UNTESTED, NATIVE, BYTES:

IMPORT: {Socket} .

NATIVE: "///COPY NATIVE_Socket.cs" .
NATIVE: "using System.Net;" .
NATIVE: "using System.Net.Sockets;" .
NATIVE: "using System.Net.NetworkInformation;" .

*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;

using AL4;

namespace AL4
{
    public class NATIVE_Socket
    {
        
        public static List<Socket> All_sockets = new List<Socket>();
        
/*

FUN Create_socket(
{Socket}.{address_family} Address|_family,
{Socket}.{socket_type} Type|_of_socket,
{Socket}.{protocol} Protocol,
BOOL Is_server) ==> INT, NATIVE: @
	"RESULT = NATIVE_Socket.Create_socket("
	"	X_Address, X_Type, X_Protocol, X_Is_server);" .
	
*/
	public static int Create_socket(
		byte X_Address,
		byte X_Type,
		byte X_Protocol,
		bool X_Is_server)
	{
		AddressFamily a = AddressFamily.Unspecified;
		switch(X_Address)
		{
		case 0: break; // 'UNSPECIFIED_ADDR|ESS'
		case 1: a = AddressFamily.AppleTalk; break; // 'APPLE_TALK'
		case 2: a = AddressFamily.Atm; break; // 'ATM|_own_services'
		case 3: a = AddressFamily.Banyan; break; // 'BANYAN|_address'
		case 4: a = AddressFamily.Ccitt; break; // 'CCITT|_like_x25'
		case 5: a = AddressFamily.Chaos; break; // 'CHAOS|_MIT_CHAOS'
		case 6: a = AddressFamily.Cluster; break; // 'CLUSTER|_Microsoft'
		case 7: a = AddressFamily.DataKit; break; // 'DATA_KIT'
		case 8: a = AddressFamily.DataLink; break; // 'DATA_LINK'
		case 9: a = AddressFamily.DecNet; break; // 'DEC_NET|_DECnet'
		case 10: a = AddressFamily.Ecma; break; // 'ECMA|_European_Computer_Manufacturers_Association'
		case 11: a = AddressFamily.FireFox; break; // 'FIRE_FOX'
		case 12: a = AddressFamily.HyperChannel; break; // 'HYPER_CHANNEL'
		case 13: a = AddressFamily.Ieee12844; break; // 'IEEE12844'
		case 14: a = AddressFamily.ImpLink; break; // 'IMP_LINK|ARPANET_IMP'
		case 15: a = AddressFamily.InterNetwork; break; // 'INTERNETWORK|_IPv4'
		case 16: a = AddressFamily.InterNetworkV6; break; // 'INTERNETWORK_V6'
		case 17: a = AddressFamily.Ipx; break; // 'IPX|_SPX'
		case 18: a = AddressFamily.Irda; break; // 'IRDA|_IsDA_InfraRed'
		case 19: a = AddressFamily.Iso; break; // 'ISO|_address'
		case 20: a = AddressFamily.Lat; break; // 'LAT|_address'
		case 21: a = AddressFamily.Max; break; // 'MAX|_address'
		case 22: a = AddressFamily.NetBios; break; // 'NET_BIOS'
		case 23: a = AddressFamily.NetworkDesigners; break; // 'NETWORK_DESIGNERS'
		case 24: a = AddressFamily.NS; break; // 'NS|_XEROX'
		case 25: a = AddressFamily.Osi; break; // 'OSI|_address'
		case 26: a = AddressFamily.Pup; break; // 'PUP|_address'
		case 27: a = AddressFamily.Sna; break; // 'SNA|_IBM_SNA'
		case 28: a = AddressFamily.Unix; break; // 'UNIX|_local_address'
		case 29: a = AddressFamily.Unknown; break; // 'UNKNOWN_ADDR|ESS'
		case 30: a = AddressFamily.VoiceView; break; // 'VOICE_VIEW'
		}
		SocketType t = SocketType.Unknown;
		switch(X_Type)
		{
		case 0: t = SocketType.Unknown; break; //'UNKNOWN_TYPE'
		case 1: t = SocketType.Dgram; break; // 'DATAGRAM|_UDP'
		case 2: t = SocketType.Raw; break; // 'RAW|_ICMP_IGMP'
		case 3: t = SocketType.Rdm; break; // 'RDM|_Reliably_Delivered_Messages'
		case 4: t = SocketType.Seqpacket; break; // 'SEQPACKET'
		case 5: t = SocketType.Stream; break; // 'STREAM|_TCP'
		}
		ProtocolType p = ProtocolType.Unspecified;
		switch(X_Protocol)
		{
		case 0: p = ProtocolType.Unspecified; break; // 'UNSPECIFIED|_PROTOCOL'
		case 1: p = ProtocolType.Ggp; break; // 'GGP|_Gateway_to_Gateway'
		case 2: p = ProtocolType.Icmp; break; // 'ICMP|_Internet_control_message'
		case 3: p = ProtocolType.IcmpV6; break; // 'ICMP_V6|_for_ipv6'
		case 4: p = ProtocolType.Idp; break; // 'IDP|_Internet_datagrams'
		case 5: p = ProtocolType.Igmp; break; // 'IGMP|_Internet_group_management'
		case 6: p = ProtocolType.IP; break; // 'IP|_Internet_protocol'
		case 7: p = ProtocolType.IPSecAuthenticationHeader; break;
		case 8: p = ProtocolType.IPSecEncapsulatingSecurityPayload; break;
		case 9: p = ProtocolType.IPv4; break; // 'IPv4|_IP_protocol_version_4'
		case 10: p = ProtocolType.IPv6; break; // 'IPv6|_IP_protocol_version_6'
		case 11: p = ProtocolType.IPv6DestinationOptions; break;
		case 12: p = ProtocolType.IPv6FragmentHeader; break;
		case 13: p = ProtocolType.IPv6HopByHopOptions; break;
		case 14: p = ProtocolType.IPv6NoNextHeader; break;
		case 15: p = ProtocolType.IPv6RoutingHeader; break;
		case 16: p = ProtocolType.Ipx; break; // 'IPX|Internetwork_paclage_exchange'
		case 17: p = ProtocolType.ND; break; // 'ND|Unofficial_Network_Disk'
		case 18: p = ProtocolType.Pup; break; // 'PUP||PARC_Universal_Packet'
		case 19: p = ProtocolType.Raw; break; // 'RAW|Not_handling_IP_packets'
		case 20: p = ProtocolType.Spx; break; // 'SPX|Sequencial_packets_exchange'
		case 21: p = ProtocolType.SpxII; break; // 'SPXII | SPX_version_2'
		case 22: p = ProtocolType.Tcp; break; // 'TCP|Transmission_control_protocol'
		case 23: p = ProtocolType.Udp; break; // 'UDP|User_datagrams_protocol'
		case 24: p = ProtocolType.Unknown; break; // 'Unknown_protocol'
		}
		Socket s = new Socket(a, t, p);
		if (All_sockets.Count == 0) All_sockets.Add(null);
		for (int i = 1; i < All_sockets.Count; i++)
		{
			if (All_sockets[i] == null)
			{
				All_sockets[i] = s;
				return i;
			}
		}
		All_sockets.Add(s);
		return All_sockets.Count-1;
	}
	
	}
}

/*

---------------------------------- 'SERVER'

FUN Listen_socket(
{Socket} SO|bject_socket,
STR Host|_address,
INT Port|_number_or_0,
INT Max_queue) ==> BOOL, NATIVE:
	CASE SO.Handle == 0 || !SO.Is_server ? ==> ;
	-------------------------- 'if address is ip', REUSED
	BOOL is_ip|_not_dns = TRUE
	FOR i IN [0 TO Host.Len-1] :
	    CASE Host.[i] !IN ".0123456789" ? 
	         is_ip = FALSE
	         BREAK i ;
	;
	------------------------- 'and native code now'
	NATIVE @   
"  	IPEndPoint e = new IPEndPoint(0, 0);"
"  	try"
"  	{"
"  		if (X_is_ip) e = new IPEndPoint(IPAddress.Parse(X_Host), (int)X_Port);"
"  		else"
"  		{"
"  			IPAddress a = Dns.Resolve(X_Host).AddressList[0];"
"  			e = new IPEndPoint(a, (int)X_Port);"
"  		}"
"  	}"
"  	catch(Exception ex) { return false; }"
"  	C_Socket so = (C_Socket)X_SO.Obj;"
"  	Socket server = NATIVE_Socket.All_sockets[(int)so.F_Handle];"
"  	server.Bind(e);"
"  	server.Listen((int)X_Max_queue);" 
"	RESULT = true; " .

FUN Socket_accept({Socket} SO|bject_socket) ==> INT, NATIVE:
	INT handle|_of_server_socket = SO.Handle
	NATIVE @
"   Socket server = NATIVE_Socket.All_sockets[(int)X_handle];"
"   Socket handler = server.Accept();"
"   if (handler == null) return 0;"
"	for (int i = 1; i < NATIVE_Socket.All_sockets.Count; i++)"
"	{"
"		if (NATIVE_Socket.All_sockets[i] == null)"
"		{"
"			NATIVE_Socket.All_sockets[i] = handler;"
"			return i;"
"		}"
"	}"
"   RESULT = NATIVE_Socket.All_sockets.Count;"
"   NATIVE_Socket.All_sockets.Add(handler);" .

FUN Socket_shutdown(
{Socket} SO|bject_socket|DUMMY, 
INT H|andler_connection_to_close), NATIVE : @
"	Socket handler = NATIVE_Socket.All_sockets[(int)X_H];"
"	handler.Shutdown(SocketShutdown.Both);"
"	handler.Close();"
"	NATIVE_Socket.All_sockets[(int)X_H] = null;" .

------------------------- 'CLIENT'

FUN Socket_connect(
{Socket} SO|bject_socket,
STR Host|_address,
INT Port|_number) ==> BOOL, NATIVE:
	INT handle|_of_client_socket = SO.Handle
	LIKE Listen_socket ................. 'if address is ip'
	NATIVE @
"	Socket client = NATIVE_Socket.All_sockets[(int)X_handle];"
"  	IPEndPoint e = new IPEndPoint(0, 0);"
"  	try"
"  	{"
"  		if (X_is_ip) e = new IPEndPoint(IPAddress.Parse(X_Host), (int)X_Port);"
"  		else"
"  		{"
"  			IPAddress a = Dns.Resolve(X_Host).AddressList[0];"
"  			e = new IPEndPoint(a, (int)X_Port);"
"  		}"
"		client.Connect(e);"
"  	}"
"  	catch(Exception ex) { return false; }"
"	RESULT = true;" .

------------------------- 'SEND / RECEIVE'

FUN Socket_receive(
{Socket} SO|bject_socket|DUMMY,
INT Handler|_socket_used_to_receive,
BYTE Data|_array_to_fill[]), NATIVE : @
"	Socket handler = NATIVE_Socket.All_sockets[(int)X_Handler];"
"	byte[] buf = new byte[256];"
"	do"
"	{"
"		int size = handler.Receive(buf);"
"		((DynAByte)X_Data).Capacity = X_Data.Count + size;"
"		for (int i = 0; i < size; i++)"
"			X_Data.Add(buf[i]);"
"	} while (handler.Available > 0);" .

FUN Socket_send(
{Socket} SO|bject_socket|DUMMY,
INT Handler|_socket_used_to_send,
BYTE Data|_array_to_send[*]), NATIVE : @
"	Socket handler = NATIVE_Socket.All_sockets[(int)X_Handler];"
"	byte[] buf = new byte[X_Data.Count];"
"	for (int i = 0; i < X_Data.Count; i++)"
"		buf[i] = X_Data.get_byte(i);"
"	handler.Send(buf);" .

-------------------------- 'CLOSE'

FUN Socket_close({Socket} SO|bject_socket), NATIVE:
	INT handle|_to_close = SO.Handle
	NATIVE @
"	Socket handler = NATIVE_Socket.All_sockets[(int)X_handle];"
"	try {"
"		if (handler != null)"
"		{"
"	    	handler.Shutdown(SocketShutdown.Both);"
"	    	handler.Close();"
"		}"     
"	} catch(Exception ex) {}"
"	NATIVE_Socket.All_sockets[(int)X_handle] = null;" .

------------------------------ 'Utility'

FUN Get_host_name ==> STR, NATIVE : @
"	RESULT = Dns.GetHostName();" .

FUN Get_host_ip ==> STR, NATIVE : @
"    UnicastIPAddressInformation mostSuitableIp = null;"
"    var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();"
"    foreach (var network in networkInterfaces)"
"    {"
"        if (network.OperationalStatus != OperationalStatus.Up) continue;"
"        var properties = network.GetIPProperties();"
"        if (properties.GatewayAddresses.Count == 0) continue;"
"        foreach (var address in properties.UnicastAddresses)"
"        {"
"            if (address.Address.AddressFamily != AddressFamily.InterNetwork)"
"                continue;"
"    "
"            if (IPAddress.IsLoopback(address.Address))"
"                continue;"
"    "
"            if (!address.IsDnsEligible)"
"            {"
"                if (mostSuitableIp == null)"
"                    mostSuitableIp = address;"
"                continue;"
"            }"
"    "
"            // The best IP is the IP got from DHCP server"
"            if (address.PrefixOrigin != PrefixOrigin.Dhcp)"
"            {"
"                if (mostSuitableIp == null || !mostSuitableIp.IsDnsEligible)"
"                    mostSuitableIp = address;"
"                continue;"
"            }"
"    "
"            return address.Address.ToString();"
"        }"
"    }"
"    "
"    if (mostSuitableIp != null) RESULT =  mostSuitableIp.Address.ToString();"
.

*/
        
/*
END
*/
