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

#include "PROJECT_NAME/Atoms.h"
#include "PROJECT_NAME/EntityPushBlock.h"
#include "PROJECT_NAME/RoomState.h"
#include "PROJECT_NAME/Sprites.h"
#include "PROJECT_NAME/Utilities.h"

#define stemobject_implementation EntityPushBlock

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(preadvance);
stemobject_vtable_entry(postadvance);
stemobject_vtable_entry(isEqual);
stemobject_vtable_entry(enumerateProperties);
stemobject_vtable_end();

EntityPushBlock * EntityPushBlock_create(Vector2i position, bool multipush, struct RoomState * roomState) {
	stemobject_create_implementation(init, position, multipush, roomState)
}

static void initComponents(EntityPushBlock * self, Vector2i position, bool multipush) {
	stemobject_assign_vtable(self->collidableComponent, EntityComponent_collidable);
	stemobject_assign_vtable(self->positionComponent, EntityComponent_position);
	stemobject_assign_vtable(self->pushableComponent, EntityComponent_pushable);
	stemobject_assign_vtable(self->spriteComponent, EntityComponent_sprite);
	EntityComponent_collidable_init(&self->collidableComponent, alwaysSolidCollisionCallback, NULL);
	EntityComponent_position_init(&self->positionComponent, position);
	EntityComponent_pushable_init(&self->pushableComponent, multipush);
	EntityComponent_sprite_init(&self->spriteComponent, multipush ? SPRITE_ID_MULTIPUSH_BLOCK : SPRITE_ID_PUSH_BLOCK, DRAW_LAYER_ENTITY, 0);
}

bool EntityPushBlock_init(EntityPushBlock * self, Vector2i position, bool multipush, struct RoomState * roomState) {
	call_super(init, self, roomState);
	initComponents(self, position, multipush);
	return true;
}

EntityPushBlock * EntityPushBlock_createWithEncodedState(struct memreadContext * memreadContext, struct RoomState * roomState) {
	GameEntity_createWithEncodedState_implementation();
}

void EntityPushBlock_initWithEncodedState(EntityPushBlock * self, struct memreadContext * memreadContext, struct RoomState * roomState) {
	initComponents(self, VECTOR2i_ZERO, false);
	call_super_virtual(initWithEncodedState, self, memreadContext, roomState);
	self->positionComponent.lastPosition = self->positionComponent.position;
	self->spriteComponent.spriteID = self->pushableComponent.multipush ? SPRITE_ID_MULTIPUSH_BLOCK : SPRITE_ID_PUSH_BLOCK;
}

EntityPushBlock * EntityPushBlock_copy(EntityPushBlock * self, struct RoomState * roomState) {
	stemobject_copy_implementation(initCopy, roomState)
}

void EntityPushBlock_initCopy(EntityPushBlock * self, compat_type(EntityPushBlock *) originalUntyped, struct RoomState * roomState) {
	call_super_virtual(initCopy, self, originalUntyped, roomState);
	EntityPushBlock * original = originalUntyped;
	initComponents(self, original->positionComponent.position, original->pushableComponent.multipush);
	call_virtual(initCopy, &self->collidableComponent, &original->collidableComponent);
	call_virtual(initCopy, &self->positionComponent, &original->positionComponent);
	call_virtual(initCopy, &self->pushableComponent, &original->pushableComponent);
	call_virtual(initCopy, &self->spriteComponent, &original->spriteComponent);
}

void EntityPushBlock_dispose(EntityPushBlock * self) {
	call_super_virtual(dispose, self);
}

compat_type(GameEntityComponent *) EntityPushBlock_getComponent(EntityPushBlock * self, ComponentType type) {
	switch (type) {
		case COMPONENT_COLLIDABLE:
			return &self->collidableComponent;
		case COMPONENT_POSITION:
			return &self->positionComponent;
		case COMPONENT_PUSHABLE:
			return &self->pushableComponent;
		case COMPONENT_SPRITE:
			return &self->spriteComponent;
		default:
			break;
	}
	return NULL;
}

void EntityPushBlock_preadvance(EntityPushBlock * self) {
	call_virtual(update, &self->positionComponent, (GameEntity *) self);
	call_virtual(update, &self->pushableComponent, (GameEntity *) self);
}

void EntityPushBlock_postadvance(EntityPushBlock * self) {
	if (self->pushableComponent.pushed) {
		EventDispatcher_dispatchEvent(self->roomState->eventDispatcher, ATOM_event_block_pushed, self);
	}
}

bool EntityPushBlock_isEqual(EntityPushBlock * self, EntityPushBlock * compare) {
	return self->positionComponent.position.x == compare->positionComponent.position.x &&
	       self->positionComponent.position.y == compare->positionComponent.position.y;
}

void EntityPushBlock_enumerateProperties(EntityPushBlock * self, bool transient, GameEntity_enumeratePropertiesCallback callback, void * context) {
	call_super_virtual(enumerateProperties, self, transient, callback, context);
	callback(&self->positionComponent.position, PROPERTY_TYPE_Vector2i_int8_t, context);
	if (!transient) {
		callback(&self->pushableComponent.multipush, PROPERTY_TYPE_bool, context);
	}
}
