/*
 * events.cpp
 *
 *  Created on: Jan 7, 2018
 *      Author: Philipp Hinz
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "events.h"
#include "logger.h"

/**
 * Struct for subscribers, contains the pointer to the handler
 */

struct subscriber_t {
	void (*handler)(event_t *);
	subscriber_t *next = NULL;
};

/**
 * Struct for the event list
 */

struct events_t {
	char event[EVENT_LENGTH];
	subscriber_t *subscribers = NULL;
	events_t *next = NULL;
};

events_t *firstEvent = NULL; /**< The first element in the event list */

/**
 * Finds the specific event in the list and returns it
 * @param event Event name
 * @return Pointer to event, NULL if not found
 */

events_t * event_find(char *event) {
	events_t *tmp = firstEvent;

	while (tmp) {
		if (!strcmp(tmp->event, event))
			return tmp;
		tmp = tmp->next;
	}
	return NULL;
}

/**
 * Finds the last event in the list and returns it
 * @return Pointer to event, NULL if no event in list
 */

events_t * event_findlast() {
	events_t *tmp = firstEvent;

	while (tmp) {
		if (!tmp->next)
			return tmp;
		tmp = tmp->next;
	}
	return NULL;
}

/**
 * Adds a subscriber to an event
 * @param event Target event
 * @param handler Event handler
 */

void addSubscriber(events_t *event, void (*handler)(event_t *)) {
	subscriber_t *newSub = (subscriber_t *) malloc(sizeof(subscriber_t)); // Cast is neccesarry in c++
	if (!newSub)
		logger_error("Not enough memory\n");
	newSub->handler = handler;
	newSub->next = NULL;

	if (event->subscribers) {
		subscriber_t *tmp = event->subscribers;
		while (tmp->next) {
			tmp = tmp->next;
		}

		tmp->next = newSub;
	} else {
		event->subscribers = newSub;
	}
}

/**
 * Adds a subscriber to an event
 * @param event Target event name
 * @param handler Event handler
 */

void event_subscribe(const char *event, void (*handler)(event_t *)) {
	//TODO change event name to a typedef enum or similar
	events_t *newEvent = (events_t *) malloc(sizeof(events_t));
	if (!newEvent)
		logger_error("Not enough memory\n");
	newEvent->next = NULL;
	newEvent->subscribers = NULL;
	strcpy(newEvent->event, event);
	addSubscriber(newEvent, handler);
	events_t *tmp = event_findlast();
	if (tmp)
		tmp->next = newEvent;
	else
		firstEvent = newEvent;
	logger(V_HAL, "New subscriber to event \"%s\"\n", event);
}

/**
 * Triggers a specific event
 * @param event Event data
 */

void event_trigger(event_t *event) {
	events_t *tmp = event_find(event->event);
	if (!tmp) {
		logger(V_HAL, "No subscribers to event \"%s\"\n", event->event);
		return;
	}

	subscriber_t *sub = tmp->subscribers;

	while (sub) {
		sub->handler(event);
		sub = sub->next;
	}

	logger(V_HAL, "Triggered event \"%s\"\n", event->event);
}

/**
 * Triggers a specific event
 * @param event Name of event
 */

void event_trigger(char *event) {
	event_t e;
	e.event = event;
	event_trigger(&e);
}

/**
 * Triggers a specific event
 * @param event Name of event
 */

void event_trigger(const char *event) {
	event_t e;
	e.event = (char*)event;
	event_trigger(&e);
}

/**
 * Triggers a specific event
 * @param event Name of event
 * @param data Pointer to some data
 * @param len Length of data
 */

void event_trigger(char *event, void *data, int len) {
	event_t e;
	e.event = event;
	e.data = data;
	e.len = len;
	event_trigger(&e);
}

/**
 * Triggers a specific event
 * @param event Name of event
 * @param data Pointer to some data
 * @param len Length of data
 */

void event_trigger(const char *event, void *data, int len) {
	event_trigger((char *) event, data, len);
}

