package nl.erwinvaneyk.core.logging;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import nl.erwinvaneyk.communication.Message;
import nl.erwinvaneyk.core.Address;
import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;
import org.influxdb.dto.Database;
import org.influxdb.dto.Serie;
import retrofit.RetrofitError;

@Slf4j
public class InfluxLogger implements RawLogger {

	public static final String DEFAULT_DATABASE = "distributedrmi";
	public static final TimeUnit TIME_UNIT = TimeUnit.MILLISECONDS;
	public static final String FIELD_ORIGIN = "origin";
	public static final String FIELD_SUBJECT = "subject";
	public static final String FIELD_TIMESTAMP_SEND = "timestamp_send";
	public static final String FIELD_TIMESTAMP_RECEIVED = "timestamp_received";

	private static InfluxLogger influxLogger;

	private final InfluxDB influxDB;
	private final String database;
	private boolean available = true;

	/**
	 *
	 * @param address default: http://172.17.0.2:8086
	 * @param username default: root
	 * @param password default: root
	 */
	public InfluxLogger(Address address, String username, String password, String database) {
		influxDB = InfluxDBFactory.connect(address.toString(), username, password);
		this.database = database;
		createDatabaseIfAbsent(database);
	}

	public static InfluxLogger getInstance() {
		if(influxLogger == null) {
			influxLogger =  new InfluxLogger(new Address("localhost",8086), "root", "root", InfluxLogger.DEFAULT_DATABASE);
			influxLogger.deleteDatabase();
			influxLogger.createDatabaseIfAbsent(influxLogger.database);
		}
		return influxLogger;
	}

	public void log(@NonNull String subject,@NonNull Map<String,Object> args) {
		if(!available) {
			return;
		}
		if(args.isEmpty()) {
			log.warn("Cannot write a serie with no arguments to subject: {}", subject);
			return;
		}
		Serie.Builder builder = new Serie.Builder(subject);
		String[] columns = args.keySet().toArray(new String[args.size()]);
		builder.columns(columns);
		builder.values(args.values().toArray(new Object[args.size()]));
		Serie serie = builder.build();
		log.debug("query to influxDB: {}", serie);
		influxDB.write(database, TIME_UNIT, serie);
	}

	@Override
	public void log(Message message) {
		log(message.getContext(), message);
	}

	@Override
	public void log(String subject, Message message) {
		Map<String, Object> args = new HashMap<>(message.getFields());
		args.put(FIELD_ORIGIN, message.getOrigin() != null ? message.getOrigin().toString() : "");
		args.put(FIELD_SUBJECT, subject);
		args.put(FIELD_TIMESTAMP_RECEIVED, message.getTimestampReceived());
		args.put(FIELD_TIMESTAMP_SEND, message.getTimestampSend());
		log(subject, args);
	}

	@Override
	public boolean isAvailable() {
		try {
			return influxDB.ping() != null;
		} catch (RetrofitError e) {
			return false;
		}
	}

	void deleteDatabase() {
		if(available) {
			influxDB.deleteDatabase(database);
			log.debug("Deleted InfluxDB database: {}", database);
		} else {
			log.warn("Could not delete database '{}'; no connection to InfluxDB");
		}
	}

	void createDatabaseIfAbsent(String database) {
		if(isAvailable()) {
			if(!influxDB.describeDatabases().stream().map(Database::getName).anyMatch(db -> db.equals(database))) {
				log.debug("Created InfluxDB database: {} ", database);
				influxDB.createDatabase(database);
			}
		} else {
			log.warn("Could not connect to InfluxDB, any logs will be ignored!");
			available = false;
		}
	}
}

@@ -31,16 +31,45 @@
 gging;%0A%0A
+import java.io.Serializable;%0A
 import j
@@ -889,16 +889,146 @@
 ceived%22;
+%0A%09public static final String FIELD_CONTEXT = %22context%22;%0A%09public static final String FIELD_DELIVERY_DURATION = %22delivery_duration%22;
 %0A%0A%09priva
@@ -2625,156 +2625,441 @@
 p%3C%3E(
-message.getFields());%0A%09%09args.put(FIELD_ORIGIN, message.getOrigin() != null ? message.getOrigin().toString() : %22%22);%0A%09%09args.put(FIELD_SUBJECT, subject
+);%0A%09%09for(Map.Entry%3CString, Serializable%3E contentArg : message.getFields().entrySet()) %7B%0A%09%09%09args.put(contentArg.getKey(), contentArg.getValue().toString());%0A%09%09%7D%0A%09%09args.put(FIELD_ORIGIN, message.getOrigin() != null ? message.getOrigin().toString() : %22%22);%0A%09%09args.put(FIELD_SUBJECT, subject);%0A%09%09args.put(FIELD_CONTEXT, message.getContext());%0A%09%09args.put(FIELD_DELIVERY_DURATION, message.getTimestampReceived() - message.getTimestampSend()
 );%0A%09

Add additional fields to message logging and serialize contents