#include "global.h"

#include "constants/ability.h"
#include "code_803E46C.h"
#include "code_8045A00.h"
#include "dungeon_message.h"
#include "code_806CD90.h"
#include "code_807CD9C.h"
#include "structs/str_dungeon.h"
#include "code_8077274_1.h"
#include "dungeon_ai.h"
#include "dungeon_engine.h"
#include "dungeon_movement.h"
#include "dungeon_pokemon_attributes.h"
#include "dungeon_map_access.h"
#include "dungeon_util.h"
#include "math.h"
#include "move_effects_target.h"
#include "moves.h"
#include "move_util.h"
#include "code_8041AD0.h"

extern u32 gUnknown_202EDCC;

extern s16 gUnknown_80F4F90;
extern u8 *gUnknown_80FCA10[];
extern u8 *gUnknown_80F8A0C[];
extern u8 *gUnknown_80FCBCC[];
extern u8 *gUnknown_80FC9E8[];
extern u8 *gUnknown_80FCB14[];
extern u8 *gUnknown_80FCAE8[];
extern u8 *gUnknown_80FC97C[];
extern u8 *gUnknown_80FC9A0[];
extern u8 *gUnknown_80FC584[];
extern u8 *gUnknown_80FD320[];
extern u8 *gUnknown_80FD2F8[];

bool8 ExposeTrap(s32 x, s32 y);
void sub_8040A84();
void sub_8049ED4();
void sub_806A5B8(Entity *);
u8 sub_8045888(Entity *);
void sub_807EC28(u32);
void sub_80694C0(Entity *, s32, s32, s32);
void sub_807D068(Entity *, DungeonPos *);
u8 sub_8044B28(void);
extern void sub_806F370(Entity *pokemon, Entity *target, u32, u32, u8 *, u8 moveType, s32, u32, u32, u32);
void sub_80421AC(Entity * pokemon, Entity * target);
void sub_807BB78(Entity *pokemon);
extern void sub_803F580(u32);
bool8 sub_808384C(DungeonPos *, DungeonPos *);
u8 sub_8083660(DungeonPos *);

void sub_807CD9C(Entity *pokemon, Entity *target, u32 direction)
{
    const Tile *tile;
    int iVar8;
    bool8 flag;
    DungeonPos sp_0x18;
    Move move;
    int sp_0x24;
    Entity *sp_0x28;
    PixelPos sp_0x2C;

    sp_0x24 = 10;
    if (IsCurrentFixedRoomBossFight()) {
        TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FC9E8); // It couldn't be knocked flying!
        return;
    }
    else
    {
        if (pokemon == target) {
            SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0],target,0);
            TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FCA10); // {POKEMON_0} couldn't be knocked flying!
            return;
        }
        SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0],target,0);
        if (HasAbility(target,ABILITY_SUCTION_CUPS)) {
            TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FCBCC); // {POKEMON_0} is anchored! It can't be knocked flying!
            return;
        }
        else
        {
            (GetEntInfo(target)->action).direction = (direction + 4) & DIRECTION_MASK;
            sub_806CDD4(target, 6, (direction + 4) & DIRECTION_MASK);
            sub_80421C0(target,0x1a3);
            TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80F8A0C); // {POKEMON_0} was blown away!
            flag = TRUE;
            sp_0x28 = NULL;

            for(; sp_0x24 > 0; sp_0x24--)
            {
                sp_0x18 = target->pos;
                sp_0x18.x += gAdjacentTileOffsets[direction].x;
                sp_0x18.y += gAdjacentTileOffsets[direction].y;
                if ((((sp_0x18.x < 0) || (sp_0x18.y < 0)) || (DUNGEON_MAX_SIZE_X <= sp_0x18.x)) ||
                    (DUNGEON_MAX_SIZE_Y <= sp_0x18.y)) goto _0807CF20;
                sp_0x2C.x = gAdjacentTileOffsets[direction].x << 0xA;
                sp_0x2C.y = gAdjacentTileOffsets[direction].y << 0xA;

                for(iVar8 = 0; iVar8 < 6; iVar8++)
                {
                    IncreaseEntityPixelPos(target,sp_0x2C.x,sp_0x2C.y);
                    if (sub_8045888(target) != 0) {
                        sub_803E46C(0x19);
                    }
                }
                tile = GetTile(sp_0x18.x, sp_0x18.y);
                if (tile->monster != NULL) {
                    sp_0x28 = tile->monster;
                    goto _0807CF26;
                }
                if (sub_80705F0(target,&sp_0x18)){
                    flag = (tile->terrainType & 3) == 0 ? FALSE : TRUE;
                    goto _0807CF20;
                }
                sub_80694C0(target,sp_0x18.x,sp_0x18.y,1);
            }


            flag = TRUE;
        _0807CF20:
            if (!flag) {
            _0807CF26:

                sub_807D068(target,&target->pos);
                sub_806F370(pokemon,target,gUnknown_80F4F90,0,0,0,0x219,0,0,0);
            }
            if (sub_8044B28()) {
                return;
            }
            if (sp_0x28 != NULL) {
                InitPokemonMove(&move,0x163);
                if (!sub_80571F0(sp_0x28,&move)) {
                    TrySendImmobilizeSleepEndMsg(pokemon,sp_0x28);
                    sub_806F370(pokemon,sp_0x28,gUnknown_80F4F90,0,0,0,0x21a,0,0,0);
                }
            }
            if (sub_8044B28()) {
                return;
            }
            if (!EntityExists(target)) {
                return;
            }
            sub_806A5B8(target);
            if (sub_80706A4(target,&target->pos)) {
                sub_807D148(target,target,0,0);
            }
            if (!EntityExists(target)) {
                return;
            }
            sub_806CE68(target,8);
            if (GetEntInfo(target)->isTeamLeader) {
                gDungeon->unk1 = 0;
                gDungeon->unk5C0 = -1;
                sub_804AC20(&target->pos);
                sub_807EC28(FALSE);
            }
            sub_806A5B8(target);
            sub_8075900(target,gDungeon->unk3A08);
        }
    }
}

