#include <stdio.h>

typedef enum {
	INFO,
	ERROR,
} Level;

typedef struct {
	const char *(*name)(void *self);
	void (*log)(void *self, Level level, const char *msg);
	void (*info)(void *self, const char *msg);
	void (*error)(void *self, const char *msg);
} LoggerVtable;

/**
 * fat pointer
 */
typedef struct {
	void *data;
	const LoggerVtable *vtable;
} DynLogger;

// dyn Trait impl
static const char *logger_name(void *self)
{
	DynLogger *lg = self;
	return lg->vtable->name(lg->data);
}
static void logger_log(void *self, Level level, const char *msg)
{
	DynLogger *lg = self;
	lg->vtable->log(lg->data, level, msg);
}
static void logger_info(void *self, const char *msg)
{
	DynLogger *lg = self;
	lg->vtable->info(lg->data, msg);
}
static void logger_error(void *self, const char *msg)
{
	DynLogger *lg = self;
	lg->vtable->error(lg->data, msg);
}

/**
 * monomorphization helper
 */
#define DEFAULT_IMPL_INFO(TYPE, LOG_FN)                           \
	static void TYPE##_info_impl(void *self, const char *msg) \
	{                                                         \
		LOG_FN(self, INFO, msg);                          \
	}

#define DEFAULT_IMPL_ERROR(TYPE, LOG_FN)                           \
	static void TYPE##_error_impl(void *self, const char *msg) \
	{                                                          \
		LOG_FN(self, ERROR, msg);                          \
	}

typedef struct {
	int count;
	const char *prefix;
} SimpleLogger;

// impl Logger for SimpleLogger
static const char *simple_logger_name_impl(void *self)
{
	return "simple logger";
}
static void simple_logger_log_impl(void *self, Level level, const char *msg)
{
	SimpleLogger *lg = self;
	lg->count += 1;

	const char *level_msg;
	switch (level) {
	case INFO:
		level_msg = "INFO";
		break;
	case ERROR:
		level_msg = "ERROR";
		break;
	}

	printf("%s %s %s: %s\ncounts: %d\n", lg->prefix,
	       simple_logger_name_impl(lg), level_msg, msg, lg->count);
}

/**
 * default implementations
 */
DEFAULT_IMPL_INFO(simple_logger, simple_logger_log_impl)

DEFAULT_IMPL_ERROR(simple_logger, simple_logger_log_impl)

static const LoggerVtable SIMPLE_LOGGER_VT = {
	.name = simple_logger_name_impl,
	.log = simple_logger_log_impl,

	// generated by macros
	.info = simple_logger_info_impl,
	.error = simple_logger_error_impl,
};

SimpleLogger new_simple_logger(const char *prefix)
{
	SimpleLogger lg = { .count = 0, .prefix = prefix };
	return lg;
}

typedef struct {
	const char *prefix;
} ColorLogger;

static const char *color_logger_name_impl(void *self)
{
	return "color logger";
}

static void color_logger_log_impl(void *self, Level level, const char *msg)
{
	ColorLogger *c = self;

	const char *level_msg;
	switch (level) {
	case INFO:
		level_msg = "INFO";
		break;
	case ERROR:
		level_msg = "ERROR";
		break;
	}
	printf("%s %s %s: %s\n", c->prefix, color_logger_name_impl(self),
	       level_msg, msg);
}

// use default impl
DEFAULT_IMPL_INFO(color_logger, color_logger_log_impl)

// override
static void color_logger_error_impl(void *self, const char *msg)
{
	ColorLogger *c = self;
	printf("%s %s \033[0;31mERROR\033[0m: %s\n", c->prefix,
	       color_logger_name_impl(self), msg);
}

static const LoggerVtable COLOR_LOGGER_VT = {
	.name = color_logger_name_impl,
	.log = color_logger_log_impl,
	.info = color_logger_info_impl,
	.error = color_logger_error_impl,
};

ColorLogger new_color_logger(const char *prefix)
{
	ColorLogger d = { .prefix = prefix };
	return d;
}

int main()
{
	SimpleLogger sp = new_simple_logger("Nya~");
	ColorLogger cl = new_color_logger("Meow~");
	DynLogger logger[] = { { .data = &sp, .vtable = &SIMPLE_LOGGER_VT },
			       { .data = &cl, .vtable = &COLOR_LOGGER_VT } };
	for (int i = 0; i < 2; ++i) {
		logger_info(logger + i, "starts");
		logger_error(logger + i, "error happened");
	}
}
