/*

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

IMPORT: {Socket} .

NATIVE: "///COPY NATIVE_Socket.java" .
NATIVE: "import java.net.*;" . 
NATIVE: "import java.io.*;" .
NATIVE: "import java.util.*;" .


*/

import java.util.ArrayList;
import java.net.*;

public class NATIVE_Socket {
        
	static class Stub_socket {
		public boolean is_server;
		public boolean is_udp;
	}
	
	public static ArrayList<Object> All_sockets = new ArrayList<Object>();
        
/*

FUN Create_socket(
{address_family} Address|_family,
{socket_type} Type|_of_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,
		boolean X_Is_server)
	{
		if (X_Address != 15 // 'INTERNETWORK|_IPv4'
		&& X_Address != 16 // 'INTERNETWORK_V6'
		) return 0;

		if (X_Type != 1 // 'DATAGRAM|_UDP'
		&& X_Type != 5 // 'STREAM|_TCP'
		) return 0;
		
		if (X_Protocol != 22 // 'TCP|Transmission_control_protocol'
		&& X_Protocol != 23 // 'UDP|User_datagrams_protocol'
		) return 0;
					
		Stub_socket s = new Stub_socket();
		s.is_server = X_Is_server;
		s.is_udp = X_Protocol == 23;
		
		if (All_sockets.size() == 0) All_sockets.add(null);
		for (int i = 1; i < All_sockets.size(); i++)
		{
			if (All_sockets.get(i) == null)
			{
				All_sockets.set(i, s);
				return i;
			}
		}
		All_sockets.add(s);
		return All_sockets.size()-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 @  
"  	C_Socket so = (C_Socket)X_SO.Obj;"
"	Object o = NATIVE_Socket.All_sockets.get(so.F_Handle);"
"	if (!(o instanceof NATIVE_Socket.Stub_socket)) return false;"
""
"	NATIVE_Socket.Stub_socket ss = (NATIVE_Socket.Stub_socket)o;"
"	if (!ss.is_server) return false;"
""
"	InetAddress Ia = InetAddress.getByName(X_Host);"
"	ServerSocket server = new ServerSocket(X_Port, X_Max_queue, Ia);"
"	NATIVE_Socket.All_sockets.set(so.F_Handle, server);"
""
"	RESULT = true; " .

FUN Socket_accept({Socket} SO|bject_socket) ==> INT, NATIVE:
	INT handle|_of_server_socket = SO.Handle
	NATIVE @
"  	C_Socket so = (C_Socket)X_SO.Obj;"
"	Object o = NATIVE_Socket.All_sockets.get(so.F_Handle);"
"	if (!(o instanceof ServerSocket)) return 0;"
"" 
"  	ServerSocket server = (ServerSocket)o;"
"   Socket handler = server.accept();"
"   if (handler == null) return 0;"
"	for (int i = 1; i < NATIVE_Socket.All_sockets.size(); i++)"
"	{"
"		if (NATIVE_Socket.All_sockets.get(i) == null)"
"		{"
"			NATIVE_Socket.All_sockets.set(i, handler);"
"			return i;"
"		}"
"	}"
"   RESULT = NATIVE_Socket.All_sockets.size();"
"   NATIVE_Socket.All_sockets.add(handler);" .

FUN Socket_shutdown(
{Socket} SO|bject_socket|DUMMY, 
INT H|andler_connection_to_close), NATIVE : @
"	Object o = NATIVE_Socket.All_sockets.get(X_H);"
"	if (!(o instanceof ServerSocket)) return;"
""
"  	ServerSocket server = (ServerSocket)o;"
""
"	server.close();"
"	NATIVE_Socket.All_sockets.set(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 @
"	Object o = NATIVE_Socket.All_sockets.get(X_handle);"
"	if (!(o instanceof NATIVE_Socket.Stub_socket)) return false;"
"	NATIVE_Socket.Stub_socket ss = (NATIVE_Socket.Stub_socket)o;"
"	if (ss.is_server) return false;"
"	InetAddress Ia = InetAddress.getByName(X_Host);"
"	if (ss.is_udp) {"
"		DatagramSocket udp_client = new DatagramSocket(X_Port, Ia);"
"		o = udp_client;"
"		RESULT = udp_client.isConnected();"
"	}"
"	else {"
"		Socket tcp_client = new Socket(X_Host, X_Port);"
"		o = tcp_client;"
"		RESULT = tcp_client.isConnected();"
"	}"
"	if (RESULT)	NATIVE_Socket.All_sockets.set(X_handle, o);" .

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

FUN Socket_receive(
{Socket} SO|bject_socket|DUMMY,
INT Handler|_socket_used_to_receive,
BYTE Data|_array_to_fill[]), NATIVE : @
"	Object o = NATIVE_Socket.All_sockets.get(X_Handler);"
""
"	if (o instanceof Socket) {"
"	    byte[] buf = new byte[256];"
"		InputStream iS = ((Socket)o).getInputStream();"
"		do {"
"			int size = iS.read(buf);"
"		    ((DynAByte)X_Data).Reallocate(X_Data.Count() + size);"
"		    for (int i = 0; i < size; i++)"
"			    X_Data.Add(buf[i]);"
"		} while(iS.available() > 0);"
"	}"
"	else if (o instanceof DatagramSocket) {"
"		 // UDP - not implemented"
"	}" .

FUN Socket_send(
{Socket} SO|bject_socket|DUMMY,
INT Handler|_socket_used_to_send,
BYTE Data|_array_to_send[*]), NATIVE : @
"	Object o = NATIVE_Socket.All_sockets.get(X_Handler);"
""
"	if (o instanceof Socket) {"
"	    byte[] buf = new byte[X_Data.Count()];"
"		OutputStream oS = ((Socket)o).getOutputStream();"
" 	    for (int i = 0; i < X_Data.Count(); i++)"
" 		    buf[i] = X_Data.Get_item(i);"
"		oS.write(buf);"
"	}"
"	else if (o instanceof DatagramSocket) {"
"		 // UDP - not implemented"
"	}" .

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

FUN Socket_close({Socket} SO|bject_socket), NATIVE:
	INT handle|_to_close = SO.Handle
	NATIVE @
"	Object o = NATIVE_Socket.All_sockets.get(X_handle);"
"	if (o instanceof Socket) {"
"		((Socket)o).close();"
"	}"
"	else if (o instanceof DatagramSocket) {"
"		((DatagramSocket)o).close();"
"	}" 
"	else if (o instanceof ServerSocket) {"
"		((ServerSocket)o).close();"
"	}" 
"	NATIVE_Socket.All_sockets.set(X_handle, null);" .

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

//FUN Get_host_name ==> STR, NATIVE : @
//"	RESULT = InetAddress.getLocalHost().getHostName();" .

FUN Get_host_ip ==> STR, NATIVE : @
"Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();"
"    for (NetworkInterface netint : Collections.list(nets)) {"
"        Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();"
"        for (InetAddress inetAddress : Collections.list(inetAddresses)) {"
"            if (inetAddress instanceof Inet4Address "
"				&& !inetAddress.isLoopbackAddress()) {"
"                return ((Inet4Address)inetAddress).getHostAddress();"
"            }"
"        }"
"    }"
"	RESULT = InetAddress.getLocalHost().getHostAddress();" .

END
*/
