/**
 * @file
 * @brief Functions for spattering blood all over the place.
 **/

#include "AppHdr.h"

#include "bloodspatter.h"

#include "cloud.h"
#include "coordit.h"
#include "env.h"
#include "fprop.h"
#include "losglobal.h"
#include "shout.h"
#include "terrain.h"
#include "religion.h"

static bool allow_bleeding_on_square(const coord_def& where)
{
    // No bleeding onto sanctuary ground, please.
    // Also not necessary if already covered in blood.
    if (is_bloodcovered(where) || is_sanctuary(where))
        return false;

    // No spattering into lava or water.
    if (grd(where) >= DNGN_LAVA && grd(where) < DNGN_FLOOR)
        return false;

    // No spattering into fountains (other than blood).
    if (grd(where) == DNGN_FOUNTAIN_BLUE
        || grd(where) == DNGN_FOUNTAIN_SPARKLING)
    {
        return false;
    }

    // The good gods like to keep their altars pristine.
    if (is_good_god(feat_altar_god(grd(where))))
        return false;

    return true;
}

bool maybe_bloodify_square(const coord_def& where)
{
    if (!allow_bleeding_on_square(where))
        return false;

    env.pgrid(where) |= FPROP_BLOODY;
    return true;
}

/**
 * Rotate the wall blood splat tile, so that it is facing the source.
 *
 * Wall blood splat tiles are drawned with the blood dripping down. We need
 * the tile to be facing an orthogonal empty space for the effect to look
 * good. We choose the empty space closest to the source of the blood.
 *
 * @param where Coordinates of the wall where there is a blood splat.
 * @param from Coordinates of the source of the blood.
 * @param old_blood blood splats created at level generation are old and can
 * have some blood inscriptions. Only for south facing splats, so you don't
 * have to turn your head to read the inscriptions.
 */
static void _orient_wall_blood(const coord_def& where, coord_def from,
                               bool old_blood)
{
    if (!feat_is_wall(env.grid(where)))
        return;

    if (from == INVALID_COORD)
        from = where;

    coord_def closer = INVALID_COORD;
    int dist = INT_MAX;
    for (orth_adjacent_iterator ai(where); ai; ++ai)
    {
        if (in_bounds(*ai) && !cell_is_solid(*ai)
            && cell_see_cell(from, *ai, LOS_SOLID)
            && (distance2(*ai, from) < dist
                || distance2(*ai, from) == dist && coinflip()))
        {
            closer = *ai;
            dist = distance2(*ai, from);
        }
    }

    // If we didn't find anything, the wall is in a corner.
    // We don't want blood tile there.
    if (closer == INVALID_COORD)
    {
        env.pgrid(where) &= ~FPROP_BLOODY;
        return;
    }

    const coord_def diff = where - closer;
    if (diff == coord_def(1, 0))
        env.pgrid(where) |= FPROP_BLOOD_WEST;
    else if (diff == coord_def(0, 1))
        env.pgrid(where) |= FPROP_BLOOD_NORTH;
    else if (diff == coord_def(-1, 0))
        env.pgrid(where) |= FPROP_BLOOD_EAST;
    else if (old_blood && one_chance_in(10))
        env.pgrid(where) |= FPROP_OLD_BLOOD;
}

