#ifndef __STATES_H__
#define __STATES_H__

#include <linux/module.h>
#include <linux/atomic.h>

struct event;

enum {
	/*
	 * This flag marks events as temporary.
	 *
	 * Thos are only used to trigger callbacks. And have the following odd
	 * semantics:
	 *  - They will not appear in event_state
	 *  - They will not conceal calls with the same value
	 *
	 * They have been added to support the callback usage for
	 * ab_onhook//offhook/fehler/active. Sadly this is the only usage and
	 * new users of this feature are STRONGLY DISCOURAGED!
	 */
	EVENT_FLAG_TRANSIENT = (1 << 0),
};

typedef void (*event_callback)(struct event *event, int old, int new);

struct event_member_def {
	const char *name;
	int event_id;

	struct {
		int ms;
		int next;
	} timeout;
};

struct event {
	const char *name;

	int max_state;
	const struct event_member_def *def;

	// Value
	atomic_t current_value;
	atomic_t pending_value;

	// Timeout related
	int timeout_setup;
	unsigned long timeout;

	//
	int raw_event_id;

	//
	int flags;

	// Callback on value update
	event_callback callback;
};

/*
 * Now include all events, which are basically states
 */
#define DEFINE_EVENT_STATE(_name, ...)       \
	enum _name##_values{                 \
		_name##_unknown = 0,         \
		__VA_ARGS__                  \
		_name##_invalid,             \
	};                                   \
	extern struct event __event_##_name; \
	static struct event *const _name = &__event_##_name;
#define DEFINE_EVENT(_name, ...) _name,
#define DEFINE_EVENT_RAW(_name, ...)         \
	extern struct event __event_##_name; \
	static struct event *const _name = &__event_##_name;
#define DEFINE_EVENT_RAW_INTERNAL(_name, ...) DEFINE_EVENT_RAW(_name)
#include <avm/hui/_events.h>

static inline int event_get_value(struct event *event)
{
	return atomic_read(&event->current_value);
}

/*
 * Will be called during the hui update cycle.
 *
 * Unless EVENT_FLAG_TRANSIENT is specified, will only be called if the value
 * changed.
 */
static inline void event_set_callback(struct event *event, event_callback cb)
{
	event->callback = cb;
}

static inline int event_is_transient(const struct event *event)
{
	return event->flags & EVENT_FLAG_TRANSIENT;
}

#endif