//
// 
//	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
// 
//	This program is free software; you can redistribute it 
//	and/or modify it under the terms of the GLOBALBASE 
//	Library General Public License (G-LGPL) as published by 
//
//	http://www.globalbase.org/
// 
//	This program is distributed in the hope that it will be 
//	useful, but WITHOUT ANY WARRANTY; without even the 
//	implied warranty of MERCHANTABILITY or FITNESS FOR A 
//	PARTICULAR PURPOSE.
//
//

extern "C" {

#define STREAM_LIB
#define LIBRARY
#define S_SOCKET_LIBRARY

#include	<stdlib.h>
#include	<errno.h>

#include	"machine/include.h"
#include	"memory_debug.h"
#include	"stream.h"
#include	"net_msg.h"
#include	"CServerMaster.h"
#include	"save_global.h"
#include	"log.h"

extern SEM stream_lock;

void insert_s_table(S_TABLE * tbl);
void _s_close_sync(STREAM * s);

int s_close_socket(STREAM *);
int s_write_socket(STREAM *,void *,int);
int s_read_socket(STREAM *,void *,int);
int s_flush_socket(STREAM *);
STREAM * s_open_file_descripter(int, S_TABLE *);
int s_get_socketip_socket(STREAM * s);

int s_error();
STREAM * s_error_stream();

S_TABLE s_socket_table = {
	's',
	STF_BUF_OUT,
	{0,0},
	0,
	(STREAM *(*)())s_open_file_descripter,
	(int(*)())s_close_socket,
	(int(*)())s_write_socket,
	(int(*)())s_read_socket,
	(int(*)())s_flush_socket,
	s_error,
	s_error_stream,
	(int(*)())s_get_socketip_socket,
};

void
init_socket_stream()
{
	insert_s_table(&s_socket_table);
}

#if !__MACH__
int
inet_addr(char * addr)
{
int a, b, c, d;
	sscanf(addr, "%d.%d.%d.%d", &a,&b,&c,&d);
	return (((a*0xff)+b)*0xff+c)*0xff+d;
}
#endif

int
s_close_socket(STREAM * s)
{
	_s_close_sync(s);

	if ( s->socket.state == S_STATE_OK ) {
		_ExStop();
		try {
				s->socket.ep->SendDisconnect();
		} catch(LException exc) {
			SInt32 err = exc.GetErrorCode();
			if ( err == kOTOutStateErr ) {
				NET_MSG("# Already Disconnected.[%x]\n", (int)s->socket.ep);
			} else if ( IsDisconnectError(err) ) {
				try {
					s->socket.ep->AcceptRemoteDisconnect();
				} catch(...) {}
				NET_MSG("# Disconnected by the remote.[%x]\n", (int)s->socket.ep);
			} else {
				fprintf(stderr, "# Error in closing connection.(ID : %d)[%x]\n",
					err, (int)s->socket.ep);
			}
		} catch(...) {}
		_ExRevert();
	}
	_ExStop();
	try {
//		s->socket.ep->Unbind();
	} catch (...) {}
	_ExRevert();
	delete s->socket.ep;
	NET_MSG("# Connection Closed.[%x]\n", (int)s->socket.ep);
	s->socket.state = S_STATE_DELETED;
	s->h.tbl = 0;
	return 0;
}

int
s_write_socket(STREAM * s,void * data,int len)
{
	int ret = len;
	if ( s->socket.state != S_STATE_OK ) {
		errno = EPIPE;
		return -1;
	}
	_ExStop();
	try {
		if ( int state = s->socket.ep->GetState() != endpoint_Connected ) {
			Throw_(state);
		}
		s->socket.ep->Send(data,(unsigned long)len);
	} catch(LException exc) {
		SInt32 err = exc.GetErrorCode();
		if ( IsDisconnectError(err) ) {
			try {
				s->socket.ep->AcceptRemoteDisconnect();
			} catch(...) {}
			s->socket.state = S_STATE_CLOSED;
			NET_MSG("# Disconnected by the remote.\n",0);
		} else if ( err == Timeout_Error || err == Abort_Error ) {
			fprintf(stderr,"# s_write_socket Aborted.[%x]\n", (int)s->socket.ep);
		} else {
			fprintf(stderr, "# Error in sending data.(ID : %d)[%x]\n",
				err, (int)s->socket.ep);
		}
		errno = EPIPE;
		ret = -1;
	} catch(...) {
		fprintf(stderr, "# Error in sending data.[%x]\n", (int)s->socket.ep);
		errno = EPIPE;
		ret = -1;
	}
	_ExRevert();
	return ret;
}

int
s_read_socket(STREAM * s,void * data,int len)
{
	int ret;
	
	if ( s->socket.state != S_STATE_OK ) {
		*(char *)data = 0;
		errno = EPIPE;
		return -1;
	}
	_ExStop();
	try {
		if ( int state = s->socket.ep->GetState() != endpoint_Connected ) {
			Throw_(state);
		}
		ret = len;
		s->socket.ep->Receive(data,(UInt32&)ret);
	} catch(LException exc) {
		SInt32 err = exc.GetErrorCode();
		if ( IsDisconnectError(err) ) {
			try {
				s->socket.ep->AcceptRemoteDisconnect();
			} catch(...) {}
			s->socket.state = S_STATE_CLOSED;
			NET_MSG("# Disconnected by the remote.\n",0);
		} else if ( err == Timeout_Error || err == Abort_Error ) {
			log_printf(LOG_ERROR,LOG_LAYER_STREAM,0,"# s_read_socket Aborted.[%x]\n", (int)s->socket.ep);
		} else {
			log_printf(LOG_ERROR,LOG_LAYER_STREAM,0,"# Error in receiving data.(ID : %d)[%x]\n",
				err, (int)s->socket.ep);
		}
		errno = EPIPE;
		ret = -1;
		*(char*)data = 0;
	} catch(...) {
		fprintf(stderr, "# Error in receiving data.[%x]\n", (int)s->socket.ep);
		errno = EPIPE;
		ret = -1;
		*(char*)data = 0;
	}
	_ExRevert();
//if ( ret > 0 ) insertLog(s,data,ret);
	return ret;
}

int
s_flush_socket(STREAM * s)
{
	return 0;
}

int
s_get_socketip_socket(STREAM * s)
{
	int ret;
	_ExStop();
	try {
		ret = s->socket.ep->GetRemoteHostAddress()->GetIPAddress();
	} catch(...) {
		ret = 0;
	}
	_ExRevert();
	return ret;
}

} // extern "C"