/**
 * Get data by calling a program
 *
 * @package vzlogger
 * @copyright Copyright (c) 2016, The volkszaehler.org project
 * @license http://www.gnu.org/licenses/gpl.txt GNU Public License
 * @author Steffen Vogel <info@steffenvogel.de>
 * @author Hermann Drexler <hed007@web.de>
 */
/*
 * This file is part of volkzaehler.org
 *
 * volkzaehler.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * volkzaehler.org 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <sys/time.h>

#include "protocols/MeterExec.hpp"
#include "Options.hpp"
#include <VZException.hpp>

MeterExec::MeterExec(std::list<Option> options)
		: Protocol("exec")
{
	OptionList optlist;

	try {
		_command = optlist.lookup_string(options, "command");
	} catch (vz::VZException &e) {
		print(log_error, "Missing command or invalid type", name().c_str());
		//pthread_exit(0);
		//throw;
	}

	// a optional format string for scanf()
	try {
		const char *config_format = optlist.lookup_string(options, "format");

		/**
		 * Compiling the provided format string in a format string for scanf
		 * by replacing the following tokens
		 *
		 * "$v" => "%1$lf" (value)
		 * "$i" => "%2$ms" (identifier)		(memory gets allocated by sscanf())
		 * "$t" => "%3$lf" (timestamp)
		 */

		int config_len = strlen(config_format);
		int scanf_len = config_len + 15; // adding extra space for longer conversion specification in scanf_format

		char *scanf_format = (char *)malloc(scanf_len); // the scanf format string

		int i = 0; // index in config_format string
		int j = 0; // index in scanf_format string
		while (i <= config_len && j <= scanf_len) {
			switch (config_format[i]) {
					case '$':
						if (i+1 < config_len) { // introducing a token
							switch (config_format[i+1]) {
									case 'v': j += sprintf(scanf_format+j, "%%1$lf"); break;
									case 'i': j += sprintf(scanf_format+j, "%%2$s"); break;
									case 't': j += sprintf(scanf_format+j, "%%3$lf"); break;
							}
							i++;
						}
						break;

					case '%':
						scanf_format[j++] = '%'; // add double %% to escape a conversion identifier
						// nobreak;
					default:
						scanf_format[j++] = config_format[i]; // just copying
			}

			i++;
		}

		print(log_debug, "Parsed format string \"%s\" => \"%s\"", name().c_str(), config_format, scanf_format);
		_format = scanf_format;
	} catch (vz::OptionNotFoundException &e) {
		_format = ""; // use default format
	} catch (vz::VZException &e) {
		print(log_error, "Failed to parse format", name().c_str());
		//pthread_exit(0);
		//throw;
	}

}

MeterExec::~MeterExec() {
}

int MeterExec::open() {
	// nothing to do
	return SUCCESS;
}

int MeterExec::close() {
	// nothing to do
	return SUCCESS;
}

ssize_t MeterExec::read(std::vector<Reading> &rds, size_t n) {

	char line[256], *endptr;

	unsigned int i = 0;
	print(log_debug, "MeterExec::read: %d, %d cmd='%s'", name().c_str(), rds.size(), n, command());

	try {
	
	FILE *_fd = popen(command(), "r");
	if (_fd == NULL) {
		print(log_error, "error on command '%s'", name().c_str(), command());
		//pthread_exit(0);
	} else {
		print(log_info, "executing command '%s'", name().c_str(), command());
		
		char* lineptr = fgets(line, 256, _fd);
		while (i<n && lineptr ) {
			char *nl;
			if ((nl = strrchr(line, '\n'))) *nl = '\0'; // remove trailing newlines
			if ((nl = strrchr(line, '\r'))) *nl = '\0';
			
			print(log_debug, "MeterExec::read: '%s' (format=%s)", name().c_str(), line, format());

			if (_format != "") {
				double timestamp=-1.0;
				// at least the value has to been read
				double value=0.0;
				char idStr[255];
				memset(idStr, 0, sizeof(idStr));
				int found = sscanf(line, format(), &value, idStr, &timestamp);
				print(log_debug, "MeterExec::read(%s) found=%d value/id/time=%lf/'%s'/%lf", name().c_str(), format(), found, value, idStr, timestamp);

				rds[i].value(value);
				ReadingIdentifier *rid(new StringIdentifier(idStr));
				rds[i].identifier(rid);
				if (found >= 1) {
					if (timestamp >=0.0)
						rds[i].time_from_double(timestamp);
					else
						rds[i].time(); // use current timestamp
					print(log_debug, "MeterExec: read ok, timestamp/id/value=%d/%s/%lf", 
						name().c_str(), rds[i].time_s(), idStr, rds[i].value());
					i++; // read successfully
				}
			}
			else { // just reading a value per line
				rds[i].value(strtod(line, &endptr));
				rds[i].time();
				ReadingIdentifier* rid(new StringIdentifier(""));
				rds[i].identifier(rid);

				if (endptr != line) {
					print(log_debug, "MeterExecread ok: timestamp/id/value=%d/%s/%lf", 
						name().c_str(), rds[i].time_s(), "", rds[i].value());
					i++; // read successfully
				}
			}

			lineptr = fgets(line, 256, _fd);
			
		} // end while
		
		if (i == 0 && !lineptr) {
			print(log_warning, "MeterExec: no line from command='%s': exiting...", name().c_str(), command());
			//pthread_exit(0);
		}
			
		int err = pclose(_fd);
		print(log_debug, "MeterExec: pclose(%d) err=%d", name().c_str(), _fd, err);
			
	} // endif _fd != NULL
	} catch (std::exception &e) {
		print(log_error, "exception", name().c_str());
		std::stringstream oss;
		oss << e.what();
		print(log_error, "Reading-THREAD 'exec' - reading got an exception : %s", name().c_str(), e.what());
		//pthread_exit(0);
	}
	
	return i;
}
