/*
 * lws-minimal-secure-streams-custom-proxy-transport
 *
 * Written in 2010-2021 by Andy Green <andy@warmcat.com>
 *                         Kutoga <kutoga@user.github.invalid>
 *
 * This file is made available under the Creative Commons CC0 1.0
 * Universal Public Domain Dedication.
 *
 *
 * This represents some existing application event loop that liblws-sspc must
 * cooperate with.
 */

#include "private.h"

custom_poll_ctx_t a_cpcx;

static struct pollfd *
custom_poll_find_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd)
{
	int n;

	for (n = 0; n < cpcx->count_pollfds; n++)
		if (cpcx->pollfds[n].fd == fd)
			return &cpcx->pollfds[n];

	return NULL;
}

int
custom_poll_add_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd, int events,
		   void *priv)
{
	struct pollfd *pfd;

	lwsl_info("%s: ADD fd %d, ev %d\n", __func__, fd, events);

	pfd = custom_poll_find_fd(cpcx, fd);
	if (pfd) {
		lwsl_err("%s: ADD fd %d already in ext table\n", __func__, fd);
		return 1;
	}

	if (cpcx->count_pollfds == LWS_ARRAY_SIZE(cpcx->pollfds)) {
		lwsl_err("%s: no room left\n", __func__);
		return 1;
	}

	cpcx->priv[cpcx->count_pollfds] = priv;
	pfd = &cpcx->pollfds[cpcx->count_pollfds++];
	pfd->fd = fd;
	pfd->events = (short)events;
	pfd->revents = 0;

	return 0;
}

int
custom_poll_del_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd)
{
	struct pollfd *pfd;

	lwsl_info("%s: DEL fd %d\n", __func__, fd);

	pfd = custom_poll_find_fd(cpcx, fd);
	if (!pfd) {
		lwsl_err("%s: DEL fd %d missing in ext table\n", __func__, fd);
		return 1;
	}

	if (cpcx->count_pollfds > 1)
		*pfd = cpcx->pollfds[cpcx->count_pollfds - 1];

	cpcx->count_pollfds--;

	return 0;
}

int
custom_poll_change_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd,
		     int events_add, int events_remove)
{
	struct pollfd *pfd;

	lwsl_info("%s: CHG fd %d, ev_add %d, ev_rem %d\n", __func__, fd,
			events_add, events_remove);

	pfd = custom_poll_find_fd(cpcx, fd);
	if (!pfd)
		return 1;

	pfd->events = (short)((pfd->events & (~events_remove)) | events_add);

	return 0;
}

int
custom_poll_run(custom_poll_ctx_t *cpcx)
{
	int n;

	while (!interrupted) {

		lws_usec_t timeout_us = 2000000000, now = lws_now_usecs();

		if (cpcx->scheduler.count) {
			lws_sorted_usec_list_t *sul = (lws_sorted_usec_list_t *)
					lws_dll2_get_head(&cpcx->scheduler);
			if (sul->us < now)
				timeout_us = 0;
			else
				timeout_us = sul->us - now;
		}

//		lwsl_notice("%s: entering poll wait %dms\n", __func__, (int)(timeout_us / 1000));

		n = poll(cpcx->pollfds, (nfds_t)cpcx->count_pollfds, (int)(timeout_us / 1000));

//		lwsl_notice("%s: exiting poll after %lluus\n", __func__,
//				(unsigned long long)(lws_now_usecs() - now));

		do {
			lws_sorted_usec_list_t *sul = (lws_sorted_usec_list_t *)
				lws_dll2_get_head(&cpcx->scheduler);

			if (!sul)
				break;

			if (sul->us > now)
				break;

			lws_dll2_remove(&sul->list);
			sul->cb(sul);
		} while (1);

		if (n <= 0)
			continue;

		/* service anything that has active revents */

		for (n = 0; n < cpcx->count_pollfds; n++) {
			int m;

			if (!cpcx->pollfds[n].revents)
				continue;

			/*
			 * the only fd we registered in this example is the
			 * transport fd, so we miss out the code to match the
			 * fd to the right callback
			 */

			m = custom_transport_event(&cpcx->pollfds[n], cpcx->priv[n]);
			if (m < 0) {
				custom_poll_del_fd(cpcx, cpcx->pollfds[n].fd);
			}
		}
	}

	return 0;
}