ranged/
#include <stdio.h>

#include "mud.h"
#include "search.h"

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Coordinate
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
coordinate::coordinate()
{
    x = 0;
    y = 0;
    z = 0;
}

coordinate::coordinate(int p_x, int p_y, int p_z)
{
    x = p_x;
    y = p_y;
    z = p_z;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Coordinate Operators: direction
 */
coordinate operator+(const struct coordinate &coord, dir_types dir)
{
    coordinate ret = coord;

    switch(dir)
    {
    case DIR_NORTH:
        ret.y++;
        break;
    case DIR_EAST:
        ret.x++;
        break;
    case DIR_SOUTH:
        ret.y--;
        break;
    case DIR_WEST:
        ret.x--;
        break;
    case DIR_NORTHEAST:
        ret.x++;
        ret.y++;
        break;
    case DIR_SOUTHEAST:
        ret.x++;
        ret.y--;
        break;
    case DIR_SOUTHWEST:
        ret.x--;
        ret.y--;
        break;
    case DIR_NORTHWEST:
        ret.x--;
        ret.y++;
        break;
    case DIR_UP:
        ret.z++;
        break;
    case DIR_DOWN:
        ret.z--;
        break;

    default:
        break;
    }

    return ret;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Coordinate Operators: coordinate
 */
coordinate operator+(const struct coordinate &coorda, const struct coordinate &coordb)
{
    coordinate ret;
    
    ret.x = coorda.x+coordb.x;
    ret.y = coorda.y+coordb.y;
    ret.z = coorda.z+coordb.z;
    return ret;
}

coordinate operator-(const struct coordinate &coorda, const struct coordinate &coordb)
{
    coordinate ret;
    
    ret.x = coorda.x-coordb.x;
    ret.y = coorda.y-coordb.y;
    ret.z = coorda.z-coordb.z;
    return ret;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Coordinate Operators: comparison
 */
bool operator<(const struct coordinate &a, const struct coordinate &b)
{
    long aval, bval;

    // Handle Z here
    if (b.z < a.z)
        return true;
    if (b.z > a.z)
        return false;

    // Convert each xy into a long
    aval = a.x + (a.y << 16);
    bval = b.x + (b.y << 16);
    return (bval < aval);
}

bool operator<=(const struct coordinate &a, const struct coordinate &b)
{
    long aval, bval;

    // Handle Z here
    if (b.z < a.z)
        return true;
    if (b.z > a.z)
        return false;

    // Convert each xy into a long
    aval = a.x + (a.y << 16);
    bval = b.x + (b.y << 16);
    return (bval <= aval);
}

bool operator>(const struct coordinate &a, const struct coordinate &b)
{
    long aval, bval;

    // Handle Z here
    if (b.z < a.z)
        return false;
    if (b.z > a.z)
        return true;

    // Convert each xy into a long
    aval = a.x + (a.y << 16);
    bval = b.x + (b.y << 16);
    return (bval > aval);
}

bool operator>=(const struct coordinate &a, const struct coordinate &b)
{
    long aval, bval;

    // Handle Z here
    if (b.z < a.z)
        return false;
    if (b.z > a.z)
        return true;

    // Convert each xy into a long
    aval = a.x + (a.y << 16);
    bval = b.x + (b.y << 16);
    return (bval >= aval);
}

bool operator==(const struct coordinate &a, const struct coordinate &b)
{
    return (a.z == b.z && a.x == b.x && a.y == b.y);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Search Frame
 *
 * Used to represent a single node in the search tree.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
search_frame::search_frame(void)
{
    udf[0]   = 0;
    udf[1]   = 0;
    udf[2]   = 0;
    udf[3]   = 0;
    udf[4]   = 0;

    value    = 0;
    last_dir = DIR_SOMEWHERE;
    offset.x = 0;
    offset.y = 0;
    offset.z = 0;
}

search_frame *search_frame::make_exit(EXIT_DATA *exit)
{
    search_frame *ret = NULL;
    ROOM_INDEX_DATA *to = NULL;

    // Validate Exit
    if (exit->vdir == DIR_SOMEWHERE || !(to = exit->to_room))
        return NULL;

    // generate result
    ret           = new search_frame();
    ret->offset   = offset+(dir_types)exit->vdir;
    ret->last_dir = (dir_types)exit->vdir;
    ret->target   = to;
    ret->value    = value+1;

    return ret;
}

dir_types search_frame::get_dir(void)
{
    long x,y,z;

    x = offset.x;
    y = offset.y;
    z = offset.z;

    if (x == 0 && y == 0 && z == 0)
        return DIR_SOMEWHERE;

    if (z != 0)
    {
        if (z > 0)
            return DIR_UP;
        return DIR_DOWN;
    }

    if (x == 0)
    {
        if (y > 0)
            return DIR_NORTH;
        return DIR_SOUTH;
    }

    if (y == 0)
    {
        if (x > 0)
            return DIR_EAST;
        return DIR_WEST;
    }

    if (x > 0)
    {
        if (y > 0)
            return DIR_NORTHEAST;
        return DIR_SOUTHEAST;
    }

    if (y > 0)
        return DIR_NORTHWEST;
    return DIR_SOUTHWEST;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Search Algorithms
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void search_BFS::search(ROOM_INDEX_DATA *start, search_callback *callback, long max_dist, bool e_closed)
{
    search(start, callback, max_dist, e_closed, false);
}

void search_BFS::search(ROOM_INDEX_DATA *start, search_callback *callback, long max_dist, bool e_closed, bool use_z)
{
    std::list<search_frame*> open;   // Frame Queue
    std::set<long>           closed; // Visited Rooms

    search_frame *frame, *tmp_frame; // Frames
    EXIT_DATA *exit;                 // Exit

    long cnt;
    bool found = false;

    // Initialize
    cnt             = 1;
    frame           = new search_frame();
    frame->value    = 0;
    frame->target   = start;
    frame->last_dir = DIR_SOMEWHERE;
    open.push_back(frame);
    closed.insert(frame->target->vnum);

    // Search Loop
    while(!found && cnt)
    {
        --cnt;
        frame = open.front();
        open.pop_front();

        // Search Exits
        for (exit = frame->target->first_exit; exit; exit = exit->next)
        {

            if (!exit)
                continue;
            // No "other" dir_types...
            if (exit->vdir == DIR_SOMEWHERE)
                continue;

            // No "Z" search
            if (!use_z && (exit->vdir == DIR_UP || exit->vdir == DIR_DOWN))
                continue;

            // Don't pass through e_closed.
            if (!e_closed && IS_SET(exit->exit_info, EX_CLOSED))
                continue;

            tmp_frame = frame->make_exit(exit);

            // The exit was unsuitable for searching.
            if (!tmp_frame)
                continue;

            // Too Far
            if (tmp_frame->value >= max_dist)
            {
                delete tmp_frame;
                continue;
            }

            // Already Visited
            if (closed.find(tmp_frame->target->vnum) != closed.end())
            {
                delete tmp_frame;
                continue;
            }

            // Handle Callback
            if (callback->search(tmp_frame))
            {
                delete tmp_frame;
                found = true;
                break;
            }

            cnt++;
            // Update Open and Closed
            open.push_back(tmp_frame);
            closed.insert(tmp_frame->target->vnum);
        }

        // finally, cleanup this frame
        delete frame;
    }

    // cleanup any remaining frames.
    while (open.size() > 0)
    {
        frame = open.front();
        open.pop_front();
        delete frame;
    }
}

void search_LOS::search(ROOM_INDEX_DATA *start, search_callback *callback, long max_dist, bool e_closed)
{
    search(start, callback, max_dist, e_closed, false);
}

void search_LOS::search(ROOM_INDEX_DATA *start, search_callback *callback, long max_dist, bool e_closed, bool use_z)
{
    ROOM_INDEX_DATA *from, *to;
    EXIT_DATA *exit, *tmp;
    dir_types  dir;
    search_frame *frame;
    bool found = false;

    // Flyweight Frame
    frame = new search_frame();

    // Exit Loop (search for appropriate dir_typess)
    for (exit = start->first_exit; exit; exit = exit->next)
    {
        dir = (dir_types)exit->vdir;

        // No "other" dir_types...
        if (exit->vdir == DIR_SOMEWHERE)
            continue;

        if (!use_z && (dir == DIR_UP || dir == DIR_DOWN))
            continue;

        // Don't pass through e_closed.
        if (!e_closed && IS_SET(exit->exit_info, EX_CLOSED))
            continue;

        // Reset Frame
        frame->offset.x = 0;
        frame->offset.y = 0;
        frame->offset.z = 0;
        frame->value    = 0.00;
        frame->last_dir = dir;

        // Init
        from = start;

        // Handle the distance
        while(++frame->value <= max_dist)
        {
            // No exit?
            if (!(tmp = get_exit(from, dir)))
                break;

            // Exit closed?
            if (!e_closed && IS_SET(tmp->exit_info, EX_CLOSED))
                break;

            // No target?
            if (!(to = tmp->to_room))
                break;

            // Update Frame
            frame->target = to;
            frame->offset = frame->offset + dir;

            // Found
            if (callback->search(frame))
            {
                found = true;
                break;
            }
            from = to;
        }
        if (found)
            break;
    }

    // Cleanup
    delete frame;
}

void search_DIR::search(ROOM_INDEX_DATA *start, search_callback *callback, dir_types dir, long max_dist, bool e_closed)
{
    ROOM_INDEX_DATA *from, *to;
    EXIT_DATA *exit;
    search_frame *frame;
    bool found = false;

    // Flyweight Frame
    frame = new search_frame();

    // No exit
    if (!(exit = get_exit(start, dir)))
        return;

    // Don't pass through e_closed.
    if (!e_closed && IS_SET(exit->exit_info, EX_CLOSED))
        return;

    // Reset Frame
    frame->offset.x = 0;
    frame->offset.y = 0;
    frame->offset.z = 0;
    frame->value    = 0.00;
    frame->last_dir = dir;

    // Init
    from = start;

    // Handle the distance
    while(++frame->value <= max_dist)
    {
        // No exit?
        if (!(exit = get_exit(from, dir)))
            break;

        // Exit closed?
        if (!e_closed && IS_SET(exit->exit_info, EX_CLOSED))
            break;

        // No target?
        if (!(to = exit->to_room))
            break;

        // Update Frame
        frame->target = to;
        frame->offset = frame->offset + dir;

        // Found
        if (callback->search(frame))
        {
            found = true;
            break;
        }
        from = to;
    }

    // Cleanup
    delete frame;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Search Callbacks
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Scan
 */
void do_scan2( CHAR_DATA * ch, char *argument )
{
    srch_scan cb(ch);
    char arg[MAX_INPUT_LENGTH];
    dir_types dir;

    argument = one_argument(argument, arg);

    if (!str_cmp(arg, "BFS"))
    {
        send_to_char("Using BFS Search:\r\n", ch);
        search_BFS::search(ch->in_room, &cb, 4, false);
    }
    else if (!str_cmp(arg, "DIR"))
    {
        send_to_char("Using DIR Search:\r\n", ch);

        dir = (dir_types)get_dir(argument);
        search_DIR::search(ch->in_room, &cb, dir, 4, false);
    }
    else
    {
        send_to_char("Using LOS Search:\r\n", ch);
        search_LOS::search(ch->in_room, &cb, 4, false);
    }

    if (!cb.found)
        send_to_char("Nobody.", ch);
}

srch_scan::srch_scan(CHAR_DATA *p_actor)
{
    actor = p_actor;
    found = 0;
}

bool srch_scan::search(search_frame *frame)
{
    CHAR_DATA *target;

    if (!frame->target->first_person)
        return false;

    ch_printf(actor, "  %d %s\r\n", (int)frame->value, dir_name[frame->get_dir()]);
    send_to_char("--------------------\r\n", actor);

    for (target = frame->target->first_person; target; target = target->next_in_room)
    {
        if (target == actor)
            continue;
        ch_printf(actor, "%s\r\n", target->short_descr);
        found++;
    }
    return false;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
 * Map
 */
void do_map( CHAR_DATA * ch, char *argument )
{
    srch_map cb;
    char arg[MAX_INPUT_LENGTH];
    dir_types dir;

    argument = one_argument(argument, arg);

    if (!str_cmp(arg, "BFS"))
    {
        send_to_char("Using BFS Search:\r\n", ch);
        search_BFS::search(ch->in_room, &cb, 4, false);
    }
    else if (!str_cmp(arg, "DIR"))
    {
        send_to_char("Using DIR Search:\r\n", ch);

        dir = (dir_types)get_dir(argument);
        search_DIR::search(ch->in_room, &cb, dir, 4, false);
    }
    else
    {
        send_to_char("Using LOS Search:\r\n", ch);
        search_LOS::search(ch->in_room, &cb, 4, false);
    }

    // Output map
    ch_printf(ch,
        "-------\r\n|%-5.5s|\r\n|%-5.5s|\r\n|%-5.5s|\r\n|%-5.5s|\r\n|%-5.5s|\r\n-------\r\n",
        cb.buf, cb.buf+5, cb.buf+10, cb.buf+15, cb.buf+20);
}

srch_map::srch_map(void)
{
    int i;
    for (i=0; i<25; i++)
        buf[i] = ' ';
    buf[12] = '#';
}

bool srch_map::search(search_frame *frame)
{
    int x,y,i;

    // No "Z" movement allowed
    if (frame->offset.z)
        return false;

    // Localize
    x = frame->offset.x;
    y = frame->offset.y;

    // X/Y constraints
    if (x > 2 || x < -2)
        return false;
    if (y > 2 || y < -2)
        return false;

    // Normalize (0 to 4 instead of -2 to 2)
    x += 2;
    y += 2;

    // Generate array position.
    // Flip the y (so north is up, south is down)
    i  = ((4-y)*5)+(x%5);

    // Already found
    if (buf[i] != ' ')
        return false;

    // Update map
    buf[i] = '.';
    return false;
}