/* $Id: main.c 258 2010-12-14 04:35:42Z qin $ */
/* $ID: main.c,v 1.64 2008/03/06 01:18:51 miyazawa Exp $ */

/*
* Copyright (C) 2004 WIDE Project.
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <netinet/in.h>
#include "tcp_sock.h"


int  server_fd=-1;                            // server_fd

static struct  fd_list fd_list_tbl[MAX_SOCKET];


int sock_initialize(void);
static void mainloop(void);

void  sock_fdset( fd_set* p_fdset, int *nfds );
void apl_handler( fd_set* p_fdset );

int
main(int argc, char **argv)
{
	// 
	
	sock_initialize();

	mainloop();
	
	/*NOTREACHED*/
	return 0;
}

static void
mainloop(void)
{

	int num_fds;
	int nfds = -1;
	fd_set fdset;
	
	
	for (;; ){
		
		FD_ZERO(&fdset);
		
		sock_fdset(&fdset, &nfds);
		
		num_fds = select(nfds, &fdset, NULL, NULL, NULL);
		if (num_fds == -1){
			GDB_PRINTF("select: %s\n", strerror(errno));
			continue;
		}
		
		apl_handler(&fdset);
		
	}
}


/*******************************************************************************
* 1.NAME         : sock_initialize()
* 2.DESCRIPTION  :
* 3.PARAMETER    : 無し
* 4.RETURN VALUE : -1       ソケット処理初期化失敗
*                : 0        ソケット処理初期化成功
* 5.NOTE         :
*               ソケットの接続為初期化
*******************************************************************************/
int sock_initialize(void)
{
	int ret;
	int on;
	struct  sockaddr_in apl_addr;
	
	// 初期化
	memset(&apl_addr, 0, sizeof(apl_addr));
	memset(fd_list_tbl, (-1), sizeof(fd_list_tbl));
	
	/***************************************/
	/*   ソケット処理部分                  */
	/***************************************/
	
	// アドレスとポート番号
	apl_addr.sin_family = AF_INET;
	apl_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	apl_addr.sin_port = htons(SERVER_SOCK_PORT);
	
	// ソケットを作成
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (server_fd < 0)
	{
		GDB_PRINTF("can not create socket(errno=%d).\n", errno);
		return(-1);
	}
	
	// アドレスの即時再利用
	on = 1;
	ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
		(char *)&on, sizeof(on));
	if (ret<0)
	{
		GDB_PRINTF("setsockopt(SO_REUSEADDR) failed(errno=%d).\n", errno);
		close(server_fd);
		server_fd = -1;
		return(-1);
	}
	
	// ソケットにアドレスを付ける
	if (bind(server_fd, (struct sockaddr*)&apl_addr, sizeof(apl_addr))<0)
	{
		GDB_PRINTF("bind failed(errno=%d).\n", errno);
		close(server_fd);
		server_fd = -1;
		return(-1);
	}
	
	// 接続を待機
	if (listen(server_fd, MAX_SOCKET)< 0)
	{
		GDB_PRINTF("listen failed(errno=%d).\n", errno);
		close(server_fd);
		server_fd = -1;
		return(-1);
	}
	
	return 0;
}
/*******************************************************************************
* 1.NAME         : iked_apl_sock_fdset()
* 2.DESCRIPTION  :
* 3.PARAMETER    : (IN/OUT)  fdset   : 受信を監視ファイルディスクリプタ集合
                 : (IN/OUT)  nfds    : ファイルディスクリプタの最大値+1
* 4.RETURN VALUE : 無し
* 5.NOTE         :
*           監視するfdをファイルディスクリプタ集合に設定
*******************************************************************************/
void  sock_fdset( fd_set* p_fdset, int *nfds )
{
    int i;

    // 接続を監視するfdを監視ファイルディスクリプタ集合に追加
    if ( server_fd >= 0 )
    {
        if ( server_fd >= *nfds )
            *nfds = server_fd + 1;
        FD_SET( server_fd, p_fdset );
    }

    // メッセージを監視するするfdを監視ファイルディスクリプタ集合に追加
    for ( i = 0; i < MAX_SOCKET; i++ )
    {
        if ( fd_list_tbl[i].fd >= 0 )
        {
            if ( fd_list_tbl[i].fd >= *nfds )
                *nfds = fd_list_tbl[i].fd + 1;
            FD_SET( fd_list_tbl[i].fd, p_fdset );
        }
    }

    return;
}