void sub_807D068(Entity *pokemon, DungeonPos *pos)
{
    int pixelX;
    int posY;
    int pixelY;
    int posX;
    int counter;
    int iVar8;
    s32 diff;
    PixelPos local_34;
    int incrementX;
    int incrementY;

    posX = pos->x * 0x1800 + 0xc00;
    posY = pos->y * 0x1800 + 0x1000;
    diff = (pokemon->pixelPos).x - posX;
    if (diff < 0) {
        diff = -diff;
    }
    if (diff < 0xc) {
        diff = (pokemon->pixelPos).y - posY;
        if (diff < 0) {
            diff = -diff;
        }
        if (diff < 0xc) goto _0807D11E;
    }
    iVar8 = 0;
    pixelX = (pokemon->pixelPos).x;
    pixelY = (pokemon->pixelPos).y;
    incrementX = (posX - pixelX) / 0xc;
    incrementY = (posY - pixelY) / 0xc;

    for(counter = 0; counter < 0xC; counter++)
    {
        pixelX += incrementX;
        pixelY += incrementY;
        local_34.x = pixelX;
        local_34.y = pixelY;
        pokemon->unk1C = sin_4096(iVar8) * 0xc;
        sub_804535C(pokemon, &local_34);
        if (sub_8045888(pokemon)) {
            sub_803E46C(0x1a);
        }
        iVar8 = iVar8 + 0xaa;
    }
_0807D11E:
    local_34.x = posX;
    local_34.y = posY;
    pokemon->unk1C = 0;
    sub_804535C(pokemon,&local_34);
    sub_803E46C(0x1a);
}

