#ifndef GAME_H
#define GAME_H
#include <stddef.h>
#include <stdbool.h>
#include <assert.h>
#include <helper.h>
#include <darray.h>
#define MAXITEMS 10

extern unsigned m_id;

enum objtype {NOOBJ = 0, MONSTER, ITEM, WALL, TARGET};
enum itemtype {NOITEM = 0, EQUIP, USE};

struct equip
{
	unsigned hp;
	unsigned dmg;
	bool isequipped;
};

struct use
{
	unsigned restorehp;
};

struct item
{
	const char *name;
	enum itemtype type;
	union
	{
		struct equip e;
		struct use u;
	}u;
};


struct monster
{
	const char *name;
	unsigned id;
	unsigned current_hp;
	unsigned max_hp;
	unsigned dmg;
	unsigned level;
	unsigned xp;
	darray(struct item) items;
	bool isplayer;
};

static inline size_t item_len(const struct monster *m)
{
	for(size_t i = 0; i < MAXITEMS; i++)
	{
		if(darray_item(m->items, i).type == NOITEM)
			return i;
	}
	return MAXITEMS;
}


struct object
{
	enum objtype type;
	char symbol;
	union
	{
		struct monster m;
		struct item i;
	} u;
};

struct game
{
	struct object *map;
	size_t xs, ys;
};

static inline struct object *game_at(const struct game *g, struct point p)
{
	return &g->map[p.y * g->xs + p.x];
}


static inline struct game create_game(size_t y, size_t x)
{
	struct game ret;
	ret.map = xmalloc(x * y * sizeof * ret.map);
	ret.xs = x;
	ret.ys = y;

	return ret;
}

static inline void delete_game(struct game *g)
{
	for(size_t y = 0; y < g->ys; y++)
	{
		for(size_t x = 0; x < g->xs; x++)
		{
			if(game_at(g, (struct point){y,x})->type == MONSTER)
				darray_free(game_at(g, (struct point){y,x})->u.m.items);
		}
	}
	free(g->map);
}

enum action {MOVE, SPAWN, QUIT, INVENTORY, REDRAW, INVALID};
enum direction {UP,DOWN,LEFT,RIGHT};

struct choice
{
	enum action a;
	union
	{
		enum direction d;
	}u;
};

#define MAX(x,y) (x >= y ? x : y)
#define MIN(x,y) (x <= y ? x : y)

#define ORC (struct object)\
			{\
				.type = MONSTER,\
				.symbol = 'O',\
				.u.m = {"Zog Zog", m_id++, 10, 10, 1, 5 , 0, darray_new(), false}\
			}

#define EMPTYFIELD  (struct object)\
					{\
						.type = NOOBJ,\
						.symbol = ' ',\
					}
#define WALLFIELD (struct object)\
				{\
					.type = WALL,\
					.symbol = '#',\
				}

static inline struct point relative(struct point p, enum direction d)
{
	switch(d)
	{
	case UP:
		return (struct point){p.y - 1, p.x};
	case DOWN:
		return (struct point){p.y + 1, p.x};
	case LEFT:
		return (struct point){p.y, p.x - 1};
	case RIGHT:
		return (struct point){p.y, p.x + 1};
	default:
		assert(false);
	}
}

static inline bool in_bounds(struct game *g, struct point p)
{
	if(p.y >= 0 && p.y < g->ys && p.x >= 0 && p.x < g->xs)
		return true;
	else
		return false;
}

static inline void spawn_enemy(struct game *g)
{
	while(true)
	{
		struct point p = {randrange(0, g->ys), randrange(0, g->xs)};
		if(in_bounds(g, p) && game_at(g, p)->type == NOOBJ) {
			*game_at(g, p) = ORC;
			break;
		}
	}
}


#endif
