#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

#include <exec/types.h>
#include <exec/ports.h>

#include <stdio.h>
#include <errno.h>
#include <functions.h>

extern int errno;
char *malloc();

#define MAXSOCKET 20

static struct socket_entry {
    int in_use;
    char *to_client;
    char *from_client;
    } sockets[MAXSOCKET];

/* this structs must be used by telnet too */
struct connect_message {
    struct Message Msg;
    char *port_name;
};

struct data_message {
    struct Message Msg;
    char *buffer;
    int length;
};


extern char *mktemp(char *);

/* For more info about this functions, consult an UNIX-manual */
int sowrite(int, char *, int);
int soread(int, char *, int);
int accept(int, struct sockaddr *, int *);
int socket( int, int, int);
int find_free_socket(void);
void soclose(int);
int setsockopt(int, int, int, char *, int);
int bind(int, struct sockaddr *, int);

int SafePutToPort(struct Message *, char *);
struct Message *SafeReadFromPort(char *);


/* functions which handle fd_sets are macro's, defined in sys/types.h
#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define	FD_ZERO(p)	bzero((char *)(p), sizeof (*(p)))
*/

void add_signal_to_fdset(n, p)
    int n;
    fd_set *p;
{
    register char *portname;
    register struct MsgPort *port;

    if(!sockets[n].in_use)
	return;
    if(!(portname = sockets[n].from_client))
	return;
    if(!(port = FindPort(portname)))
	return;
    *p = *p | 1 << port->mp_SigBit;
    return;
}


void delete_signal_from_fdset(n, p)
    int n;
    fd_set *p;
{
    char *portname;
    struct MsgPort *port;
    ULONG signal;

    fprintf(stderr, "FD_CLR\n");
    if(!sockets[n].in_use) {
	fprintf(stderr, "signal from unused socket %d taken\n", n);
	return;
	}
    if(!(portname = sockets[n].from_client)) {
	fprintf(stderr, "socket %d has no from_client port !\n",n);
	return;
	}
    fprintf(stderr, "%s\n", portname);
    if(!(port = FindPort(portname))) {
	fprintf(stderr, "Could not find port %s\n", portname);
	return;
	}
    signal = 1 << port->mp_SigBit;
    fprintf(stderr, "signal(hex): %x\n", signal);
    fprintf(stderr, "fdset (hex): %x\n", *p);

    *p = (*p) & (~signal);
    fprintf(stderr, "fdset (hex): %x\n", *p);
    return;
}

int is_member_of_fdset(n, p)
    int n;
    register fd_set *p;
{
    register char *portname;
    register struct MsgPort *port;
    register ULONG signal;
    register int result;

    if(!sockets[n].in_use) {
	fprintf(stderr, "signal from unused socket %d taken\n", n);
	return 0;
	}
    if(!(portname = sockets[n].from_client)) {
	fprintf(stderr, "socket %d has no from_client port !\n",n);
	return 0;
	}
    if(!(port = FindPort(portname))) {
	fprintf(stderr, "Could not find port %s\n", portname);
	return 0;
	}
    signal = 1 << port->mp_SigBit;
    result = (*p) & signal;

    return (result!=0);
}


shutdown(socket, number)
    register int socket;
    register int number;
{
    char tmp[4];

    tmp[0] = -1;
    tmp[1] = -3;
    tmp[2] =  1;
    tmp[3] =  '\0';
    sowrite(socket, tmp, 3);
}


/* sowrite will write to the clientport
 * the client must read from the clientport, and write to the serverport
 * When we write a message to the other side, we wait for the reply
 */
