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

#ifdef __GNUC__
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include <arpa/inet.h> // IPアドレスの確認用 inet_ntoa等
#define CLOSE_SOCKET close
#endif

#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")
#include<winsock.h>
#define CLOSE_SOCKET closesocket
#undef FD_SETSIZE
#define FD_SETSIZE 2048
#endif

#define STR_MAX 512

int main()
{
	int server_socket;
	struct sockaddr_in server_sock_in;
	int port = 5001;
	int read_size = 0;
	int i = 0;
	int res;
	char buf[STR_MAX];
	fd_set fds, readfds;

	#ifdef __GNUC__
	int sock_optval = 1;
	#endif

	#ifdef _WIN32
	const char sock_optval = 1;
	WSADATA	wsadata;
	if(WSAStartup(MAKEWORD(2,2),&wsadata) == SOCKET_ERROR){
		return 1;
	}
	#endif

	// 1. ソケット初期化
	if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1){
		perror("ERROR: Invalid socket");
		return 1;
	}

	if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,&sock_optval, sizeof(sock_optval)) == -1){
		perror("ERROR: setsockopt()");
		return 1;
	}

	// 2. BIND
	server_sock_in.sin_family = AF_INET;
	server_sock_in.sin_port = htons(port);
	server_sock_in.sin_addr.s_addr = htonl(INADDR_ANY);

	if(bind(server_socket, (struct sockaddr *)&server_sock_in, sizeof(server_sock_in)) < 0){
		perror("ERROR: bind()");
		return 1;
	}
	// 3.Listen (BSDではSOMAXCONNは128)
	if(listen(server_socket, SOMAXCONN) < 0){
		perror("ERROR: listen()");
		return 1;
	}

	printf("Listen %s:%d:%d\n", inet_ntoa(server_sock_in.sin_addr), ntohs(server_sock_in.sin_port), server_socket);

	// select によるlistenソケットの監視の準備

	#ifdef _WIN32
	fds.fd_count=0; // Windowsだと初期化していないオブジェクトの参照渡しでwarning発生するためその対策
	#endif

	FD_ZERO(&fds);
	FD_SET(server_socket, &fds);

	while(1){
		int client_socket;
		int addrsize;
		struct sockaddr_in client_sock_in;

		// fdsは上書きされるためreadfdsにコピー
		readfds = fds;

		// select処理
		switch(select(FD_SETSIZE, &readfds, NULL, NULL, NULL)){
			case	-1:
				perror("ERROR: select() = -1");
				return 1;
			case	0:
				break;
			default:
				for( client_socket=0 ; client_socket<FD_SETSIZE ; client_socket++ ){
					if( FD_ISSET(client_socket, &readfds) ){
						if( client_socket == server_socket ){
							addrsize = sizeof(client_sock_in);

							if((client_socket = accept(server_socket,(struct sockaddr *)&client_sock_in, &addrsize)) == -1){
								perror("ERROR: accept()");
								return 1;
							}
							else if( client_socket > FD_SETSIZE-1 ){
								perror("Error: client_socket > FD_SETSIZE-1");
								return 1;
							}

							printf("client connection %s:%d:%d\n", inet_ntoa(client_sock_in.sin_addr), ntohs(client_sock_in.sin_port), client_socket);

							if( client_socket != -1 ){
								FD_SET(client_socket, &fds);
							}
						}
						else{
							int client_socket_send;
							// 1行読み込む
							read_size = 0;i=0;
							for(read_size = 0, i=0; read_size < STR_MAX; read_size++, i++){

								#ifdef __GNUC__
								res = read(client_socket, buf+i, 1);
								#endif

								#ifdef _WIN32
								res = recv(client_socket, buf+i, 1, 0);
								#endif

								if(res == -1){
									perror("ERROR: read()");
									exit(1);
								}
								else if(res == 0){
									break;
								}
								// 終端コードがあれば抜け出す。(FLASHの場合は\0)
								if(buf[i] == '\n'){
									i++; read_size++; // FLASHの場合は不要
									break;
								}
							}
							// FLASHの場合は以下1行が不要
							buf[i] = '\0';
							if( read_size == 0 ){
								// 切断したソケットを監視対象から削除
								printf("client disconnection %s:%d:%d\n", inet_ntoa(client_sock_in.sin_addr), ntohs(client_sock_in.sin_port), client_socket);
								if(CLOSE_SOCKET(client_socket) == -1){
									perror("ERROR: CLOSE_SOCKET(client_socket)");
									return 1;
								}
								FD_CLR(client_socket, &fds);
								break;
							}
							printf("%4d: %s",client_socket, buf);
							// 全クライアントに送信。
							for( client_socket_send=0 ; client_socket_send < FD_SETSIZE ; client_socket_send++ ){
								if( FD_ISSET(client_socket_send, &fds) ){
									#ifdef __GNUC__
									write(client_socket_send, buf, strlen(buf));
									#endif

									#ifdef _WIN32
									send(client_socket_send, buf, strlen(buf), 0);
									#endif
								}
							}
						}
					}
				}
		}
	}
	if(CLOSE_SOCKET(server_socket) == -1){
		perror("ERROR: CLOSE_SOCKET(server_socket)");
		return 1;
	}
	#ifdef _WIN32
	WSACleanup();
	#endif
	return 0;
}
