/
/** paths.c *******************************************************
 *                                                                *
 *     A lot of effort  and  time has  been put  into this code.  *
 *     Permission to  use it is  granted  provided that you keep  *
 *  this header intact! :)                                        *
 *     I would like VERY much any comments/suggestions you might  *
 *  have. If you use this code, I would really appreciate if you  *
 *  could mail me just stating you used it! :)                    *
 *                                                                *
 *  - Evren                                                       *
 *                                                                *
 *  Jorge Ribeiro Pereira <si22668@ci.uminho.pt>      20/12/1998  *
 *                                                                *
 ******************************************************************/

#if defined( macintosh )
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include "merc.h"


/**************************************************************************
 *                                                                        *
 * IMPORTANT FUNCTIONS                                                    *
 * ( note: since the core functions are recursive, most of them need some *
 *         extra parameters,  but  since  they  aren't needed outside,  I *
 *         provide the following functions, that serve as entrance to the *
 *         auxiliar functions (which have "_" appended to their name)     *
 *                                                                        *
 **************************************************************************/

// If you plan to use these functions out from this file,
// THIS SHOULD GO INTO merc.h, FROM HERE -->

typedef struct path_data 	PATH_DATA;

typedef bool ROOM_CMP_FUN 	( ROOM_INDEX_DATA * pRoom,
                            	  void * pArgument );

struct path_data
{
   ROOM_INDEX_DATA	* pRoom;	// room
   int			  dir;		// direction from the previous room
   PATH_DATA		* next;		// next room in path
};

int get_direction		( ROOM_INDEX_DATA * pOrig,
                    	  	  ROOM_INDEX_DATA * pDest, bool pass );
PATH_DATA * get_path		( ROOM_INDEX_DATA * pOrig,
                       	  	  ROOM_INDEX_DATA * pDest, bool pass );

int get_dir_somewhere		( ROOM_INDEX_DATA *pOrig,
                       		  ROOM_CMP_FUN *function, 
                       		  void *pArgument, bool pass );
PATH_DATA * get_path_somewhere	( ROOM_INDEX_DATA *pOrig,
                       		  ROOM_CMP_FUN *function, 
                       		  void *pArgument, bool pass );

void free_path			( PATH_DATA **list );
char * path_string		( PATH_DATA *path );


// <------- TO HERE

/*************************************************************************/

#define	VISITED( room )		( (room)->visited )

// this piece of code is alike in all functions,t shows up, but I'm 
// not gonna make a function out of it due to performance issues
#define IGNORE_ROOM( pOrig, i, pRoom, pass )\
(   !pOrig->exit[i]\
 || !( pRoom = pOrig->exit[i]->to_room )\
 || (  !pass && !IS_SET( pOrig->exit[i]->exit_flags, EX_CLOSED ) )\
 || pOrig->area != pRoom->area  )


// auxiliar data structures
typedef struct full_path_data	FULL_PATH_DATA;	
struct full_path_data		// structure for returning a path and its
{				// length (needed to find the the best path)
   int		  length;	// used in: get_path_, get_path_somewhere
   PATH_DATA	* path;
};

typedef struct dir_data		DIR_DATA;
struct dir_data			// structure for returning the best direction
{				// and the length of the path( needed to find
   int		  length;	// the best direction )
   int		  dir;		// used in: get_direction_, get_dir_somewhere_
};


/*
 *  AUXILIAR FUNCTIONS
 */
// creating a new room for a path
PATH_DATA *new_path_data( ROOM_INDEX_DATA *pRoom, int dir )
{
   PATH_DATA *v;

   v = malloc( sizeof (PATH_DATA) );

   v->pRoom		= pRoom; 
   v->dir		= dir;
   v->next		= NULL;
   return v;
}

// add a room to a path
void append_path( PATH_DATA **list, ROOM_INDEX_DATA *pRoom, int dir )
{
   PATH_DATA *v;

   v = malloc( sizeof (PATH_DATA) );

   v->pRoom		= pRoom; 
   v->dir		= dir;
   v->next		= *list;
   *list		= v;

   return;
}

// freeing memory for a path
void free_path( PATH_DATA **list )
{
   PATH_DATA **v, *v_next;

   if ( !*list )
      return;

   for ( v = list; *v; )
   {
      v_next = (*v)->next;
      free( *v );
      *v = v_next;
   }
}

char * path_string( PATH_DATA *path )
{
   static char buf[MAX_STRING_LENGTH];

   buf[0] = '\0';
   for ( ; path; path = path->next )
   {
      strcat( buf, dir_name[path->dir] );
      if ( path->next && path->next->next )
         strcat( buf, ", " );
      else if ( path->next )
         strcat( buf, " and " );
   }
   return buf;
}