int sowrite(socket, buffer, length)
    int socket, length;
    char *buffer;
{
    register struct data_message *msg;
    register struct MsgPort *waitport;
    register ULONG signal;
    register int i;

    waitport = CreatePort(NULL, 0);
    if(!waitport) {
	fprintf(stderr, "Could not allocate waitport\n");
	errno = EIO;
	return -1;
	}
    if(!(msg = (struct data_message *) malloc(sizeof (struct data_message)) )) {
	fprintf(stderr, "Could not allocate message\n");
	errno = EIO;
	return -1;
	}
    msg->Msg.mn_Node.ln_Type = NT_MESSAGE;
    msg->Msg.mn_Length = sizeof(struct data_message);
    msg->Msg.mn_ReplyPort = waitport;

    msg->buffer = buffer;
    msg->length = length;

    if(!SafePutToPort( (struct Message*)msg, sockets[socket].to_client)) {
	fprintf(stderr,"Could not find client-port\n");
	errno = EIO;
	length = -1;
	}
    else
	{
	signal = Wait( SIGBREAKF_CTRL_D | 1L << waitport->mp_SigBit);
	if(signal & SIGBREAKF_CTRL_D ) {
	    errno = EIO;
	    fprintf(stderr, "sowrite : aborted waiting for reply\n");
	    DeletePort(waitport);
	    return -1;
	    }
	errno = 0;		/* NO ERROR */
	}
    free((char*) msg);
    DeletePort(waitport);
    return length;
}


int soread(socket, buffer, length)
    int socket, length;
    char *buffer;
{
    register struct data_message *msg = NULL;
    register struct MsgPort *port = NULL;
    register int bufpos = 0;	/* first free position in the buffer */
    register int i,x;

    /* If we get here, there must be a message
     */

     if(!(port = FindPort(sockets[socket].from_client))) {
	fprintf(stderr,"Could not find the port\n");
	errno = EHOSTUNREACH;
	return -1;
	}

    /* Yes, read the port !! */
    while( msg = (struct data_message*) SafeReadFromPort(sockets[socket].from_client))
	{ /* concatenate the received message to the buffer */
	for(i=0;i<msg->length;i++)
	    buffer[bufpos+i] = msg->buffer[i];

	ReplyMsg((struct Message *)msg);   /* tell telnet we received it */
	bufpos = bufpos+i;
	}
    if(bufpos == 0) {
	fprintf(stderr, "soread : could not read any message !!\n");
	errno = EMSGSIZE;
	return -1;
	}
    return bufpos;
}

int accept(s, addr, addrlen)
    int s;
    struct sockaddr *addr;
    int *addrlen;
{
    register struct MsgPort *port;
    register struct connect_message *msg;
    register int new_socket;
    register char *to_client;
    register ULONG signal;

    /* copy socket s to a new socket, and return the descriptor for the
     * new socket-descriptor and fill in the addr en addrlen fields
     */
    if(!sockets[s].in_use) {
	fprintf(stderr, "socket %d is not in use !!!\n", s);
        errno = EINTR;
        return -1;
        }

    if(!(port = FindPort(sockets[s].from_client))) {
	fprintf(stderr, "Could not find port\n");
	return -1;
	}

    /* Allow 1 client, let the rest wait */
    msg = (struct connect_message *) SafeReadFromPort(sockets[s].from_client);
    if(!msg) {
        errno = EWOULDBLOCK;
        return -1;
	}
    new_socket = socket(0,0,0);
    if(new_socket == -1) {
	msg->port_name = NULL;
        ReplyMsg((struct Message *)msg);
        return -1;
        }

/* store the port to which we must send data */
    to_client = malloc(strlen(msg->port_name)+1);
    strcpy(to_client, msg->port_name);
    sockets[new_socket].to_client = to_client;

/* return the port to which we will listen */
    msg->port_name = sockets[new_socket].from_client;

    ReplyMsg((struct Message *) msg);
    return new_socket;
}


int socket( domain, type, protocol)
    int domain, type, protocol;
{
    register struct MsgPort *port;
    register int new_sd;
    register char *name, *tmpname;

/* this code does not need Forbid()/Permit(), when you want to run 2 mud's,
   you should add it :+)
 */
    tmpname = mktemp("parserXXXXXXXX");
    name = malloc(strlen(tmpname) + 1);
    strcpy(name, tmpname);

    if(!name) {
	fprintf(stderr, "Could not create new port name\n");
	return -1;
	}
    new_sd = find_free_socket();
    if(new_sd == -1) {
        errno = EMFILE;
        return -1;
        }
    if((port = (struct MsgPort *) CreatePort(name, 0)) == NULL) {
        errno = ENOBUFS;
        return -1;
        }
    sockets[new_sd].from_client = name;	/* we will listen to this name */
    sockets[new_sd].to_client   = NULL;	/* will be filled in by accept() */
    sockets[new_sd].in_use      = 1;

    return new_sd;
}


