// Copyright (c) 2023 Alex Diener. All rights reserved.

#include "PROJECT_NAME/GameEntity.h"
#include "PROJECT_NAME/RoomState.h"
#include "PROJECT_NAME/SharedDefinitions.h"
#include "PROJECT_NAME/Sprites.h"
#include "PROJECT_NAME/Utilities.h"
#include <stdio.h>

#define stemobject_implementation GameEntity

stemobject_vtable_begin();
stemobject_vtable_entry(createWithEncodedState);
stemobject_vtable_entry(initWithEncodedState);
stemobject_vtable_entry(dispose);
stemobject_vtable_entry(copy);
stemobject_vtable_entry(initCopy);
stemobject_vtable_entry(postinit);
stemobject_vtable_entry(getComponent);
stemobject_vtable_entry(roomEntered);
stemobject_vtable_entry(roomExited);
stemobject_vtable_entry(preadvance);
stemobject_vtable_entry(advanceTurn);
stemobject_vtable_entry(postadvance);
stemobject_vtable_entry(isEqual);
stemobject_vtable_entry(encodeState);
stemobject_vtable_entry(xorState);
stemobject_vtable_entry(enumerateProperties);
stemobject_vtable_entry(undoActionPerformed);
stemobject_vtable_end();

bool GameEntity_init(GameEntity * self, struct RoomState * roomState) {
	static unsigned int nextEntityID;
	call_super(init, self);
	self->roomState = roomState;
	self->entityID = nextEntityID++;
	self->markedForRemoval = false;
	return true;
}

GameEntity * GameEntity_createWithEncodedState(struct memreadContext * memreadContext, struct RoomState * roomState) {
#ifdef DEBUG
	fprintf(stderr, "GameEntity_createWithEncodedState() must be overridden\n");
#endif
	abort();
	return NULL;
}

static void initWithEncodedStateCallback(void * address, GameEntity_propertyType type, void * context) {
	struct memreadContext * memreadContext = context;
	switch (type) {
		case PROPERTY_TYPE_Vector2i_int8_t: {
			Vector2i * value = address;
			memreadPrimitive(memreadContext, value->x, int8_t);
			memreadPrimitive(memreadContext, value->y, int8_t);
			break;
		}
		case PROPERTY_TYPE_bool: {
			bool * value = address;
			memreadPrimitive(memreadContext, *value, uint8_t);
			break;
		}
		case PROPERTY_TYPE_uint8_t: {
			uint8_t * value = address;
			memreadPrimitive(memreadContext, *value, uint8_t);
			break;
		}
		case PROPERTY_TYPE_uint16_t: {
			uint16_t * value = address;
			memreadPrimitive(memreadContext, *value, uint16_t);
			break;
		}
		case PROPERTY_TYPE_uint32_t: {
			uint32_t * value = address;
			memreadPrimitive(memreadContext, *value, uint32_t);
			break;
		}
	}
}

void GameEntity_initWithEncodedState(GameEntity * self, struct memreadContext * memreadContext, struct RoomState * roomState) {
	call_super(init, self);
	self->roomState = roomState;
	self->markedForRemoval = false;
	call_virtual(enumerateProperties, self, false, initWithEncodedStateCallback, memreadContext);
}

void GameEntity_dispose(GameEntity * self) {
	call_super_virtual(dispose, self);
}

GameEntity * GameEntity_copy(GameEntity * self, struct RoomState * roomState) {
#ifdef DEBUG
	fprintf(stderr, "GameEntity_copy() must be overridden\n");
#endif
	abort();
	return NULL;
}

void GameEntity_initCopy(GameEntity * self, compat_type(GameEntity *) originalUntyped, struct RoomState * roomState) {
	call_super(init, self);
	GameEntity * original = originalUntyped;
	self->entityID = original->entityID;
	self->roomState = roomState;
	self->markedForRemoval = original->markedForRemoval;
}

void GameEntity_postinit(GameEntity * self) {
}

compat_type(GameEntityComponent *) GameEntity_getComponent(GameEntity * self, ComponentType type) {
	return NULL;
}

void GameEntity_roomEntered(GameEntity * self) {
}

void GameEntity_roomExited(GameEntity * self) {
}

void GameEntity_preadvance(GameEntity * self) {
}

void GameEntity_advanceTurn(GameEntity * self) {
}

void GameEntity_postadvance(GameEntity * self) {
}

bool GameEntity_isEqual(GameEntity * self, GameEntity * compare) {
	return true;
}

static void encodeStateCallback(void * address, GameEntity_propertyType type, void * context) {
	struct memwriteContext * memwriteContext = context;
	switch (type) {
		case PROPERTY_TYPE_Vector2i_int8_t: {
			Vector2i * value = address;
			memwritePrimitive(memwriteContext, value->x, int8_t);
			memwritePrimitive(memwriteContext, value->y, int8_t);
			break;
		}
		case PROPERTY_TYPE_bool: {
			bool * value = address;
			memwritePrimitive(memwriteContext, *value, uint8_t);
			break;
		}
		case PROPERTY_TYPE_uint8_t: {
			uint8_t * value = address;
			memwritePrimitive(memwriteContext, *value, uint8_t);
			break;
		}
		case PROPERTY_TYPE_uint16_t: {
			uint16_t * value = address;
			memwritePrimitive(memwriteContext, *value, uint16_t);
			break;
		}
		case PROPERTY_TYPE_uint32_t: {
			uint32_t * value = address;
			memwritePrimitive(memwriteContext, *value, uint32_t);
			break;
		}
	}
}

void GameEntity_encodeState(GameEntity * self, struct memwriteContext * memwriteContext, bool transient) {
	call_virtual(enumerateProperties, self, transient, encodeStateCallback, memwriteContext);
}

static void xorStateCallback(void * address, GameEntity_propertyType type, void * context) {
	struct memreadContext * memreadContext = context;
	switch (type) {
		case PROPERTY_TYPE_Vector2i_int8_t: {
			Vector2i * value = address;
			memreadXorPrimitive(memreadContext, value->x, int8_t);
			memreadXorPrimitive(memreadContext, value->y, int8_t);
			break;
		}
		case PROPERTY_TYPE_bool: {
			bool * value = address;
			memreadXorPrimitive(memreadContext, *value, uint8_t);
			break;
		}
		case PROPERTY_TYPE_uint8_t: {
			uint8_t * value = address;
			memreadXorPrimitive(memreadContext, *value, uint8_t);
			break;
		}
		case PROPERTY_TYPE_uint16_t: {
			uint16_t * value = address;
			memreadXorPrimitive(memreadContext, *value, uint16_t);
			break;
		}
		case PROPERTY_TYPE_uint32_t: {
			uint32_t * value = address;
			memreadXorPrimitive(memreadContext, *value, uint32_t);
			break;
		}
	}
}

void GameEntity_xorState(GameEntity * self, struct memreadContext * memreadContext) {
	call_virtual(enumerateProperties, self, true, xorStateCallback, memreadContext);
}

void GameEntity_enumerateProperties(GameEntity * self, bool transient, GameEntity_enumeratePropertiesCallback callback, void * context) {
	if (!transient) {
		callback(&self->entityID, PROPERTY_TYPE_uint32_t, context);
	}
}

void GameEntity_undoActionPerformed(GameEntity * self) {
}