// This function returns a structure with the path length and the direction.
// Use get_direction(below), to get just the direction.
DIR_DATA get_direction_( ROOM_INDEX_DATA *pOrig,
                         ROOM_INDEX_DATA *pDest, bool pass )
{
   ROOM_INDEX_DATA *pRoom;
   int i;
   DIR_DATA tmp, best;

   if ( pOrig == pDest )
   {
      best.dir		= -1; 
      best.length	= 1;
      return best;
   }

   best.dir	= tmp.dir	= -1;
   best.length	= tmp.length	= 0;

   VISITED( pOrig ) = TRUE;
   for ( i = 0; i< MAX_DIR; i++ )
   {
      if ( IGNORE_ROOM( pOrig, i, pRoom, pass ) )
         continue;

      if ( VISITED( pRoom ) )
         continue;
   
      tmp = get_direction_( pRoom, pDest, pass ); // recursion

      if ( tmp.length < 1 )
         continue;

      if ( best.length == 0 || tmp.length < best.length )
      {
          best.dir	= i;
          best.length	= tmp.length + 1;
      }
   }
   VISITED( pOrig ) = FALSE;

   return best;
}

DIR_DATA get_dir_somewhere_( ROOM_INDEX_DATA *pOrig,
                             ROOM_CMP_FUN *function, 
                             void *pArgument,
                             bool pass )
{
   ROOM_INDEX_DATA *pRoom;
   int i;
   DIR_DATA tmp, best;

   if ( (function)( pOrig, pArgument ) ) // the only change from get_direction_
   {
       best.dir		= -1;
       best.length	= 1;
       return best;
   }

   best.dir	= tmp.dir	= -1;
   best.length	= tmp.length	= 0;

   VISITED( pOrig ) = TRUE;
   for ( i = 0; i< MAX_DIR; i++ )
   {
      if ( IGNORE_ROOM( pOrig, i, pRoom, pass ) )
         continue;
   
      if ( VISITED( pRoom ) )
         continue;
   
      tmp = get_dir_somewhere_( pRoom, function, pArgument, pass ); 

      if ( tmp.length < 1 )
         continue;

      if ( best.length == 0 || tmp.length < best.length )
      {
          best.dir	= i;
          best.length	= tmp.length + 1;
      }
   }
   VISITED( pOrig ) = FALSE;

   return best;
}


FULL_PATH_DATA get_path_( ROOM_INDEX_DATA *pOrig, ROOM_INDEX_DATA *pDest,
                          bool pass )
{
   int i;
   FULL_PATH_DATA tmp, best;
   ROOM_INDEX_DATA *pRoom;

   if ( pOrig == pDest )
   {
       best.length      = 1;
       best.path	= NULL;
       return best;
   }
   
   tmp.length	= best.length	= 0;
   tmp.path	= best.path	= NULL;

   VISITED( pOrig ) = TRUE;
   for ( i = 0; i < MAX_DIR; i++ )
   {
      if ( IGNORE_ROOM( pOrig, i, pRoom, pass ) )
         continue;

      if ( VISITED( pRoom ) )
         continue;

      tmp = get_path_( pRoom, pDest, pass );

      if ( tmp.length < 1 )
         continue;
      
      if ( best.length == 0 || tmp.length < best.length )
      {
         free_path( &best.path );
         best.length	 = tmp.length + 1;
         best.path	 = tmp.path;
         append_path( &best.path, pRoom, i );
      }
      else
      {
         free_path( &tmp.path );
      }
   }
   VISITED( pOrig ) = FALSE;

   return best;
}


FULL_PATH_DATA get_path_somewhere_( ROOM_INDEX_DATA *pOrig,
                                    ROOM_CMP_FUN *function, 
                                    void *pArgument, bool pass )
{
   int i;
   FULL_PATH_DATA tmp, best;
   ROOM_INDEX_DATA *pRoom;

   if ( (function)( pOrig, pArgument ) ) 
   {
       best.length      = 1;
       best.path	= NULL;
       return best;
   }

   tmp.length	= best.length	= 0;
   tmp.path	= best.path	= NULL;

   VISITED( pOrig ) = TRUE;
   for ( i = 0; i < MAX_DIR; i++ )
   {
      if ( IGNORE_ROOM( pOrig, i, pRoom, pass ) )
         continue;

      if ( VISITED( pRoom ) )
         continue;

      tmp = get_path_somewhere_( pRoom, function, pArgument, pass );

      if ( tmp.length < 1 )
         continue;
      
      if ( best.length  == 0 || tmp.length < best.length )
      {
         free_path( &best.path );
         best.length	 = tmp.length + 1;
         best.path	 = tmp.path;
         append_path( &best.path, pRoom, i );
      }
      else
      {
         free_path( &tmp.path );
      }
   }
   VISITED( pOrig ) = FALSE;

   return best;
}

/***********************************************************************/

