/***************************************************************************
 *  file: shop.c , Shop module.                            Part of DIKUMUD *
 *  Usage: Procedures handling shops and shopkeepers.                      *
 *  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
 *                                                                         *
 *  Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse  *
 *  Performance optimization and bug fixes by MERC Industries.             *
 *  You can use our stuff in any way you like whatsoever so long as this   *
 *  copyright notice remains intact.  If you like it please drop a line    *
 *  to mec@garnet.berkeley.edu.                                            *
 *                                                                         *
 *  This is free software and you are benefitting.  We hope that you       *
 *  share your changes too.  What goes around, comes around.               *
 ***************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "structs.h"
#include "mob.h"
#include "obj.h"
#include "handler.h"
#include "db.h"
#include "interp.h"
#include "utils.h"

#define MAX_SHOP    64
#define MAX_TRADE    5

extern struct str_app_type str_app[];
extern struct index_data *mob_index;

struct shop_data
{
    int type[MAX_TRADE];    /* Types of things shop will buy.       */
    float profit_buy;       /* Factor to multiply cost with.        */
    float profit_sell;      /* Factor to multiply cost with.        */
    char *no_such_item1;    /* Message if keeper hasn't got an item */
    char *no_such_item2;    /* Message if player hasn't got an item */
    char *missing_cash1;    /* Message if keeper hasn't got cash    */
    char *missing_cash2;    /* Message if player hasn't got cash    */
    char *do_not_buy;       /* If keeper doesn't buy such things.   */
    char *message_buy;      /* Message when player buys item        */
    char *message_sell;     /* Message when player sells item       */
    int keeper;             /* The mob who owns the shop (virtual)  */
    int in_room;            /* Where is the shop?                   */
    int open1, open2;       /* When does the shop open?             */
    int close1, close2;     /* When does the shop close?            */
};



extern struct room_data *world;
extern struct time_info_data time_info;

struct shop_data shop_index[MAX_SHOP];
int max_shop;



/*
 * See if a shop keeper wants to trade.
 */
int is_ok( struct char_data *keeper, struct char_data *ch, int shop_nr )
{
    char buf[240];

    /*
     * Undesirables.
     */
    if ( IS_SET(ch->specials.affected_by, AFF_KILLER) )
    {
	do_say( keeper, "Go away before I call the guards!!", 0 );
	sprintf( buf, "%s the KILLER is over here!\n\r", GET_NAME(ch) );
	do_shout( keeper, buf, 0 );
	return FALSE;
    }

    if ( IS_SET(ch->specials.affected_by, AFF_THIEF) )
    {
	do_say( keeper, "Thieves are not welcome!", 0 );
	sprintf( buf, "%s the THIEF is over here!\n\r", GET_NAME(ch) );
	do_shout( keeper, buf, 0 );
	return FALSE;
    }

    /*
     * Shop hours.
     */
    if ( time_info.hours < shop_index[shop_nr].open1 )
    {
	do_say( keeper, "Come back later!", 0 );
	return FALSE;
    }
    
    else if ( time_info.hours <= shop_index[shop_nr].close1 )
	;

    else if ( time_info.hours < shop_index[shop_nr].open2 )
    {
	do_say( keeper, "Come back later!", 0 );
	return FALSE;
    }

    else if ( time_info.hours <= shop_index[shop_nr].close2 )
	;

    else
    {
	do_say( keeper, "Sorry, come back tomorrow.", 0 );
	return FALSE;
    }

    /*
     * Invisible people.
     */
    if ( !CAN_SEE( keeper, ch ) )
    {
	do_say( keeper, "I don't trade with someone I can't see!", 0 );
	return FALSE;
    }

    return TRUE;
}



/*
 * See if a shop will buy an item.
 */
int trade_with( struct obj_data *item, int shop_nr )
{
    int counter;

    if ( item->obj_flags.cost < 1 )
	return FALSE;

    for ( counter = 0; counter < MAX_TRADE; counter++ )
    {
	if ( GET_ITEM_TYPE(item) == shop_index[shop_nr].type[counter] )
	    return TRUE;
    }

    return FALSE;
}



/*
 * Buy an item from a shop.
 */
void shopping_buy( char *arg, struct char_data *ch,
     struct char_data *keeper, int shop_nr )
{
    char buf[MAX_STRING_LENGTH];
    char argm[100];
    struct obj_data *obj;
    int cost;

    if ( !is_ok( keeper, ch, shop_nr ) )
	return;