int find_free_socket()
{
    register int entry = MAXSOCKET-1;
    while(entry >= 0) {
        if(sockets[entry].in_use == 0)
             return entry;
        entry--;
	}
    return -1;
}


void soclose(socket)
    int socket;
{
    register struct MsgPort *port;

    /* I should check that this was the last reference to the socket */
    if(socket < 0 || socket >= MAXSOCKET) {
	fprintf(stderr, "failed to close socket %d\n", socket);
	return;
	}
    if(!sockets[socket].in_use) {
	fprintf(stderr, "failed to close socket %d\n", socket);
	return;
	}
    port = FindPort(sockets[socket].from_client);
    if(!port) {
	fprintf(stderr,"tried to close non-existing port\n");
	return;
	}
    DeletePort(port);

    if(sockets[socket].from_client)
	free(sockets[socket].from_client);
    if(sockets[socket].to_client)
	free(sockets[socket].to_client);

    sockets[socket].from_client = NULL;
    sockets[socket].to_client = NULL;
    sockets[socket].in_use = 0;
}


int setsockopt(s, level, optname, optval, optlen)
    int s, level, optname;
    char *optval;
    int optlen;
{
    return 0;
}


int bind(s, name, namelen)
    int s;
    struct sockaddr *name;
    int namelen;
{
    register struct MsgPort *old_port, *new_port;
    register char *port_name;

    /* according to the manual, socket s should have no name yet, but
     * it should get the name specified in name. So lets remove the old
     * name, and replace it with a new one. The portname is specified as
     * an unsigned int, but let's pretend it is a 32 bits number. We need
     * 10 + 1 digits in the string, let's make that 20 and hope it's enough
     */
    port_name = malloc(20);
    sprintf(port_name, "%u", ((struct sockaddr_in *)name)->sin_port);

    if(!sockets[s].in_use) {/* trying to bind a socket that's not in use */
	fprintf(stderr, "Can't bind a free socket !\n");
	return -1;
	}
    old_port = FindPort(sockets[s].from_client);
    if(!old_port) {
	fprintf(stderr, "Non-existing port\n");
	return -1;
	}

    if(FindPort((char*)port_name) != NULL) { /* Each name must be unique */
	fprintf(stderr, "Name must be unique\n");
        errno = EADDRINUSE;
        return -1;
        }

/* Delete the old port, and create a new port with a name, no
   checking for messages, cos I did not send any
 */
    new_port = CreatePort(port_name,0);
    if(!new_port) {
	fprintf(stderr, "failed to create new socket\n");
	return -1;
	}
    /* delete the old port */
    soclose(s);

    sockets[s].from_client = port_name;
    sockets[s].to_client = NULL;	/* no replyport !! */
    sockets[s].in_use = 1;

    return 0;
}


int listen(s, backlog)
    int s, backlog;
{
    return 0;
}


int SafePutToPort(message, portname)
    struct Message *message;
    char * portname;
{
    register struct MsgPort *port;

    Forbid();
    port = FindPort(portname);
    if(port)
	PutMsg(port, message);
    Permit();
    if(port) {
	errno = 0;
	return 1;
	}
    else
	{
	errno = EIO;
	return 0;
	}
}


struct Message *SafeReadFromPort(portname)
    char * portname;
{
    register struct MsgPort *port;
    register struct Message *msg;

    Forbid();
    port = FindPort(portname);
    if(port)
	msg = GetMsg(port);
    Permit();
    if(port)
	{
	errno = 0;
	return msg;
	}
    else
	{
	errno = EIO;
	return NULL;
	}
}
