#include <unistd.h>
#include <sys/ioctl.h>
#ifdef sun
#include <sys/filio.h>
#endif
#include <errno.h>
#include <unistd.h>

#include "toba.h"
#include "runtime.h"
#include "java_net_SocketImpl.h"
#include "java_net_PlainSocketImpl.h"
#include "java_net_InetAddress.h"
#include "java_io_FileDescriptor.h"

/* java/net/PlainSocketImpl socketCreate (Z)V */
Void socketCreate_z_UMixT(Object Harg1, Boolean arg2) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;
    struct in_java_io_FileDescriptor *fd;
    int unixfd, type, reuse;

    fd = this->fd;
    if(!fd)
        throwNullPointerException("socketCreate");

    if(arg2 == JAVA_TRUE)
        type = SOCK_STREAM;
    else
        type = SOCK_DGRAM;

    unixfd = socket(AF_INET, type, 0);    
    if(unixfd == -1)
        throwIOException("socket", errno);

    reuse = 1;
    setsockopt(unixfd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse));

    fd->fd = java_fd(unixfd);
}

/* java/net/PlainSocketImpl socketConnect (Ljava/net/InetAddress;I)V */
Void socketConnect_Ii_bXUUz(Object Harg1, Object Harg2, Int port) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;
    struct in_java_net_InetAddress *addr = 
        (struct in_java_net_InetAddress *)Harg2;
    struct in_java_io_FileDescriptor *fd;
    struct sockaddr_in saddr;
    int res, adlen;

    fd = this->fd;
    if(!fd)
        throwNullPointerException("socketConnect");

    set_address(addr, port, &saddr);
    res = connect(unix_fd(fd->fd), (struct sockaddr *)&saddr, sizeof(saddr));
    if(res == -1)
        throwIOException("connect", errno);

    /* fill in the address of the connection */
    this->address = addr;
    this->port = port;

    /* we have to ask for the local port, the system may have bound us */
    adlen = sizeof(saddr);
    res = getsockname(unix_fd(fd->fd), (struct sockaddr *)&saddr, &adlen);
    if(res == -1)
        throwIOException("getsockname", errno);
    this->localport = ntohs(saddr.sin_port);
}

/* java/net/PlainSocketImpl socketBind (Ljava/net/InetAddress;I)V */
Void socketBind_Ii_AuqtA(Object Harg1, Object Harg2, Int port) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;
    struct in_java_net_InetAddress *addr = 
        (struct in_java_net_InetAddress *)Harg2;
    struct in_java_io_FileDescriptor *fd;
    struct sockaddr_in saddr;
    int res;

    fd = this->fd;
    if(!fd)
        throwNullPointerException("socketBind");
    
    set_address(addr, port, &saddr);
    res = bind(unix_fd(fd->fd), (struct sockaddr *)&saddr, sizeof(saddr));
    if(res == -1)
        throwIOException("bind", errno);
    this->localport = port;
}

/* java/net/PlainSocketImpl socketListen (I)V */
Void socketListen_i_6q4JG(Object Harg1, Int count) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;
    struct in_java_io_FileDescriptor *fd;
    int res;

    fd = this->fd;
    if(!fd)
        throwNullPointerException("socketListen");
    
    res = listen(unix_fd(fd->fd), count);
    if(res == -1)
        throwIOException("listen", errno);
}

/* java/net/PlainSocketImpl socketAccept (Ljava/net/SocketImpl;)V */
Void socketAccept_S_BD9k3(Object Harg1, Object Harg2) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;
    struct in_java_net_SocketImpl *s = 
        (struct in_java_net_SocketImpl *)Harg2;
    struct in_java_io_FileDescriptor *fd, *retfd;
    struct in_java_net_InetAddress *retaddr;
    struct sockaddr_in saddr;
    int adlen, res;

    fd = this->fd;
    if(!fd || !s)
        throwNullPointerException("socketAccept");
    
    adlen = sizeof(saddr);
    do {
        res = accept(unix_fd(fd->fd), (struct sockaddr *)&saddr, &adlen);
        if(res == -1 && errno != EINTR)
            throwIOException("accept", errno);
    } while(res == -1);

    /* wrap up all the return values into s */
    retfd = (struct in_java_io_FileDescriptor *)
              construct(&cl_java_io_FileDescriptor.C);
    retfd->fd = java_fd(res);
    retaddr = (struct in_java_net_InetAddress *)
                construct(&cl_java_net_InetAddress.C);
    retaddr->hostName = 0;
    retaddr->address = ntohl(saddr.sin_addr.s_addr);
    retaddr->family = AF_INET;

    s->fd = retfd;
    s->address = retaddr;
    s->port = ntohs(saddr.sin_port);
    
    adlen = sizeof(saddr);
    res = getsockname(unix_fd(retfd->fd), (struct sockaddr *)&saddr, &adlen);
    if(res == -1)
        throwIOException("accept", errno);
    s->localport = ntohs(saddr.sin_port); 
}

/* java/net/PlainSocketImpl socketAvailable ()I */
Int socketAvailable__2cTi2(Object Harg1) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;
    struct in_java_io_FileDescriptor *fd;
    int val;

    fd = this->fd;
    if(!fd)
        throwNullPointerException("socketAvailable");

    if(ioctl(unix_fd(fd->fd), FIONREAD, &val) == -1)
        throwIOException("fionread", errno);

    return val;
}

/* java/net/PlainSocketImpl socketClose ()V */
Void socketClose__gGYaV(Object Harg1) 
{
    struct in_java_net_PlainSocketImpl *this = 
        (struct in_java_net_PlainSocketImpl *)Harg1;

    fd_close(this->fd);
}

