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

#include "PROJECT_NAME/Atoms.h"
#include "PROJECT_NAME/EntityComponent_attack.h"
#include "PROJECT_NAME/EntityDestructibleTile.h"
#include "PROJECT_NAME/RoomState.h"
#include "PROJECT_NAME/Sprites.h"
#include "PROJECT_NAME/Utilities.h"

#define stemobject_implementation EntityDestructibleTile

stemobject_vtable_begin();
stemobject_vtable_entry(createWithEncodedState);
stemobject_vtable_entry(initWithEncodedState);
stemobject_vtable_entry(copy);
stemobject_vtable_entry(initCopy);
stemobject_vtable_entry(dispose);
stemobject_vtable_entry(getComponent);
stemobject_vtable_entry(isEqual);
stemobject_vtable_entry(enumerateProperties);
stemobject_vtable_entry(undoActionPerformed);
stemobject_vtable_end();

EntityDestructibleTile * EntityDestructibleTile_create(Vector2i position, bool destroyed, struct RoomState * roomState) {
	stemobject_create_implementation(init, position, destroyed, roomState)
}

static void destroyTile(EntityDestructibleTile * self) {
	self->destroyed = true;
	self->spriteComponent.spriteID = SPRITE_ID_WALL_DESTROYED;
	self->spriteComponent.drawLayer = DRAW_LAYER_FLOOR;
	EventDispatcher_dispatchEvent(self->roomState->eventDispatcher, ATOM_event_tile_destroyed, self);
}

static bool collisionCallback(struct GameEntity * collidingEntity, Vector2i fromDirection, void * context) {
	EntityDestructibleTile * self = context;
	EntityComponent_attack * attackComponent = call_virtual(getComponent, collidingEntity, COMPONENT_ATTACK);
	if (attackComponent != NULL && (attackComponent->type & ATTACK_DESTROY_WALL)) {
		destroyTile(self);
		if (attackComponent->type & ATTACK_OVERRIDE_COLLISION) {
			return false;
		}
	}
	return true;
}

static void updateSprite(EntityDestructibleTile * self) {
	self->spriteComponent.spriteID = self->destroyed ? SPRITE_ID_WALL_DESTROYED : SPRITE_ID_WALL_CRUMBLY;
}

static void initComponents(EntityDestructibleTile * self, Vector2i position) {
	stemobject_assign_vtable(self->positionComponent, EntityComponent_position);
	stemobject_assign_vtable(self->spriteComponent, EntityComponent_sprite);
	stemobject_assign_vtable(self->collidableComponent, EntityComponent_collidable);
	EntityComponent_position_init(&self->positionComponent, position);
	EntityComponent_sprite_init(&self->spriteComponent, SPRITE_ID_WALL_CRUMBLY, DRAW_LAYER_WALL, 0);
	EntityComponent_collidable_init(&self->collidableComponent, collisionCallback, self);
}

bool EntityDestructibleTile_init(EntityDestructibleTile * self, Vector2i position, bool destroyed, struct RoomState * roomState) {
	call_super(init, self, roomState);
	self->destroyed = destroyed;
	initComponents(self, position);
	updateSprite(self);
	return true;
}

EntityDestructibleTile * EntityDestructibleTile_createWithEncodedState(struct memreadContext * memreadContext, struct RoomState * roomState) {
	GameEntity_createWithEncodedState_implementation();
}

void EntityDestructibleTile_initWithEncodedState(EntityDestructibleTile * self, struct memreadContext * memreadContext, struct RoomState * roomState) {
	initComponents(self, VECTOR2i_ZERO);
	call_super_virtual(initWithEncodedState, self, memreadContext, roomState);
	self->positionComponent.lastPosition = self->positionComponent.position;
	updateSprite(self);
}

EntityDestructibleTile * EntityDestructibleTile_copy(EntityDestructibleTile * self, struct RoomState * roomState) {
	stemobject_copy_implementation(initCopy, roomState)
}

void EntityDestructibleTile_initCopy(EntityDestructibleTile * self, compat_type(EntityDestructibleTile *) originalUntyped, struct RoomState * roomState) {
	call_super_virtual(initCopy, self, originalUntyped, roomState);
	EntityDestructibleTile * original = originalUntyped;
	self->destroyed = original->destroyed;
	initComponents(self, original->positionComponent.position);
	call_virtual(initCopy, &self->positionComponent, &original->positionComponent);
	call_virtual(initCopy, &self->spriteComponent, &original->spriteComponent);
	call_virtual(initCopy, &self->collidableComponent, &original->collidableComponent);
	self->collidableComponent.callbackContext = self;
	updateSprite(self);
}

void EntityDestructibleTile_dispose(EntityDestructibleTile * self) {
	call_super_virtual(dispose, self);
}

compat_type(GameEntityComponent *) EntityDestructibleTile_getComponent(EntityDestructibleTile * self, ComponentType type) {
	switch (type) {
		case COMPONENT_COLLIDABLE:
			if (!self->destroyed) {
				return &self->collidableComponent;
			}
			break;
		case COMPONENT_POSITION:
			return &self->positionComponent;
		case COMPONENT_SPRITE:
			return &self->spriteComponent;
		default:
			break;
	}
	return NULL;
}

bool EntityDestructibleTile_isEqual(EntityDestructibleTile * self, EntityDestructibleTile * compare) {
	return self->destroyed == compare->destroyed;
}

void EntityDestructibleTile_enumerateProperties(EntityDestructibleTile * self, bool transient, GameEntity_enumeratePropertiesCallback callback, void * context) {
	call_super_virtual(enumerateProperties, self, transient, callback, context);
	if (!transient) {
		callback(&self->positionComponent.position, PROPERTY_TYPE_Vector2i_int8_t, context);
	}
	callback(&self->destroyed, PROPERTY_TYPE_bool, context);
}

void EntityDestructibleTile_undoActionPerformed(EntityDestructibleTile * self) {
	updateSprite(self);
}