static void _maybe_bloodify_square(const coord_def& where, int amount,
                                   bool spatter = false,
                                   bool smell_alert = true,
                                   const coord_def& from = INVALID_COORD,
                                   const bool old_blood = false)
{
    if (amount < 1)
        return;

    bool may_bleed = allow_bleeding_on_square(where);

    bool ignite_blood = player_mutation_level(MUT_IGNITE_BLOOD)
    && you.see_cell(where);

    if (ignite_blood)
        amount *= 2;

    if (x_chance_in_y(amount, 20))
    {
        dprf("might bleed now; square: (%d, %d); amount = %d",
             where.x, where.y, amount);
        if (may_bleed)
        {
            env.pgrid(where) |= FPROP_BLOODY;
            _orient_wall_blood(where, from, old_blood);

            if (ignite_blood
                && !cell_is_solid(where)
                && env.cgrid(where) == EMPTY_CLOUD)
            {
                place_cloud(CLOUD_FIRE, where, 5 + random2(6), &you);
            }
        }

        // The blood spilled can be detected via blood scent even if the
        // spot it would fall is prevented from becoming bloodied (for
        // example, because it is water)
        if (smell_alert && in_bounds(where))
            blood_smell(12, where);

        if (spatter)
        {
            // Smaller chance of spattering surrounding squares.
            for (adjacent_iterator ai(where); ai; ++ai)
            {
                _maybe_bloodify_square(*ai, amount/15, false, true, from,
                                       old_blood);
            }
        }
    }
}

// Currently flavour only: colour ground (and possibly adjacent squares) red.
// "damage" depends on damage taken (or hitpoints, if damage higher),
// or, for sacrifices, on the number of chunks possible to get out of a corpse.
void bleed_onto_floor(const coord_def& where, monster_type montype,
                      int damage, bool spatter, bool smell_alert,
                      const coord_def& from, const bool old_blood)
{
    ASSERT_IN_BOUNDS(where);

    if (montype == MONS_PLAYER && !you.can_bleed())
        return;

    if (montype != NUM_MONSTERS && montype != MONS_PLAYER)
    {
        monster m;
        m.type = montype;
        if (!m.can_bleed())
            return;
    }

    _maybe_bloodify_square(where, damage, spatter, smell_alert, from,
                           old_blood);
}

void blood_spray(const coord_def& origin, monster_type montype, int level)
{
    int tries = 0;
    for (int i = 0; i < level; ++i)
    {
        // Blood drops are small and light and suffer a lot of wind
        // resistance.
        int range = random2(8) + 1;

        while (tries < 5000)
        {
            ++tries;

            coord_def bloody = origin;
            bloody.x += random_range(-range, range);
            bloody.y += random_range(-range, range);

            if (in_bounds(bloody) && cell_see_cell(origin, bloody, LOS_SOLID))
            {
                bleed_onto_floor(bloody, montype, 99, false, true, origin);
                break;
            }
        }
    }
}

static void _spatter_neighbours(const coord_def& where, int chance,
                                const coord_def& from = INVALID_COORD)
{
    for (adjacent_iterator ai(where, false); ai; ++ai)
    {
        if (!allow_bleeding_on_square(*ai))
            continue;

        if (one_chance_in(chance))
        {
            env.pgrid(*ai) |= FPROP_BLOODY;
            _orient_wall_blood(where, from, true);
            _spatter_neighbours(*ai, chance+1, from);
        }
    }
}

void generate_random_blood_spatter_on_level(const map_bitmask *susceptible_area)
{
    const int max_cluster = 7 + random2(9);

    // Lower chances for large bloodshed areas if we have many clusters,
    // but increase chances if we have few.
    // Chances for startprob are [1..3] for 7-9 clusters,
    //                       ... [1..4] for 10-12 clusters, and
    //                       ... [2..5] for 13-15 clusters.

    int min_prob = 1;
    int max_prob = 4;

    if (max_cluster < 10)
        max_prob--;
    else if (max_cluster > 12)
        min_prob++;

    for (int i = 0; i < max_cluster; ++i)
    {
        const coord_def c = random_in_bounds();

        if (susceptible_area && !(*susceptible_area)(c))
            continue;

        // startprob is used to initialise the chance for neighbours
        // being spattered, which will be decreased by 1 per recursion
        // round. We then use one_chance_in(chance) to determine
        // whether to spatter a given grid or not. Thus, startprob = 1
        // means that initially all surrounding grids will be
        // spattered (3x3), and the _higher_ startprob the _lower_ the
        // overall chance for spattering and the _smaller_ the
        // bloodshed area.
        const int startprob = min_prob + random2(max_prob);

        maybe_bloodify_square(c);

        _spatter_neighbours(c, startprob);
    }
}
