/*=== Main thread */
void *tnet_transport_mainthread(void *param)
{
    tnet_transport_t *transport = param;
    transport_context_t *context = transport->context;
    int ret, status;
    tsk_size_t i;
    tsk_bool_t is_stream;
    tnet_fd_t fd;

    struct sockaddr_storage remote_addr = {0};
    transport_socket_xt* active_socket;

    /* check whether the transport is already prepared */
    if(!transport->prepared) {
        TSK_DEBUG_ERROR("Transport must be prepared before strating.");
        goto bail;
    }

    is_stream = TNET_SOCKET_TYPE_IS_STREAM(transport->master->type);

    TSK_DEBUG_INFO("Starting [%s] server with IP {%s} on port {%d} using master fd {%d} with type {%d} with max_fds {%lu}...",
                   transport->description,
                   transport->master->ip,
                   transport->master->port,
                   transport->master->fd,
                   transport->master->type,
                   sizeof(context->ufds)/sizeof(context->ufds[0]));

    while(TSK_RUNNABLE(transport)->running || TSK_RUNNABLE(transport)->started) {
        context->polling = tsk_true;
        ret = tnet_poll(context->ufds, context->count, -1);
        context->polling = tsk_false;
        if(ret < 0) {
            TNET_PRINT_LAST_ERROR("poll() have failed.");
            goto bail;
        }

        if(!TSK_RUNNABLE(transport)->running && !TSK_RUNNABLE(transport)->started) {
            TSK_DEBUG_INFO("Stopping [%s] server with IP {%s} on port {%d} with type {%d}...", transport->description, transport->master->ip, transport->master->port, transport->master->type);
            goto bail;
        }

        /* lock context */
        tsk_safeobj_lock(context);

        /* == == */
        for(i=0; i<context->count; i++) {
            if(!context->ufds[i].revents) {
                continue;
            }

            // TSK_DEBUG_INFO("REVENTS(i=%d) = %d", i, context->ufds[i].revents);

            if(context->ufds[i].fd == context->pipeR) {
                TSK_DEBUG_INFO("PipeR event = %d", context->ufds[i].revents);
                if(context->ufds[i].revents & TNET_POLLIN) {
                    static char __buffer[1024];
                    if(read(context->pipeR, __buffer, sizeof(__buffer)) < 0) {
                        TNET_PRINT_LAST_ERROR("Failed to read from the Pipe");
                    }
                }
                else if(context->ufds[i].revents & TNET_POLLHUP) {
                    TNET_PRINT_LAST_ERROR("Pipe Error");
                    goto bail;
                }
                context->ufds[i].revents = 0;
                continue;
            }

            /* Get active event and socket */
            active_socket = context->sockets[i];

            /*================== TNET_POLLHUP ==================*/
            if(context->ufds[i].revents & (TNET_POLLHUP)) {
                if(context->ufds[i].revents & TNET_POLLOUT) {
                    TSK_DEBUG_INFO("POLLOUT and POLLHUP are exclusive");
                }
                else {
                    fd = active_socket->fd;
                    TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLHUP(%d)", transport->description, fd);

                    tnet_transport_remove_socket(transport, &active_socket->fd);
                    TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, fd);
                    continue;
                }
            }

            /*================== TNET_POLLERR ==================*/
            if(context->ufds[i].revents & (TNET_POLLERR)) {
                fd = active_socket->fd;
                TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLERR(%d)", transport->description, fd);

                tnet_transport_remove_socket(transport, &active_socket->fd);
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, fd);
                continue;
            }

            /*================== TNET_POLLNVAL ==================*/
            if(context->ufds[i].revents & (TNET_POLLNVAL)) {
                fd = active_socket->fd;
                TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLNVAL(%d)", transport->description, fd);

                tnet_transport_remove_socket(transport, &active_socket->fd);
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, fd);
                continue;
            }

            /*================== POLLIN ==================*/
            if(context->ufds[i].revents & TNET_POLLIN) {
                tsk_size_t len = 0;
                void* buffer = tsk_null;
                tnet_transport_event_t* e;

                // TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLIN(%d)", transport->description, active_socket->fd);

                /* check whether the socket is paused or not */
                if(active_socket->paused) {
                    TSK_DEBUG_INFO("Socket is paused");
                    goto TNET_POLLIN_DONE;
                }

                /* Retrieve the amount of pending data.
                 * IMPORTANT: If you are using Symbian please update your SDK to the latest build (August 2009) to have 'FIONREAD'.
                 * This apply whatever you are using the 3rd or 5th edition.
                 * Download link: http://wiki.forum.nokia.com/index.php/Open_C/C%2B%2B_Release_History
                 */
                ret = tnet_ioctlt(active_socket->fd, FIONREAD, &len);
                if((ret < 0 || !len) && is_stream) {
                    /* It's probably an incoming connection --> try to accept() it */
                    int listening = 0, remove_socket = 0;
                    socklen_t socklen = sizeof(listening);

                    TSK_DEBUG_INFO("ioctlt(%d), len=%u returned zero or failed", active_socket->fd, (unsigned)len);

                    // check if socket is listening
                    if(getsockopt(active_socket->fd, SOL_SOCKET, SO_ACCEPTCONN, &listening, &socklen) != 0) {
#if defined(BSD) /* old FreeBSD versions (and OSX up to Lion) do not support SO_ACCEPTCONN */
                        listening = 1;
#else
                        TNET_PRINT_LAST_ERROR("getsockopt(SO_ACCEPTCONN, %d) failed\n", active_socket->fd);
                        /* not socket accepted -> no socket to remove */
                        goto TNET_POLLIN_DONE;
#endif
                    }
                    if (listening) {
                        if((fd = accept(active_socket->fd, tsk_null, tsk_null)) != TNET_INVALID_SOCKET) {
                            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_ACCEPT(fd=%d)", transport->description, fd);
                            addSocket(fd, transport->master->type, transport, tsk_true, tsk_false, tsk_null);
                            TSK_RUNNABLE_ENQUEUE(transport, event_accepted, transport->callback_data, fd);
                            if(active_socket->tlshandle) {
                                transport_socket_xt* tls_socket;
                                if((tls_socket = getSocket(context, fd))) {
                                    if(tnet_tls_socket_accept(tls_socket->tlshandle) != 0) {
                                        TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, fd);
                                        tnet_transport_remove_socket(transport, &fd);
                                        TNET_PRINT_LAST_ERROR("SSL_accept() failed");
                                        continue;
                                    }
                                }
                            }
                        }
                        else {
                            TNET_PRINT_LAST_ERROR("accept(%d) failed", active_socket->fd);
                            remove_socket = 1;
                        }
                    }
                    else {
                        TSK_DEBUG_INFO("Closing socket with fd = %d because ioctlt() returned zero or failed", active_socket->fd);
                        remove_socket = 1;
                    }

                    if(remove_socket) {
                        fd = active_socket->fd;
                        tnet_transport_remove_socket(transport, &active_socket->fd);
                        TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, fd);
                        continue;
                    }
                    goto TNET_POLLIN_DONE;
                }

                if(len <= 0) {
#if defined(__ANDROID__) || defined(ANDROID)
                    // workaround for indoona OSX which sends bodiless UDP packets
                    // vand Android requires to call recv() even if len is equal to zero
                    if(len == 0 && ret == 0) {
                        static char __fake_buff[1];
                        ret = recv(active_socket->fd, __fake_buff, len, 0);
                    }
#endif
                    goto TNET_POLLIN_DONE;
                }

                if (!(buffer = tsk_calloc(len, sizeof(uint8_t)))) {
                    TSK_DEBUG_ERROR("TSK_CALLOC FAILED");
                    goto TNET_POLLIN_DONE;
                }

                // Retrieve the remote address
                if (TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)) {
                    ret = tnet_getpeername(active_socket->fd, &remote_addr);
                }

                // Receive the waiting data
                if (active_socket->tlshandle) {
                    int isEncrypted;
                    tsk_size_t tlslen = len;
                    if ((ret = tnet_tls_socket_recv(active_socket->tlshandle, &buffer, &tlslen, &isEncrypted)) == 0) {
                        if (isEncrypted) {
                            TSK_FREE(buffer);
                            goto TNET_POLLIN_DONE;
                        }
                        if (ret == 0) {
                            len = ret = tlslen;
                        }
                    }
                }
                else {
                    if (is_stream) {
                        ret = tnet_sockfd_recv(active_socket->fd, buffer, len, 0);
                    }
                    else {
                        ret = tnet_sockfd_recvfrom(active_socket->fd, buffer, len, 0, (struct sockaddr*)&remote_addr);
                    }
                }

                if(ret < 0) {
                    TSK_FREE(buffer);
                    status = tnet_geterrno();
                    // do not remove the socket for i/o pending errors
                    if (status == TNET_ERROR_WOULDBLOCK || status == TNET_ERROR_INPROGRESS || status == TNET_ERROR_EAGAIN) {
                        TSK_DEBUG_WARN("recv returned error code:%d", status);
                    }
                    else {
                        TNET_PRINT_LAST_ERROR("recv/recvfrom have failed");
                        removeSocket(i, context);
                    }
                    goto TNET_POLLIN_DONE;
                }

                if((len != (tsk_size_t)ret) && len) {
                    len = (tsk_size_t)ret;
                    // buffer = tsk_realloc(buffer, len);
                }

                if(len > 0) {
                    transport->bytes_in += len;
                    e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd);
                    e->data = buffer, buffer = tsk_null;
                    e->size = len;
                    e->remote_addr = remote_addr;

                    TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(transport), e);
                }
                TSK_FREE(buffer);