    one_argument( arg, argm );
    if ( *argm == '\0' )
    {
	sprintf( buf, "%s What do you want to buy?", GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    if ( ( obj = get_obj_in_list_vis( ch, argm, keeper->carrying ) ) == NULL )
    {
	sprintf( buf, shop_index[shop_nr].no_such_item1, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    if ( obj->obj_flags.cost <= 0 )
    {
	extract_obj( obj );
	sprintf( buf, shop_index[shop_nr].no_such_item1, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    cost = (int) (obj->obj_flags.cost * shop_index[shop_nr].profit_buy);

    if ( GET_GOLD(ch) < cost )
    {
	sprintf( buf, shop_index[shop_nr].missing_cash2, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }
    
    if ( IS_CARRYING_N(ch) + 1 > CAN_CARRY_N(ch) )
    {
	send_to_char( "You can't carry that many items.\n\r", ch );
	return;
    }

    if ( IS_CARRYING_W(ch) + obj->obj_flags.weight > CAN_CARRY_W(ch) )
    {
	send_to_char( "You can't carry that much weight.\n\r", ch );
	return;
    }

    act( "$n buys $p.", FALSE, ch, obj, 0, TO_ROOM );
    sprintf( buf, shop_index[shop_nr].message_buy, GET_NAME(ch), cost );
    do_tell( keeper, buf, 0 );
    sprintf( buf, "You now have %s.\n\r", obj->short_description );
    send_to_char( buf, ch );
    GET_GOLD(ch)     -= cost;
    GET_GOLD(keeper) += cost;

    /* Wormhole to map_eq_level */
    if ( obj->obj_flags.eq_level == 1000 )
	obj = read_object( obj->item_number, 0 );
    else
	obj_from_char( obj );

    obj_to_char( obj, ch );
    return; 
}



/*
 * Sell an item to a shop keeper.
 */
void shopping_sell( char *arg, struct char_data *ch,
     struct char_data *keeper, int shop_nr )
{
    char buf[MAX_STRING_LENGTH];
    char argm[100];
    struct obj_data *obj;
    int cost;

    if ( !is_ok( keeper, ch, shop_nr ) )
	return;

    one_argument( arg, argm );

    if ( *argm == '\0' )
    {
	sprintf( buf, "%s What do you want to sell?", GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    if ( ( obj = get_obj_in_list_vis( ch, argm, ch->carrying ) ) == NULL )
    {
	sprintf( buf, shop_index[shop_nr].no_such_item2, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    if ( !trade_with( obj, shop_nr ) || obj->obj_flags.cost < 1 )
    {
	sprintf( buf, shop_index[shop_nr].do_not_buy, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    cost = (int) ( obj->obj_flags.cost * shop_index[shop_nr].profit_sell );
    if ( GET_GOLD(keeper) < cost )
    {
	sprintf( buf, shop_index[shop_nr].missing_cash1, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    act( "$n sells $p.", FALSE, ch, obj, 0, TO_ROOM );
    sprintf( buf, shop_index[shop_nr].message_sell, GET_NAME(ch), cost );
    do_tell( keeper, buf, 0 );
    sprintf( buf, "The shopkeeper now has %s.\n\r", obj->short_description );
    send_to_char( buf,ch );
    GET_GOLD(ch)     += cost;
    GET_GOLD(keeper) -= cost;

    if ( get_obj_in_list( argm, keeper->carrying )
    || GET_ITEM_TYPE(obj) == ITEM_TRASH )
    {
	extract_obj( obj );
    }
    else
    {
	obj_from_char( obj );
	obj_to_char( obj, keeper );
    }

    return;
}



/*
 * Value an item.
 */
void shopping_value( char *arg, struct char_data *ch, 
    struct char_data *keeper, int shop_nr )
{
    char buf[MAX_STRING_LENGTH];
    char argm[100];
    struct obj_data *obj;
    int cost;

    if ( !is_ok( keeper, ch, shop_nr ) )
	return;

    one_argument( arg, argm );

    if ( *argm == '\0' )
    {
	sprintf( buf, "%s What do you want to value?", GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    if ( ( obj = get_obj_in_list_vis( ch, argm, ch->carrying ) ) == NULL )
    {
	sprintf( buf, shop_index[shop_nr].no_such_item2, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    if ( !trade_with( obj, shop_nr ) || obj->obj_flags.cost < 1 )
    {
	sprintf( buf, shop_index[shop_nr].do_not_buy, GET_NAME(ch) );
	do_tell( keeper, buf, 0 );
	return;
    }

    cost = (int) ( obj->obj_flags.cost * shop_index[shop_nr].profit_sell );
    sprintf( buf, "%s I'll give you %d gold coins for that.",
	GET_NAME(ch), cost );
    do_tell( keeper, buf, 0 );
    return;
}



/*
 * List available items.
 */
void shopping_list( char *arg, struct char_data *ch,
     struct char_data *keeper, int shop_nr )
{
    char buf[MAX_STRING_LENGTH];
    char buf2[MAX_INPUT_LENGTH];
    struct obj_data *obj;
    int cost;
    extern char *drinks[];
    int found;

    if ( !is_ok( keeper, ch, shop_nr ) )
	return;

    strcpy( buf, "[Price] Item\n\r" );
    found = FALSE;
    for ( obj = keeper->carrying; obj; obj = obj->next_content )
    {
	if ( !CAN_SEE_OBJ( ch, obj ) || obj->obj_flags.cost <= 0 )
	    continue;

	found = TRUE;

	cost = (int) ( obj->obj_flags.cost * shop_index[shop_nr].profit_buy );
	if ( GET_ITEM_TYPE(obj) == ITEM_DRINKCON && obj->obj_flags.value[1] )
	{
	    sprintf( buf2, "[%5d] %s of %s.\n\r",
		cost, obj->short_description,
		drinks[obj->obj_flags.value[2]] );
	}
	else
	{
	    sprintf( buf2, "[%5d] %s.\n\r",
		cost, obj->short_description );
	}

	buf2[8] = UPPER(buf2[8]);
	strcat( buf, buf2 );
    }

    if ( !found )
	send_to_char( "You can't buy anything here!\n\r", ch );
    else
	send_to_char( buf, ch );

    return;
}



/*
 * Spec proc for shop keepers.
 */
int shop_keeper( struct char_data *ch, int cmd, char *arg )
{
    struct char_data *keeper;
    int shop_nr;

    /*
     * Find a shop keeper in the room.
     */
    for ( keeper = world[ch->in_room].people;
	keeper != NULL;
	keeper = keeper->next_in_room )
    {
	if ( IS_MOB(keeper) && mob_index[keeper->nr].func == shop_keeper )
	    goto LFound1;
    }
    log( "Shop_keeper: keeper not found." );
    return FALSE;

 LFound1:
    for ( shop_nr = 0; shop_nr < max_shop; shop_nr++ )
    {
	if ( shop_index[shop_nr].keeper == keeper->nr )
	    goto LFound2;
    }
    log( "Shop_keeper: shop_nr not found." );
    return FALSE;

 LFound2:
    if ( ch->in_room != shop_index[shop_nr].in_room )
	return FALSE;

    switch ( cmd )
    {
    default: return FALSE;
    case 56: shopping_buy   ( arg, ch, keeper, shop_nr ); break;
    case 57: shopping_sell  ( arg, ch, keeper, shop_nr ); break;
    case 58: shopping_value ( arg, ch, keeper, shop_nr ); break;
    case 59: shopping_list  ( arg, ch, keeper, shop_nr ); break;
    }

    return TRUE;
}



void boot_the_shops()
{
    char *buf;
    int temp;
    int count;
    FILE *fp;

    if ( ( fp = fopen( SHOP_FILE, "r" ) ) == NULL )
    {
	perror( SHOP_FILE );
	exit( 1 );
    }

    max_shop = 0;

    for ( ;; )
    {
	buf = fread_string( fp );
	if ( *buf == '$' )
	    break;
	if ( *buf != '#' )
	    continue;

	if ( max_shop >= MAX_SHOP )
	{
	    perror( "Too many shops.\n" );
	    exit( 1 );
	}

	/*
	 * Ignore "producing" list.
	 */
	for ( count = 0; count < 6; count++ )
	    fscanf( fp, "%d \n", &temp );

	fscanf( fp, "%f \n", &shop_index[max_shop].profit_buy  );
	fscanf( fp, "%f \n", &shop_index[max_shop].profit_sell );
	for( count = 0; count < MAX_TRADE; count++ )
	{
	    fscanf( fp, "%d \n", &shop_index[max_shop].type[count] );
	}

	shop_index[max_shop].no_such_item1	= fread_string( fp );
	shop_index[max_shop].no_such_item2	= fread_string( fp );
	shop_index[max_shop].do_not_buy		= fread_string( fp );
	shop_index[max_shop].missing_cash1	= fread_string( fp );
	shop_index[max_shop].missing_cash2	= fread_string( fp );
	shop_index[max_shop].message_buy	= fread_string( fp );
	shop_index[max_shop].message_sell	= fread_string( fp );

	fscanf( fp, "%d \n", &temp );		/* Temper	*/
	fscanf( fp, "%d \n", &temp );		/* Temper	*/

	fscanf( fp, "%d \n", &temp );
	shop_index[max_shop].keeper	= real_mobile( temp );

	fscanf( fp, "%d \n", &temp );		/* With_whom	*/

	fscanf( fp, "%d \n", &temp );
	shop_index[max_shop].in_room	= real_room( temp );

	fscanf( fp, "%d \n", &shop_index[max_shop].open1   );
	fscanf( fp, "%d \n", &shop_index[max_shop].close1  );
	fscanf( fp, "%d \n", &shop_index[max_shop].open2   );
	fscanf( fp, "%d \n", &shop_index[max_shop].close2  );

	max_shop++;
    }

    fclose( fp );
}



void assign_the_shopkeepers( )
{
    int shop_nr;

    for ( shop_nr = 0; shop_nr < max_shop; shop_nr++ )
	mob_index[shop_index[shop_nr].keeper].func = shop_keeper;

    return;
}