int get_direction( ROOM_INDEX_DATA *pOrig,
                    ROOM_INDEX_DATA *pDest, bool pass )
{
   return get_direction_( pOrig, pDest, pass ).dir;
}


PATH_DATA * get_path( ROOM_INDEX_DATA *pOrig,
                     ROOM_INDEX_DATA *pDest, bool pass )
{
   return get_path_( pOrig, pDest, pass ).path;
}


int get_dir_somewhere( ROOM_INDEX_DATA *pOrig,
                       ROOM_CMP_FUN *function, 
                       void *pArgument, bool pass )
{
   return get_dir_somewhere_( pOrig, function, pArgument, pass ).dir;
}


PATH_DATA * get_path_somewhere( ROOM_INDEX_DATA *pOrig,
                                ROOM_CMP_FUN *function, 
                                void *pArgument, bool pass )
{
   return get_path_somewhere_( pOrig, function, pArgument, pass ).path;
}


/**************************************************************************/

void do_path( CHAR_DATA *ch, char *argument )
{
  ROOM_INDEX_DATA	* pDest;
  PATH_DATA		* path;
  char 			  arg[MAX_INPUT_LENGTH];
  int 			  vnum;
  char			  buf[MAX_STRING_LENGTH];

  argument = one_argument( argument, arg );

  if ( arg[0] == '\0' )
  {
     send_to_char( "Find path to which room (vnum)?\n\r", ch );
     return;
  }

  if ( ( vnum = atoi( arg ) ) <= 0 )   
  {  
     send_to_char( "Invalid room vnum.\n\r", ch );
     return;
  }

  if ( !( pDest = get_room_index( vnum ) ) )
  {  
     send_to_char( "Destination room not found.\n\r", ch );
     return;
  }

  if ( ch->in_room == pDest )
  {  
     send_to_char( "I suggest you stay right where you are.\n\r", ch );
     return;
  }

  path = get_path( ch->in_room, pDest, TRUE );

  if ( !path )
  {  
     send_to_char( "No path found.\n\r", ch );
     return;
  }

  sprintf( buf, "To get there you'll have to go: %s.\n\r",
                path_string( path ) );
  
  free_path( &path );		// don't forget to free the memory used

  send_to_char( buf, ch );
}


void do_direction( CHAR_DATA *ch, char *argument )
{ 
  ROOM_INDEX_DATA       * pDest;
  char                    arg[MAX_INPUT_LENGTH];
  int                     vnum;
  char                    buf[MAX_STRING_LENGTH];
  int dir;

  argument = one_argument( argument, arg );

  if ( arg[0] == '\0' )
  {
     send_to_char( "Find direction to which room (vnum)?\n\r", ch );
     return;
  }

  if ( ( vnum = atoi( arg ) ) <= 0 )
  {
     send_to_char( "Invalid room vnum.\n\r", ch );
     return;
  }

  if ( !( pDest = get_room_index( vnum ) ) )
  {  
     send_to_char( "Destination room not found.\n\r", ch );
     return;
  }

  if ( ch->in_room == pDest )
  {  
     send_to_char( "I suggest you stay right where you are.\n\r", ch );
     return;
  }

  dir = get_direction( ch->in_room, pDest, TRUE );

  if ( dir == -1 )
  {  
     send_to_char( "No path found.\n\r", ch );
     return;
  }

  sprintf( buf, "I suggest you go %s.\n\r", dir_name[dir] );
  send_to_char( buf, ch );
}


/**********************************************************************/

// see how simple it is?
bool is_room_name( ROOM_INDEX_DATA *pRoom, void * pArgument )
{
   char *argument = (char *) pArgument;
   return is_name( argument, pRoom->name );
}

void do_whereto( CHAR_DATA *ch, char *argument )
{
   PATH_DATA *path;
   char buf[MAX_STRING_LENGTH];

   if ( argument[0] == '\0' )
   {
      // I hope this sentence isn't MS copyrighted...
      send_to_char( "Where do you want to go? \n\r", ch );
      return;
   }

   path = get_path_somewhere( ch->in_room, is_room_name,
                              argument, TRUE );

   if ( !path )
   {
      send_to_char( "You can't remember how to get there.\n\r", ch );
      return;
   }

   sprintf( buf, "To get to %s you'll have to go: %s.\n\r",
                 argument, path_string( path ) );

   free_path( &path );           // don't forget to free the memory used
 
   send_to_char( buf, ch );
}


/* NOTE FOR PROGRAMMERS
If we didn't care for performance, we could carve get_direction_
and get_path_ out from get_dir_somewhere_ and get_path_somewhere_
using the following predicate function:

bool is_equal( ROOM_INDEX_DATA *pRoom, void * pArgument )
{
   ROOM_INDEX_DATA *pDest = (ROOM_INDEX_DATA *) pArgument;
   return pRoom == pDest;
}

and passing the destination room pointer as the pArgument to the _somewhere_
functions.
*/