/*
 * sai-power com-warmcat-sai client protocol implementation
 *
 * Copyright (C) 2019 - 2025 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
 */

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

#include "p-private.h"

static const char *tokens[] = {
	"Voltage",
	"Current",
	"Active",
	"Power",
	"Apparent",
	"Reactive",
	"Factor",
	"Energy",
	"Today",
	"Yesterday",
	"Total",
};

enum {
	TOKORD_VOLTAGE,
	TOKORD_CURRENT,
	TOKORD_ACTIVE,
	TOKORD_POWER,
	TOKORD_APPARENT,
	TOKORD_REACTIVE,
	TOKORD_FACTOR,
	TOKORD_ENERGY,
	TOKORD_TODAY,
	TOKORD_YESTERDAY,
	TOKORD_TOTAL
};

int
saip_parse_tasmota_status(tasmota_parse_t *tp)
{
	lws_tokenize_elem e;
	unsigned int *i;
	const char *p;
	int n;

	do {
		e = lws_tokenize(&tp->ts);

		if (e == LWS_TOKZE_DELIMITER) {
			switch (tp->ts.token[0]) {
			case '<':
				tp->s |=  1u;
				break;
			case '>':
				tp->s &= (uint8_t)~1u;
				continue;
			case '{':
				tp->s |=  2u;
				break;
			case '}':
				tp->s &= (uint8_t)~2u;
				continue;
			case '&':
				tp->s |=  4u;
				break;
			case ';':
				tp->s &= (uint8_t)~4u;
				continue;
			}
		}

		if (tp->s)
			continue;

		switch (e) {
		case LWS_TOKZE_ENDED:
			return 1;

		case LWS_TOKZE_TOKEN:
			for (n = 0; n < (int)LWS_ARRAY_SIZE(tokens); n++) {
				if (strlen(tokens[n]) == tp->ts.token_len &&
				    !strcmp(tokens[n], tp->ts.token)) {
					tp->match = (uint16_t)((tp->match << 8) | n);
					break;
				}
			}

			if (n == LWS_ARRAY_SIZE(tokens)) {
				// lwsl_notice("%s: unknown token '%.*s'\n", __func__, (int)tp->ts.token_len, tp->ts.token);
				continue;
			}
			break;

		case LWS_TOKZE_INTEGER:
			if ((tp->match & 0xff) == TOKORD_VOLTAGE)
				tp->td.voltage_v = (unsigned int)atoi(tp->ts.token);
			if ((tp->match >> 8)   == TOKORD_ACTIVE   &&
			    (tp->match & 0xff) == TOKORD_POWER)
				tp->td.active_power_w = (unsigned int)atoi(tp->ts.token);
			if ((tp->match >> 8)   == TOKORD_APPARENT &&
			    (tp->match & 0xff) == TOKORD_POWER)
				tp->td.apparent_power_va = (unsigned int)atoi(tp->ts.token);
			if ((tp->match >> 8)   == TOKORD_REACTIVE &&
			    (tp->match & 0xff) == TOKORD_POWER)
				tp->td.reactive_power_var = (unsigned int)atoi(tp->ts.token);
			break;

		case LWS_TOKZE_FLOAT:
			i = NULL;

			if ((tp->match & 0xff) == TOKORD_CURRENT)
				i = &tp->td.current_ma;
			if ((tp->match >> 8)   == TOKORD_POWER  &&
			    (tp->match & 0xff) == TOKORD_FACTOR)
				i = &tp->td.power_factor_scaled_1000;
			if ((tp->match >> 8)   == TOKORD_ENERGY &&
			    (tp->match & 0xff) == TOKORD_TODAY)
				i = &tp->td.energy_today_wh;
			if ((tp->match >> 8)   == TOKORD_ENERGY &&
			    (tp->match & 0xff) == TOKORD_YESTERDAY)
				i = &tp->td.energy_yesterday_wh;
			if ((tp->match >> 8)   == TOKORD_ENERGY &&
			    (tp->match & 0xff) == TOKORD_TOTAL)
				i = &tp->td.energy_total_wh;

			if (i) {
				*i = 1000 * (unsigned int)atoi(tp->ts.token);
				p = strchr(tp->ts.token, '.');
				if (p++) {
					static const unsigned int mu[] = { 0, 100, 10, 1 };

					n = (int)strlen(p);
					if (n > 3)
						n = 3;
					*i += (unsigned int)atoi(p) * mu[n];
				}
			}
			break;
		default:
			break;
		}

	} while (e > 0);

	return e;
}