/*******************************************************************************
* 1.NAME         : iked_apl_handler()
* 2.DESCRIPTION  :
* 3.PARAMETER    : (IN)  fdset  : socket fd
* 4.RETURN VALUE :  無し
* 5.NOTE         :
*
*******************************************************************************/
void apl_handler( fd_set* p_fdset )
{
    struct  sockaddr_in cli_addr;             // 相手のアドレス
    int     cli_addr_len;
    int     con_so;
    int     ret;
    char    *message;
    int     i;
    int     sock_sbuff_size;                // ソケット送信領域サイズ
    int     sock_rbuff_size;                // ソケット受信領域サイズ

    // クライアントから接続された時
    if ( (server_fd >= 0) && (FD_ISSET(server_fd, p_fdset)) )
    {
        cli_addr_len = sizeof( cli_addr );
        con_so = accept( server_fd,
                              ( struct sockaddr *) &cli_addr,
                              ( socklen_t *)&cli_addr_len );

        if ( con_so < 0 )
        {
            GDB_PRINTF( "accept failed(errno=%d).\n",errno);
            return ;
        }
        // FDを管理テーブルに登録
        for ( i = 0; i < MAX_SOCKET; i++ )
        {
            if ( fd_list_tbl[i].fd < 0 )
            {
                GDB_PRINTF( "connection accepted for fd_list[%d]= (%d)\n",
                    i, con_so);
                fd_list_tbl[i].fd = con_so;

                // ソケット受信領域サイズ
                sock_rbuff_size = SO_RCVBUF_SIZE;
                ret = setsockopt(con_so, SOL_SOCKET, SO_RCVBUF,
                          (const char*)&sock_rbuff_size, sizeof(int));
                 if ( ret < 0 )
                {
                    GDB_PRINTF( "setsockopt(SO_RCVBUF) failed(errno=%d).\n",errno );
                }
                 // ソケット送信領域サイズ
                sock_sbuff_size = SO_SNDBUF_SIZE;
                ret = setsockopt(con_so, SOL_SOCKET, SO_SNDBUF,
                           (const char*)&sock_sbuff_size, sizeof(int));
                 if ( ret < 0 )
                {
                    GDB_PRINTF(  "setsockopt(SO_SNDBUF) failed(errno=%d).\n",errno );
                }

                break;
            }
        }

        // 管理テーブルがもう全部使用された時
        if ( i == MAX_SOCKET )
        {
                // ソケットを切断
                close( con_so );
                GDB_PRINTF( "no available fd_list_tbl to new connection.\n");
        }
    }
    else
    {
        // メッセージが届いた時
        for ( i = 0; i < MAX_SOCKET; i++ )
        {
            if ( (fd_list_tbl[i].fd >= 0) &&
                (FD_ISSET( fd_list_tbl[i].fd, p_fdset)) ) {
                // メッセージ内容を受信
                ret = recv_mesg( fd_list_tbl[i].fd, &message );

                if ( ret > 0 )
                {
                    // メッセージ内容の受信OK
 
			GDB_PRINTF( "(fd=%d, message=%s ).\n", fd_list_tbl[i].fd, message );
			//send_mesg( fd_list_tbl[i].fd, "test  fuciton\n" );
                    // メモリ領域を解放
                    free( message );
                }
                else
                {
                    // メッセージ内容の受信失敗
                    close( fd_list_tbl[i].fd );
                    fd_list_tbl[i].fd = -1;
                    fd_list_tbl[i].module = -1;
                }
            }
        }
    }

    return ;
}
