/*
 * sai-expect
 *
 * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation:
 *  version 2.1 of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA  02110-1301  USA
 *
 * sai-builder     : exports logproxy unix domain paths in SAI_LOGPROXY_TTY0/1
 *   sai-device    : exports raw tty node path in SAI_DEVICE_TTY0/1
 *     sai-expect  : proxies SAI_DEVICE_TTYx to SAI_LOGPROXY_TTYx, performs TX,
 *		     waits for and assesses RX
 *
 * Sai-expect opportunistically opens ttys exported by sai-device in the
 * environment, proxies logs to sai-builder (which forwards them to the sai-
 * server that is storing them with the task), and parses the received tty
 * data and emits strings as requested by its arguments.
 */

#include <libwebsockets.h>
#include <string.h>
#include <signal.h>

#include "e-private.h"

struct lws_ss_handle *ssh[3];
static lws_state_notify_link_t nl;
static const char *serpath[2];
static struct lws_vhost *vh;
int interrupted, bad = 1;

static const char * const default_ss_policy =
	"{"
	  "\"s\": ["
		/*
		 * Unix Domain Socket connections to sai-builder logging
		 */
		"{\"logproxy\": {"
			"\"endpoint\":"		"\"+${sockpath}\","
			"\"protocol\":"		"\"raw\","
			"\"metadata\": ["
				"{\"sockpath\": \"\"}"
			"]"
		"}}"
	"]}"
;

static const struct lws_protocols *pprotocols[] = {
	&protocol_serial,
	NULL
};

static lws_state_notify_link_t * const app_notifier_list[] = {
	&nl, NULL
};

static int
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
		    int current, int target)
{
	struct lws_context *context = lws_system_context_from_system_mgr(mgr);
	char log[128];
	int n;

	switch (target) {

	case LWS_SYSTATE_OPERATIONAL:
		if (current != LWS_SYSTATE_OPERATIONAL)
			break;

		ssh[0] = saicom_lp_ss_from_env(context, "SAI_LOGPROXY");
		if (!ssh[0])
			goto bail;
		ssh[1] = saicom_lp_ss_from_env(context, "SAI_LOGPROXY_TTY0");
		if (!ssh[1])
			goto bail;
		ssh[2] = saicom_lp_ss_from_env(context, "SAI_LOGPROXY_TTY1");
		if (!ssh[2])
			goto bail;

		if (!sai_serial_try_open(vh, serpath[0], 115200, 0))
			goto bail;
		if (serpath[1])
			if (!sai_serial_try_open(vh, serpath[1], 115200, 1))
				goto bail;

		n = lws_snprintf(log, sizeof(log), "sai-expect: starting\n");
		saicom_lp_add(ssh[0], log, (unsigned int)n);
		break;
	}

	return 0;

bail:
	return 1;
}

void sigint_handler(int sig)
{
	interrupted = 1;
}

int main(int argc, const char **argv)
{
	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
	struct lws_context_creation_info info;
	struct lws_context *context;
	const char *p;

	serpath[0] = getenv("SAI_DEVICE_TTY0");
	serpath[1] = getenv("SAI_DEVICE_TTY1");

	if (!serpath[0]) {
		lwsl_err("%s: need at least SAI_DEVICE_TTY0\n", __func__);
		return 1;
	}

	if ((p = lws_cmdline_option(argc, argv, "-d")))
		logs = atoi(p);

	lws_set_log_level(logs, NULL);

	lwsl_user("Sai Expect - "
		  "Copyright (C) 2019-2020 Andy Green <andy@warmcat.com>\n");

	memset(&info, 0, sizeof info);
	info.port = CONTEXT_PORT_NO_LISTEN;
	info.pprotocols = pprotocols;

	info.pss_policies_json = default_ss_policy;
	info.pt_serv_buf_size = 8 * 1024;
	info.options = LWS_SERVER_OPTION_VALIDATE_UTF8 |
			LWS_SERVER_OPTION_EXPLICIT_VHOSTS;

	signal(SIGINT, sigint_handler);
	info.fd_limit_per_thread = 1 + 8 + 1;

	/* hook up our lws_system state notifier */

	nl.name = "sai-devices";
	nl.notify_cb = app_system_state_nf;
	info.register_notifier_list = app_notifier_list;

	/* create the lws context */

	context = lws_create_context(&info);
	if (!context) {
		lwsl_err("lws init failed\n");
		return 1;
	}

	/* ... and our vhost... */

	vh = lws_create_vhost(context, &info);
	if (!vh) {
		lwsl_err("%s: Failed to create vhost\n", __func__);
		goto bail1;
	}

	while (!lws_service(context, 0) && !interrupted)
		;

bail1:
	lws_context_destroy(context);

	return bad;
}