/**********************************************************************
 
	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.

**********************************************************************/

#include <winsock2.h>
#include	<stdlib.h>
#include	<stdarg.h>
#include	<stdio.h>
#include	<io.h>

#include	"memory_debug.h"
#include	"stream.h"
#include	"task.h"
#include	"utils.h"

/*
#include	"logger.h"
LoggerHandle sock_log;
*/

extern SEM stream_lock;
#ifndef _SOCKLEN_T
typedef size_t socklen_t;
#endif

int s_close_socket();
int s_write_socket();
int s_read_socket();
int s_vprintf_socket();
int s_vprintf_cr_socket();
int s_flush_socket();
int s_vscanf_socket();
int s_proc_send_socket();
STREAM * s_proc_recv_socket();

int s_get_socketip_socket(STREAM * s);

STREAM *_s_open_socket_descripter();


S_TABLE s_socket_table = {
	's',
	0,
	{0,0},
	0,
	/* called by gbpmd */
	_s_open_socket_descripter,
	s_close_socket,
	s_write_socket,
	s_read_socket,
	s_flush_socket,
	s_proc_send_socket,
	s_proc_recv_socket,
	s_get_socketip_socket
};

STREAM *_s_open_socket_descripter(SOCKET desc, S_TABLE *tbl){
	er_panic("s_open_socket_descripter");
	return NULL;
}


/*
void shutdown_socket(STREAM * s){
	shutdown(s->socket.sock, SD_BOTH);
}
*/

int
s_close_socket(STREAM * s)
{
	if(s->socket.shutdown_before_close){
		/*
			logger_write(sock_log, "shutdown socket %d", s->socket.sock); 
		*/
		shutdown(s->socket.sock, 2);
	}
	
	_s_close_sync(s);
	/*
	logger_write(sock_log, "close socket %d", s->socket.sock); 
	*/
	closesocket(s->socket.sock);
	s->socket.sock = INVALID_SOCKET;
	return 0;
}

int
s_write_socket(STREAM * s, void * data, int len)
{
	int nResult;
	
	long timeout = 12;
	do{
		FD_SET fd = {{1}, {s->socket.sock}};
		TIMEVAL tv = {timeout, 0};
		nResult = select(0, NULL, &fd, NULL, &tv);
	}while(nResult == 0);
	
	if(nResult == SOCKET_ERROR){
		return -1;
	}
	nResult = send(s->socket.sock, data, len, 0);
/*
	{
		char *log_buf;
		if(nResult>=0){
			log_buf=(char*)malloc(nResult+1);
			memcpy(log_buf, data, nResult);
			log_buf[nResult]='\0';

			logger_write(sock_log, "write socket %d \t%s", s->socket.sock, log_buf);
			free(log_buf);
		}
		else{
			logger_write(sock_log, "socket write error %d %d", s->socket.sock, WSAGetLastError());
		}
	}
*/
	return nResult;
}

int
s_read_socket(STREAM * s, void * data, int len)
{

	int nResult;
	long timeout = 12;
	do{
		FD_SET fd = {{1}, {s->socket.sock}};
		TIMEVAL tv = {timeout, 0};
		nResult = select(0, &fd, NULL, NULL, &tv);
	}while(nResult==0);
	if(nResult == SOCKET_ERROR) {
		return -1;
	}
	
	nResult = recv(s->socket.sock, data, len, 0);
/*
	{
		char *log_buf;
		if(nResult>=0){
			log_buf=(char*)malloc(nResult+1);
			memcpy(log_buf, data, nResult);

			log_buf[nResult]='\0';
			logger_write(sock_log, "recv socket %d \t%s", s->socket.sock, log_buf);
			free(log_buf);
		}
		else{
			logger_write(sock_log, "socket read error %d %d", s->socket.sock, WSAGetLastError());
		}
	}
	*/
	return nResult;
}

int
s_flush_socket(STREAM * s)
{

	return 0;
}

int
s_get_socketip_socket(STREAM * s)
{
int ret;
int er;
socklen_t size;
struct sockaddr_in ss;
	size = sizeof(ss);
	er = getpeername(s->socket.sock,(struct sockaddr*)&ss,&size);
	if ( er < 0 )
		return 0;
	ret = ntohl(ss.sin_addr.s_addr);
	return ret;
}



int
s_proc_send_socket(STREAM * s, PROC_SEND_CHUNK** chunk, HANDLE hTargetProcess)
{
	int flags;
	
	PROC_SEND_SOCKET *ps = (PROC_SEND_SOCKET *)d_alloc(sizeof(PROC_SEND_SOCKET));

	ps->header.size = sizeof(PROC_SEND_SOCKET);
	ps->header.type = s->h.tbl->type;
	ps->open_mode = s->h.mode;
	
	flags = DUPLICATE_SAME_ACCESS;
	
	if(!DuplicateHandle(
		GetCurrentProcess(), 
		(HANDLE)(s->socket.sock),
		hTargetProcess,
		(HANDLE*)&(ps->sock),
		0,
		FALSE,
		flags)){
		er_panic("s_proc_send_socket DuplicateHandle error");
	}
	s->socket.shutdown_before_close = 0;
	*chunk = (PROC_SEND_CHUNK*)ps;
	
	return 0;
}


STREAM *
s_proc_recv_socket(S_TABLE *table, PROC_SEND_CHUNK* chunk)
{
	STREAM *ret;
	PROC_SEND_SOCKET *ps;
	
	ps = (PROC_SEND_SOCKET *)chunk;
	
	ret = d_alloc(sizeof(S_SOCKET));
	ret->h.tbl = table;
	ret->h.thread = 0;
	ret->socket.sock = ps->sock;
	ret->socket.shutdown_before_close = 1;

	_s_open(ret,ps->open_mode);

	return ret;
}

void
init_socket_stream()
{
/*
	sock_log = logger_open("s_socket.log", "s_socket", 
		LOGGER_FORMAT_TICK|
		LOGGER_FORMAT_SEQUENCE|
		LOGGER_FORMAT_THREADID|
		LOGGER_FORMAT_PROCESSID|
		LOGGER_FORMAT_APPNAME|
		LOGGER_FORMAT_WITHLF);
	logger_write_caption(sock_log);
*/	insert_s_table(&s_socket_table);
}