TNET_POLLIN_DONE:
                /*context->ufds[i].revents &= ~TNET_POLLIN*/
                ;
            }


            /*================== TNET_POLLOUT ==================*/
            if(context->ufds[i].revents & TNET_POLLOUT) {
                TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLOUT", transport->description);
                if(!active_socket->connected) {
                    active_socket->connected = tsk_true;
                    TSK_RUNNABLE_ENQUEUE(transport, event_connected, transport->callback_data, active_socket->fd);
                }
                //else{
                context->ufds[i].events &= ~TNET_POLLOUT;
                //}
            }


            /*================== TNET_POLLPRI ==================*/
            if(context->ufds[i].revents & TNET_POLLPRI) {
                TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- TNET_POLLPRI", transport->description);
            }

            context->ufds[i].revents = 0;
        }/* for */

done:
        /* unlock context */
        tsk_safeobj_unlock(context);

    } /* while */

bail:

    TSK_DEBUG_INFO("Stopped [%s] server with IP {%s} on port {%d}", transport->description, transport->master->ip, transport->master->port);
    return 0;
}
/*=== Main thread */
void* TSK_STDCALL tnet_transport_mainthread(void *param)
{
    tnet_transport_t *transport = (tnet_transport_t*)param;
    transport_context_t *context = (transport_context_t*)transport->context;
    DWORD evt;
    WSANETWORKEVENTS networkEvents;
    DWORD flags = 0;
    int ret;

    struct sockaddr_storage remote_addr = { 0 };
    WSAEVENT active_event;
    transport_socket_xt* active_socket;
    int index;

    TSK_DEBUG_INFO("Starting [%s] server with IP {%s} on port {%d} with type {%d}...", transport->description, transport->master->ip, transport->master->port, transport->master->type);

    while (TSK_RUNNABLE(transport)->running || TSK_RUNNABLE(transport)->started) {
        /* Wait for multiple events */
        if ((evt = WSAWaitForMultipleEvents((DWORD)context->count, context->events, FALSE, WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED) {
            TNET_PRINT_LAST_ERROR("WSAWaitForMultipleEvents have failed.");
            goto bail;
        }

        if (!TSK_RUNNABLE(transport)->running && !TSK_RUNNABLE(transport)->started) {
            goto bail;
        }

        /* lock context */
        tsk_safeobj_lock(context);

        /* Get active event and socket */
        index = (evt - WSA_WAIT_EVENT_0);
        active_event = context->events[index];
        if (!(active_socket = context->sockets[index])) {
            goto done;
        }

        /* Get the network events flags */
        if (WSAEnumNetworkEvents(active_socket->fd, active_event, &networkEvents) == SOCKET_ERROR) {
            TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
            TNET_PRINT_LAST_ERROR("WSAEnumNetworkEvents have failed.");

            tsk_safeobj_unlock(context);
            goto bail;
        }

        /*================== FD_ACCEPT ==================*/
        if (networkEvents.lNetworkEvents & FD_ACCEPT) {
            tnet_fd_t fd;

            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_ACCEPT", transport->description);

            if (networkEvents.iErrorCode[FD_ACCEPT_BIT]) {
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
                TNET_PRINT_LAST_ERROR("ACCEPT FAILED.");
                goto done;
            }

            /* Accept the connection */
            if ((fd = (tnet_fd_t)WSAAccept(active_socket->fd, NULL, NULL, AcceptCondFunc, (DWORD_PTR)context)) != INVALID_SOCKET) {
                /* Add the new fd to the server context */
                addSocket(fd, transport->master->type, transport, tsk_true, tsk_false, tsk_null);
                if (active_socket->tlshandle) {
                    transport_socket_xt* tls_socket;
                    if ((tls_socket = getSocket(context, fd))) {
                        if (tnet_tls_socket_accept(tls_socket->tlshandle)) {
                            tnet_transport_remove_socket(transport, &fd);
                            TNET_PRINT_LAST_ERROR("SSL_accept() failed");
                            goto done;
                        }
                    }
                }
                if (WSAEventSelect(fd, context->events[context->count - 1], FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR) {
                    tnet_transport_remove_socket(transport, &fd);
                    TNET_PRINT_LAST_ERROR("WSAEventSelect() have failed.");
                    goto done;
                }
                TSK_RUNNABLE_ENQUEUE(transport, event_accepted, transport->callback_data, fd);
            }
            else {
                TNET_PRINT_LAST_ERROR("ACCEPT FAILED.");
                goto done;
            }




        }

        /*================== FD_CONNECT ==================*/
        if (networkEvents.lNetworkEvents & FD_CONNECT) {
            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_CONNECT", transport->description);

            if (networkEvents.iErrorCode[FD_CONNECT_BIT]) {
                tnet_fd_t fd = active_socket->fd;
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, fd);
                tnet_transport_remove_socket(transport, &fd);
                TNET_PRINT_LAST_ERROR("CONNECT FAILED.");
                goto done;
            }
            else {
                TSK_RUNNABLE_ENQUEUE(transport, event_connected, transport->callback_data, active_socket->fd);
                active_socket->connected = 1;
            }
        }


        /*================== FD_READ ==================*/
        if (networkEvents.lNetworkEvents & FD_READ) {
            DWORD readCount = 0;
            WSABUF wsaBuffer;

            /* TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_READ", transport->description); */

            /* check whether the socket is paused or not */
            if (active_socket->paused) {
                TSK_DEBUG_INFO("Socket is paused");
                goto FD_READ_DONE;
            }

            if (networkEvents.iErrorCode[FD_READ_BIT]) {
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
                TNET_PRINT_LAST_ERROR("READ FAILED.");
                goto done;
            }

            /* Retrieve the amount of pending data */
            if (tnet_ioctlt(active_socket->fd, FIONREAD, &(wsaBuffer.len)) < 0) {
                TNET_PRINT_LAST_ERROR("IOCTLT FAILED.");
                goto done;
            }

            if (!wsaBuffer.len) {
                goto done;
            }

            /* Alloc data */
            if (!(wsaBuffer.buf = tsk_calloc(wsaBuffer.len, sizeof(uint8_t)))) {
                goto done;
            }

            /* Retrieve the remote address */
            if (TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)) {
                ret = tnet_getpeername(active_socket->fd, &remote_addr);
            }

            /* Receive the waiting data. */
            if (active_socket->tlshandle) {
                int isEncrypted;
                tsk_size_t len = wsaBuffer.len;
                if (!(ret = tnet_tls_socket_recv(active_socket->tlshandle, &wsaBuffer.buf, &len, &isEncrypted))) {
                    if (isEncrypted) {
                        TSK_FREE(wsaBuffer.buf);
                        goto done;
                    }
                    wsaBuffer.len = (ULONG)len;
                }
            }
            else {
                if (TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)) {
                    ret = WSARecv(active_socket->fd, &wsaBuffer, 1, &readCount, &flags, 0, 0);
                }
                else {
                    int len = sizeof(remote_addr);
                    ret = WSARecvFrom(active_socket->fd, &wsaBuffer, 1, &readCount, &flags,
                                      (struct sockaddr*)&remote_addr, &len, 0, 0);
                }
                if (readCount < wsaBuffer.len) {
                    wsaBuffer.len = readCount;
                    /* wsaBuffer.buf = tsk_realloc(wsaBuffer.buf, readCount); */
                }
            }

            if (ret) {
                ret = WSAGetLastError();
                if (ret == WSAEWOULDBLOCK) {
                    // Doesn't (always) mean congestion but... another thread is also poll()ing the FD. For example, when TURN session has a reference to the fd.
                    TSK_DEBUG_WARN("WSAEWOULDBLOCK error for READ SSESSION");
                }
                else if (ret == WSAECONNRESET && TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type)) {
                    /* For DGRAM ==> The sent packet gernerated "ICMP Destination/Port unreachable" result. */
                    TSK_FREE(wsaBuffer.buf);
                    goto done; // ignore and retry.
                }
                else {
                    TSK_FREE(wsaBuffer.buf);

                    removeSocket(index, context);
                    TNET_PRINT_LAST_ERROR("WSARecv have failed.");
                    goto done;
                }
            }
            else {
                tnet_transport_event_t* e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd);
                transport->bytes_in += wsaBuffer.len;
                e->data = wsaBuffer.buf;
                e->size = wsaBuffer.len;
                e->remote_addr = remote_addr;

                TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(transport), e);
            }