void sub_807D148(Entity *pokemon, Entity *target, u32 param_3,
                 DungeonPos *pos)
{
    EntityInfo *info;
    u32 direction;
    DungeonPos local_2c;
    DungeonPos pos2;
    bool8 flag;

    info = GetEntInfo(target);
    flag = FALSE;

    SubstitutePlaceholderStringTags(gFormatBuffer_Monsters[0],target,0);
    if (HasAbility(target,ABILITY_SUCTION_CUPS)) {
        TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FCAE8);
        return;
    }
    if (IsCurrentFixedRoomBossFight()) {
        TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FC97C);
        return;
    }
    if (param_3 == 1)
    {
        pos2 = gDungeon->unkE21C;
        if(pos2.x == target->pos.x && pos2.y == target->pos.y) {
            TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FC9A0); // It's already on the stairs!
            PetrifiedStatusTarget(pokemon,target);
            return;
        }
    }

    TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FC584); // $m0 warped!
    TrySendImmobilizeSleepEndMsg(target,target);
    sub_80421AC(pokemon,target);
    if (sub_8045888(target)) {
        direction = (info->action).direction;
        target->unk1C += + 0x800;
        while ( target->unk1C < 0xa000) {
            if ((gUnknown_202EDCC & 3) == 0) {
                direction = (direction + 1) & DIRECTION_MASK;
                info->action.direction = direction & DIRECTION_MASK;
                sub_806CE68(target,direction);
            }
            sub_803E46C(0x22);
            target->unk1C += 0x800;
        }
    }


    switch(param_3)
    {
        default:
        case 0:
            if (!sub_8083660(&local_2c)) {
                local_2c = target->pos;
                flag = TRUE;
            }
            break;
        case 1:
            if (!sub_808384C(&local_2c,&gDungeon->unkE21C)) {
                local_2c = target->pos;
                flag = TRUE;
            }
            break;
        case 2:
            if (!sub_808384C(&local_2c,pos)) {
                local_2c = target->pos;
                flag = TRUE;
            }
            break;
        case 3:
            local_2c = *pos;
            break;

    }

    sub_80694C0(target,local_2c.x,local_2c.y,1);
    sub_804535C(target,0);
    sub_807BB78(target);
    sub_803F580(1);
    if (sub_8045888(target)) {
        direction = (info->action).direction;
        target->unk1C = 0x9c00;
        do {
            if ((gUnknown_202EDCC & 3) == 0) {
                direction = (direction + 1) & DIRECTION_MASK;
                info->action.direction = direction & DIRECTION_MASK;
                sub_806CE68(target,direction);
            }
            sub_803E46C(0x22);
            target->unk1C -= 0x400;
        } while (0 < target->unk1C);
    }
    target->unk1C = 0;
    sub_803E46C(0x22);
    if (flag) {
        TryDisplayDungeonLoggableMessage3(pokemon,target,*gUnknown_80FCB14); // But it dropped back at the same spot!
    }
    if (param_3 == 1) {
        PetrifiedStatusTarget(pokemon,target);
    }
    if (info->isTeamLeader) {
        sub_804AC20(&target->pos);
        gDungeon->unk1 = 0;
        gDungeon->unk5C0 = -1;
        sub_807EC28(0);
    }
    sub_806A5B8(target);
    sub_8075900(target,gDungeon->unk3A08);
}

void sub_807D3CC(Entity *param_1)
{
    u32 room;
    u8 flag;
    int x;
    int y;
    s32 range;
    RoomData *roomData;

    flag = FALSE;
    range = gDungeon->unk181e8.visibilityRange;
    if (range == 0) {
        range = 2;
    }
    room = GetEntityRoom(param_1);

    if (room == CORRIDOR_ROOM) {
        for (y = (param_1->pos).y - range; y <= ((param_1->pos).y + range); y++)
        {
            for (x = (param_1->pos).x - range; x <= ((param_1->pos).x + range); x++) {
                flag |= ExposeTrap(x,y);
            }
        }
    }
    else {
        roomData = &gDungeon->roomData[room];
        for (y = roomData->bottomRightCornerY + -1; y <= roomData->topLeftCornerY + 1; y++) {
            for (x = roomData->bottomRightCornerX + -1; x <= roomData->topLeftCornerX + 1; x++) {
                flag |= ExposeTrap(x,y);
            }
        }
    }
    if (flag) {
        LogMessageByIdWithPopupCheckUser(param_1,*gUnknown_80FD2F8); // All traps were exposed
        sub_8040A84();
        sub_8049ED4();
    }
    else {
        LogMessageByIdWithPopupCheckUser(param_1,*gUnknown_80FD320); // There appears to be no hidden traps.
    }
}

bool8 ExposeTrap(s32 x,s32 y)
{
    const Tile *tile;
    Entity *trap;

    tile = GetTile(x,y);
    trap = tile->object;
    if (((trap != NULL) && (GetEntityType(trap) == ENTITY_TRAP)) && (!trap->isVisible)) {
        trap->isVisible = TRUE;
        return TRUE;
    }
    else {
        return FALSE;
    }
}
