#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>
#include <process.h>
#include <string.h>
// ํจ์ ๋ฐ ๋ณ์์ ์ ์ธ
int server_init();
int server_close();
unsigned int WINAPI do_chat_service(void* param);
unsigned int WINAPI recv_and_forward(void* param);
int add_client(int index);
int read_client(int index);
void remove_client(int index);
int notify_client(char* message);
char* get_client_ip(int index);
// ํด๋ผ์ด์ธํธ ์์ผ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๊ตฌ์กฐ์ฒด
typedef struct sock_info
{
    SOCKET s;
    HANDLE ev;
    char nick[50];
    char ipaddr[50];
} SOCK_INFO;
// ํฌํธ ๋ฒํธ์ ํด๋ผ์ด์ธํธ ์์ ๋ํ ์์ ์ ์
int port_number = 9999;
const int client_count = 10;
SOCK_INFO sock_array[client_count + 1]; // ํด๋ผ์ด์ธํธ ์ ๋ณด๋ฅผ ๋ด๋ ๋ฐฐ์ด
int total_socket_count = 0; // ํ์ฌ ์ฐ๊ฒฐ๋ ์์ผ์ ์
// ๋ฉ์ธ ํจ์
int main(int argc, char* argv[])
{
    unsigned int tid;
    char message[MAXBYTE] = "";
    HANDLE mainthread;
    printf("\n์ฌ์ฉ๋ฒ : mcodes_server [ํฌํธ๋ฒํธ]\n");
    printf("         ex) mcodes_server.exe 9999\n");
    printf("         ex) mcodes_server.exe \n\n");
    if (argv[1] != NULL)
        port_number = atoi(argv[1]);
    // ์๋ฒ ์ฐ๋ ๋ ์์
    mainthread = (HANDLE)_beginthreadex(NULL, 0, do_chat_service, (void*)0, 0, &tid);
    if (mainthread)
    {
        while (1)
        {
            gets_s(message, MAXBYTE);
            if (strcmp(message, "/x") == 0)
                break;
            notify_client(message);
        }
        // ์๋ฒ ์ข
๋ฃ
        server_close();
        WSACleanup();
        CloseHandle(mainthread);
    }
    return 0;
}
// ์๋ฒ ์ด๊ธฐํ ํจ์
int server_init()
{
    WSADATA wsadata;
    SOCKET s;
    SOCKADDR_IN server_address;
    memset(&sock_array, 0, sizeof(sock_array));
    total_socket_count = 0;
    if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
    {
        puts("WSAStartup ์๋ฌ.");
        return -1;
    }
    if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        puts("socket ์๋ฌ.");
        return -1;
    }
    memset(&server_address, 0, sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(port_number);
    if (bind(s, (struct sockaddr*)&server_address, sizeof(server_address)) < 0)
    {
        puts("bind ์๋ฌ");
        return -2;
    }
    if (listen(s, SOMAXCONN) < 0)
    {
        puts("listen ์๋ฌ");
        return -3;
    }
    return s;
}
// ์๋ฒ ์ข
๋ฃ ํจ์
int server_close()
{
    for (int i = 1; i < total_socket_count; i++)
    {
        closesocket(sock_array[i].s);
        WSACloseEvent(sock_array[i].ev);
    }
    return 0;
}
// ํด๋ผ์ด์ธํธ์์ ํต์ ์ ๋ด๋นํ๋ ํจ์
unsigned int WINAPI do_chat_service(void* param)
{
    SOCKET server_socket;
    WSANETWORKEVENTS ev;
    int index;
    WSAEVENT handle_array[client_count + 1];
    // ์๋ฒ ์ด๊ธฐํ
    server_socket = server_init();
    if (server_socket < 0)
    {
        printf("์ด๊ธฐํ ์๋ฌ\n");
        exit(0);
    }
    else
    {
        printf("\n >> ์๋ฒ ์ด๊ธฐํ๊ฐ ์๋ฃ๋์์ต๋๋ค. (ํฌํธ๋ฒํธ:%d)\n", port_number);
        HANDLE event = WSACreateEvent();
        sock_array[total_socket_count].ev = event;
        sock_array[total_socket_count].s = server_socket;
        strcpy_s(sock_array[total_socket_count].nick, "svr");
        strcpy_s(sock_array[total_socket_count].ipaddr, "0.0.0.0");
        WSAEventSelect(server_socket, event, FD_ACCEPT);
        total_socket_count++;
        while (true)
        {
            memset(&handle_array, 0, sizeof(handle_array));
            for (int i = 0; i < total_socket_count; i++)
                handle_array[i] = sock_array[i].ev;
            index = WSAWaitForMultipleEvents(total_socket_count, handle_array, false, INFINITE, false);
            if ((index != WSA_WAIT_FAILED) && (index != WSA_WAIT_TIMEOUT))
            {
                WSAEnumNetworkEvents(sock_array[index].s, sock_array[index].ev, &ev);
                if (ev.lNetworkEvents == FD_ACCEPT)
                    add_client(index);
                else if (ev.lNetworkEvents == FD_READ)
                    read_client(index);
                else if (ev.lNetworkEvents == FD_CLOSE)
                    remove_client(index);
            }
        }
        closesocket(server_socket);
    }
    WSACleanup();
    _endthreadex(0);
    return 0;
}
// ํด๋ผ์ด์ธํธ ์ถ๊ฐ ํจ์
int add_client(int index)
{
    SOCKADDR_IN addr;
    int len = 0;
    SOCKET accept_sock;
    if (total_socket_count == FD_SETSIZE)
        return 1;
    else {
        len = sizeof(addr);
        memset(&addr, 0, sizeof(addr));
        accept_sock = accept(sock_array[0].s, (SOCKADDR*)&addr, &len);
        HANDLE event = WSACreateEvent();
        sock_array[total_socket_count].ev = event;
        sock_array[total_socket_count].s = accept_sock;
        strcpy_s(sock_array[total_socket_count].ipaddr, inet_ntoa(addr.sin_addr));
        WSAEventSelect(accept_sock, event, FD_READ | FD_CLOSE);
        total_socket_count++;
        printf(" >> ์ ๊ท ํด๋ผ์ด์ธํธ ์ ์(IP : %s)\n", inet_ntoa(addr.sin_addr));
        char msg[256];
        sprintf_s(msg, " >> ์ ๊ท ํด๋ผ์ด์ธํธ ์ ์(IP : %s)\n", inet_ntoa(addr.sin_addr));
        notify_client(msg);
    }
    return 0;
}
// ํด๋ผ์ด์ธํธ ์ฝ๊ธฐ ํจ์
int read_client(int index)
{
    unsigned int tid;
    HANDLE mainthread = (HANDLE)_beginthreadex(NULL, 0, recv_and_forward, (void*)index, 0, &tid);
    WaitForSingleObject(mainthread, INFINITE);
    CloseHandle(mainthread);
    return 0;
}
// ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฉ์์ง ์์  ๋ฐ ์ ๋ฌ ํจ์
unsigned int WINAPI recv_and_forward(void* param)
{
    int index = (int)param;
    char message[MAXBYTE], share_message[MAXBYTE];
    SOCKADDR_IN client_address;
    int recv_len = 0, addr_len = 0;
    char* token1 = NULL;
    char* next_token = NULL;
    memset(&client_address, 0, sizeof(client_address));
    if ((recv_len = recv(sock_array[index].s, message, MAXBYTE, 0)) > 0)
    {
        addr_len = sizeof(client_address);
        getpeername(sock_array[index].s, (SOCKADDR*)&client_address, &addr_len);
        strcpy_s(share_message, message);
        if (strlen(sock_array[index].nick) <= 0)
        {
            token1 = strtok_s(message, "]", &next_token);
            strcpy_s(sock_array[index].nick, token1 + 1);
        }
        for (int i = 1; i < total_socket_count; i++)
            send(sock_array[i].s, share_message, MAXBYTE, 0);
    }
    _endthreadex(0);
    return 0;
}
// ํด๋ผ์ด์ธํธ ์ ๊ฑฐ ํจ์
void remove_client(int index)
{
    char remove_ip[256];
    char message[MAXBYTE];
    strcpy_s(remove_ip, get_client_ip(index));
    printf(" >> ํด๋ผ์ด์ธํธ ์ ์ ์ข
๋ฃ(Index: %d, IP: %s, ๋ณ๋ช
: %s)\n", index, remove_ip, sock_array[index].nick);
    sprintf_s(message, " >> ํด๋ผ์ด์ธํธ ์ ์ ์ข
๋ฃ(IP: %s, ๋ณ๋ช
: %s)\n", remove_ip, sock_array[index].nick);
    closesocket(sock_array[index].s);
    WSACloseEvent(sock_array[index].ev);
    total_socket_count--;
    sock_array[index].s = sock_array[total_socket_count].s;
    sock_array[index].ev = sock_array[total_socket_count].ev;
    strcpy_s(sock_array[index].ipaddr, sock_array[total_socket_count].ipaddr);
    strcpy_s(sock_array[index].nick, sock_array[total_socket_count].nick);
    notify_client(message);
}
// ํด๋ผ์ด์ธํธ IP ์ฃผ์ ๊ฐ์ ธ์ค๋ ํจ์
char* get_client_ip(int index)
{
    static char ipaddress[256];
    int addr_len;
    struct sockaddr_in sock;
    addr_len = sizeof(sock);
    if (getpeername(sock_array[index].s, (struct sockaddr*)&sock, &addr_len) < 0)
        return NULL;
    strcpy_s(ipaddress, inet_ntoa(sock.sin_addr));
    return ipaddress;
}
// ํด๋ผ์ด์ธํธ์ ๋ฉ์์ง ์๋ฆฌ๋ ํจ์
int notify_client(char* message)
{
    for (int i = 1; i < total_socket_count; i++)
        send(sock_array[i].s, message, MAXBYTE, 0);
    return 0;
}