FD_READ_DONE:
            ;
        }




        /*================== FD_WRITE ==================*/
        if (networkEvents.lNetworkEvents & FD_WRITE) {
            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_WRITE", transport->description);

            if (networkEvents.iErrorCode[FD_WRITE_BIT]) {
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
                TNET_PRINT_LAST_ERROR("WRITE FAILED.");
                goto done;
            }
        }



        /*================== FD_CLOSE ==================*/
        if (networkEvents.lNetworkEvents & FD_CLOSE) {
            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_CLOSE", transport->description);

            TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, active_socket->fd);
            removeSocket(index, context);
        }

        /*	http://msdn.microsoft.com/en-us/library/ms741690(VS.85).aspx

        	The proper way to reset the state of an event object used with the WSAEventSelect function
        	is to pass the handle of the event object to the WSAEnumNetworkEvents function in the hEventObject parameter.
        	This will reset the event object and adjust the status of active FD events on the socket in an atomic fashion.
        	*/
        /* WSAResetEvent(active_event); <== DO NOT USE (see above) */

done:
        /* unlock context */
        tsk_safeobj_unlock(context);
    } /* while(transport->running) */


bail:

    TSK_DEBUG_INFO("Stopped [%s] server with IP {%s} on port {%d} with type {%d}...", transport->description, transport->master->ip, transport->master->port, transport->master->type);
    return tsk_null;
}