#include "enemy.h"

#include "constants.h"
#include "player.h"
#include "level.h"

enemy_base::enemy_base(RNG *rng, const feature enabled_features,
                       const enum race &nrace, const position &pos,
                       const int gen_room_num, std::string abbrev):
        character{rng, enabled_features, nrace, pos},
        room_num{gen_room_num}, abbrev{abbrev} {}

int enemy_base::get_room_num() const {
        return room_num;
}

long_result enemy_base::act(level *lvl, character *pc, bool hostile) {
        if (is_adjacent(pos, pc->get_pos()) && hostile)
                return attack(pc);

        if (enabled_features & FEATURE_ENEMIES_CHASE && hostile &&
            distance_sqr(pos, pc->get_pos()) <= DIST_THRESHOLD_SQR) {
                position tmp;
                position target = {BIG_NUMBER, BIG_NUMBER};

                for (int i = 0; i < DIRECTION_CNT; ++i) {
                        tmp = pos + MOVE[i];

                        if (((enabled_features & FEATURE_DOORS) ? true
                             : lvl->get_room(tmp) == room_num) &&
                            lvl->is_available(tmp) &&
                            distance_sqr(tmp, pc->get_pos()) <
                            distance_sqr(target, pc->get_pos()))
                                target = tmp;
                }

                if (target.x != BIG_NUMBER)
                        pos = target;

                return {NOTHING, ""};
        }

        int choices_cnt = 0;
        position choices[DIRECTION_CNT];

        for (int i = 0; i < DIRECTION_CNT; ++i)
                if (((enabled_features & FEATURE_DOORS) ?
                     true : lvl->get_room(pos + MOVE[i]) == room_num ) &&
                    lvl->is_available(pos + MOVE[i])) {
                        choices[choices_cnt] = pos + MOVE[i];
                        ++choices_cnt;
                }

        if (choices_cnt)
                pos = choices[rng->rand_under(choices_cnt)];

        return {NOTHING, ""};
}

long_result enemy_base::attack(character *ch) {
        return ch->get_hit(this, ATK, base_hit_rate);
}

std::string enemy_base::get_abbrev() const {
        return abbrev;
}

long_result enemy_base::get_hit(character *ch, const int tATK,
                                const fraction &hit_rate) {
        if (rng->trial(hit_rate)) {
                int tmp = calc_dmg(tATK, DEF);
                tmp = tmp > HP ? HP : tmp;
                HP -= tmp;

                if (HP == 0)
                        return {result::HIT,
                                "PC deals " + std::to_string(tmp) +
                                " damage to " + abbrev + " (" +
                                std::to_string(HP) + " HP). " +
                                abbrev + " is slain by PC. "};

                return {result::HIT, "PC deals " +
                        std::to_string(tmp) + " damage to " + abbrev + " (" +
                        std::to_string(HP) + " HP). "};
        }

        return {MISS, "PC tried to hit " + abbrev + " but missed. "};
}

int enemy_base::dies(level *lvl) {
        return rand_gold_drop(rng);
}

enemy_base *get_enemy_at(const position &pos, const enemy_list &elist) {
        for (auto e : elist)
                if (e->get_pos() == pos)
                        return e;

        return nullptr;
}

void enemy_base::print(output *out) {
        out->print_char(pos, CHAR_REP[race], COLOR_PAIR(COLOR_RED));
}
