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

#include "structs.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "utils.h"

#define SHOP_FILE       "tinyworld.shp"
#define MAX_TRADE        5
#define MAX_PROD         5

#define MAX_SALE_ITEMS 100

#define MAXSHOPMESS      4

char *no_such_item1[]={
  "%s Haven't got that on storage - try LIST.",
  "%s I don't have any of those.",
  "%s I don't sell those, try Home Depot.",
  "%s I don't deal in that kind crap.",
  "%s Do you see any of those around here?  I don't."
};
char *no_such_item2[]={
  "%s You don't seem to have that.",
  "%s You don't have any.",
  "%s Where are you hiding it?",
  "%s I don't think you have any.",
  "%s Who are you trying to kid?"
};
char *do_not_buy[]={
  "%s I don't buy THAT. Try another shop.",
  "%s I don't want it.",
  "%s I don't need it.",
  "%s Looks like trash to me, no thanks.",
  "%s Go drop that in the dump."
};
char *missing_cash[]={
  "%s You seem a little short on coins, pal.",
  "%s Sorry, I don't give credit.",
  "%s Go stop at the bank first.",
  "%s On your budget?  No chance.",
  "%s My price is too high for you.  Scram."
};
char *message_buy[]={
  "%s That'll be %d coins, thank you.",
  "%s Yours for only %d coins.",
  "%s I'll take your %d coins, thanks.",
  "%s A shrewd investment for only %d coins.",
  "%s Here it is.  I'll take %d coins."
};
char *message_sell[]={
  "%s You'd get %d coins for it.",
  "%s That's worth %d coins.",
  "%s That's worth %d coins.",
  "%s That's worth %d coins.",
  "%s I'd give you %d coins and not a penny more."
};
struct sale_item {
  int n;
  int k;
  struct obj_data *p;
} salelist[MAX_SALE_ITEMS];

/* extern struct str_app_type str_app[]; */
extern struct index_data *mob_index;
extern struct index_data *obj_index;
extern char *apply_types[];

char *fread_string(FILE *fl);

void show_item_list(char buffer[], int shop_nr);
int fill_item_list(struct obj_data *h);
void object_identify_to_char(struct char_data *ch, struct obj_data *o);

struct shop_data
{
  int producing[MAX_PROD];/* Which item to produce (virtual)      */
  float profit_buy;       /* Factor to multiply cost with.        */
  float profit_sell;      /* Factor to multiply cost with.        */
  byte type[MAX_TRADE];   /* Which item to trade.                 */
  int keeper;             /* The mobil who owns the shop (virtual)*/
  int with_who;    /* Who does the shop trade with?  */
  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;
int number_of_shops;

int is_ok(struct char_data *keeper, struct char_data *ch, int shop_nr)
{
  if (shop_index[shop_nr].open1>time_info.hours){
    do_say(keeper,
    "Come back later!",17);
    return(FALSE);
  } else if (shop_index[shop_nr].close1<time_info.hours)
    if (shop_index[shop_nr].open2>time_info.hours){
      do_say(keeper,
      "Sorry, we have closed, but come back later.",17);
      return(FALSE);
    } else if (shop_index[shop_nr].close2<time_info.hours){
      do_say(keeper,
      "Sorry, come back tomorrow.",17);
      return(FALSE);
    };

  if(!(CAN_SEE(keeper,ch))) {
    do_say(keeper, "I don't trade with someone I can't see!",17);
    return(FALSE);
  };

  switch(shop_index[shop_nr].with_who){
    case 0 : return(TRUE);
    case 1 : return(TRUE);
    default : return(TRUE);
  };
}

int trade_with(struct obj_data *item, int shop_nr)
{
  int counter;

  if(item->obj_flags.cost < 1)
     return(FALSE);
  if(shop_index[shop_nr].type[0] == -2)
    return TRUE;
  for(counter=0;counter<MAX_TRADE;counter++)
    if(shop_index[shop_nr].type[counter]==item->obj_flags.type_flag)
      return(TRUE);
  return(FALSE);
}

int shop_producing(struct obj_data *item, int shop_nr)
{
  int counter;

  if(item->item_number<0) return(FALSE);

  for(counter=0;counter<MAX_PROD;counter++)
    if (shop_index[shop_nr].producing[counter] == item->item_number)
      return(TRUE);
  return(FALSE);
}

void shopping_appraise( char *arg, struct char_data *ch,
   struct char_data *keeper, int shop_nr)
{
  char argm[100], buf[MAX_STRING_LENGTH];
  struct obj_data *temp1;
  int k,n;

  if(!(is_ok(keeper,ch,shop_nr)))
    return;
  one_argument(arg, argm);
  if(!(*argm)) {
    sprintf(buf,"%s what do you want to appraise?",GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }
  if(isdigit(*argm)){
    n = fill_item_list(keeper->carrying);
    k = atoi(argm) - 1;
    if((k < 0)||(k >= n)){
      sprintf(buf, no_such_item1[number(0,MAXSHOPMESS)],GET_NAME(ch));
      do_tell(keeper,buf,19);
      return;
    }
    temp1 = salelist[k].p;
  } else if(!( temp1 = get_obj_in_list_vis(ch,argm,keeper->carrying))) {
    sprintf(buf, no_such_item1[number(0,MAXSHOPMESS)],GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }
  object_identify_to_char(ch,temp1);
  return; 
}
void shopping_buy( char *arg, struct char_data *ch,
   struct char_data *keeper, int shop_nr)
{
  char argm[100], buf[MAX_STRING_LENGTH];
  struct obj_data *temp1;
  struct char_data *temp_char;
  int k,n;

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

  one_argument(arg, argm);
  if(!(*argm)) {
    sprintf(buf,"%s what do you want to buy??",GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }
  if(isdigit(*argm)){
    n = fill_item_list(keeper->carrying);
    k = atoi(argm) - 1;
    if((k < 0)||(k >= n)){
      sprintf(buf, no_such_item1[number(0,MAXSHOPMESS)],GET_NAME(ch));
      do_tell(keeper,buf,19);
      return;
    }
    temp1 = salelist[k].p;
  } else if(!( temp1 = get_obj_in_list_vis(ch,argm,keeper->carrying))) {
    sprintf(buf, no_such_item1[number(0,MAXSHOPMESS)],GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }

  if(temp1->obj_flags.cost <= 0) {
    sprintf(buf, no_such_item1[number(0,MAXSHOPMESS)],GET_NAME(ch));
    do_tell(keeper,buf,19);
    extract_obj(temp1);
    return;
  }

  if(GET_GOLD(ch) < (int) (temp1->obj_flags.cost*
    shop_index[shop_nr].profit_buy) && GET_LEVEL(ch)<(IMO+1)) {
    sprintf(buf, missing_cash[number(0,MAXSHOPMESS)],GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }
  
  if ((IS_CARRYING_N(ch) + 1 > CAN_CARRY_N(ch))) {
    sprintf(buf,"%s: You can't carry that many items.\n\r",fname(temp1->name));
    send_to_char(buf, ch);
    return;
  }

  if ((IS_CARRYING_W(ch) + temp1->obj_flags.weight) > CAN_CARRY_W(ch)) {
    strcpy(buf,"I guess it's too heavy for you.\n\r");
    send_to_char(buf, ch);
    return;
  }
  act("$n buys $p.", FALSE, ch, temp1, 0, TO_ROOM);
  sprintf(buf, message_buy[number(0,MAXSHOPMESS)], GET_NAME(ch),
    (int) (temp1->obj_flags.cost* shop_index[shop_nr].profit_buy));
  do_tell(keeper,buf,19);
  sprintf(buf,"You now have %s.\n\r", temp1->short_description);
  send_to_char(buf,ch);
  if(GET_LEVEL(ch) <= IMO)
    GET_GOLD(ch)-=(int)(temp1->obj_flags.cost* shop_index[shop_nr].profit_buy);

  if(shop_producing(temp1,shop_nr))
    temp1 = read_object(temp1->item_number, REAL);
  else
    obj_from_char(temp1);
  obj_to_char(temp1,ch);
  return; 
}

void shopping_sell( char *arg, struct char_data *ch,
   struct char_data *keeper,int shop_nr)
{
  char argm[100], buf[MAX_STRING_LENGTH];
  struct obj_data *temp1;
  struct char_data *temp_char;

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

  one_argument(arg, argm);

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

  if(!( temp1 = get_obj_in_list_vis(ch,argm,ch->carrying))) {
    sprintf(buf, no_such_item2[number(0,MAXSHOPMESS)] ,GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }

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

  act("$n sells $p.", FALSE, ch, temp1, 0, TO_ROOM);

  sprintf(buf,message_sell[number(0,MAXSHOPMESS)],
    GET_NAME(ch),
    (int) (temp1->obj_flags.cost* shop_index[shop_nr].profit_sell));
  do_tell(keeper,buf,19);
  sprintf(buf,"The shopkeeper now has %s.\n\r", temp1->short_description);
  send_to_char(buf,ch);
  GET_GOLD(ch)+=(int)(temp1->obj_flags.cost*shop_index[shop_nr].profit_sell);

  obj_from_char(temp1);
  obj_to_char(temp1,keeper);
  return;
}

void shopping_value( char *arg, struct char_data *ch, 
  struct char_data *keeper, int shop_nr)
{
  char argm[100], buf[MAX_STRING_LENGTH];
  struct obj_data *temp1;

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

  one_argument(arg, argm);

  if(!(*argm)) {
    sprintf(buf,"%s What do you want me to valuate??", GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }

  if(!( temp1 = get_obj_in_list_vis(ch,argm,ch->carrying))) {
    sprintf(buf,no_such_item2[number(0,MAXSHOPMESS)],
      GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }

  if(!(trade_with(temp1,shop_nr))) {
    sprintf(buf, do_not_buy[number(0,MAXSHOPMESS)], GET_NAME(ch));
    do_tell(keeper,buf,19);
    return;
  }
  sprintf(buf,message_sell[number(0,MAXSHOPMESS)],
    GET_NAME(ch),(int) (temp1->obj_flags.cost*
      shop_index[shop_nr].profit_sell));
  do_tell(keeper,buf,19);

  return;
}

void shopping_list( char *arg, struct char_data *ch,
   struct char_data *keeper, int shop_nr)
{
  char buffer[8*MAX_STRING_LENGTH];
  int n;

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

  n = fill_item_list(keeper->carrying);
  if(n > 0){
    show_item_list(buffer,shop_nr);
    page_string(ch->desc, buffer, 1);
  } else
    send_to_char("Nothing for sale.\n\r",ch);
}

int shop_keeper(struct char_data *ch, int cmd, char *arg)
{
  char argm[100], buf[MAX_STRING_LENGTH];
  struct obj_data *temp1;
  struct char_data *temp_char;
  struct char_data *keeper;
  int shop_nr,n;

  keeper = 0;
  for (temp_char = world[ch->in_room].people; (!keeper) && (temp_char) ; 
    temp_char = temp_char->next_in_room)
  if (IS_MOB(temp_char))
    if (mob_index[temp_char->nr].func == shop_keeper)
      keeper = temp_char;
  for(shop_nr=0 ; shop_index[shop_nr].keeper != keeper->nr; shop_nr++);
  if((cmd == 298) && (ch->in_room == 
     real_room(shop_index[shop_nr].in_room)))
  {
    shopping_appraise(arg,ch,keeper,shop_nr);
    return(TRUE);
  }
  if((cmd == 56) && (ch->in_room == 
     real_room(shop_index[shop_nr].in_room)))
  {
    shopping_buy(arg,ch,keeper,shop_nr);
    return(TRUE);
  }
  if((cmd ==57 ) && (ch->in_room == 
     real_room(shop_index[shop_nr].in_room))) /* Sell */
  {
    shopping_sell(arg,ch,keeper,shop_nr);
    return(TRUE);
  }
  if((cmd == 58) && (ch->in_room == 
     real_room(shop_index[shop_nr].in_room))) /* value */
  {
    shopping_value(arg,ch,keeper,shop_nr);
    return(TRUE);
  }
  if((cmd==59)&&(ch->in_room==real_room(shop_index[shop_nr].in_room))){
    /* List */
    shopping_list(arg,ch,keeper,shop_nr);
    return(TRUE);
  }
  if (cmd == 156){
    send_to_char("Oops.\n\r",ch);
    hit(keeper,ch,-1);
    return(TRUE);
  }
  if ((cmd==84) || (cmd==207) || (cmd==172)) {   /* Cast, recite, use */
    act("$N tells you 'No magic here - kid!'.", FALSE, ch, 0, keeper, TO_CHAR);
    return TRUE;
  }
  return(FALSE);
}

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

  if (!(shop_f = fopen(SHOP_FILE, "r")))
  {
    perror("Error in boot shop\n");
    exit(0);
  }

  number_of_shops = 0;

  for(;;) {
    buf = fread_string(shop_f);
    if(*buf == '#') {
      if(!number_of_shops)
        CREATE(shop_index, struct shop_data, 1);
      else if(!(shop_index=
        (struct shop_data*) realloc(shop_index,(number_of_shops + 1)*
        sizeof(struct shop_data))))
        {
          perror("Error in boot shop\n");
          exit(0);
        }
      for(count=0;count<MAX_PROD;count++) {
        fscanf(shop_f,"%d \n", &temp);
        if (temp >= 0)
          shop_index[number_of_shops].producing[count]= real_object(temp);
        else
          shop_index[number_of_shops].producing[count]= temp;
      }
      fscanf(shop_f,"%f \n",
        &shop_index[number_of_shops].profit_buy);
      fscanf(shop_f,"%f \n",
        &shop_index[number_of_shops].profit_sell);
      for(count=0;count<MAX_TRADE;count++) {
         fscanf(shop_f,"%d \n", &temp);
         shop_index[number_of_shops].type[count] = (byte) temp;
      }
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].keeper);
      shop_index[number_of_shops].keeper = 
        real_mobile(shop_index[number_of_shops].keeper);
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].with_who);
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].in_room);
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].open1);
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].close1);
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].open2);
      fscanf(shop_f,"%d \n", &shop_index[number_of_shops].close2);
      number_of_shops++;
    } else if(*buf == '$'){
        break;
    }
  }
  fclose(shop_f);
}
void assign_the_shopkeepers()
{
  int i;
  
  for(i=0 ; i<number_of_shops ; i++)
    mob_index[shop_index[i].keeper].func = shop_keeper;
}

int fill_item_list(struct obj_data *h)
{
  struct obj_data *p;
  int f,i,j,k,n=0;

  for(p=h;p;p=p->next_content){
    f = 0;
    k = p->item_number;
    for(i=0;i<n;i++)
      if(salelist[i].k == k){
        f = 1;
        salelist[i].n++;
        break;
      }
    if(!f){
      salelist[n].n = 1;
      salelist[n].k = k;
      salelist[n].p = p;
      n++;
      if(n==(MAX_SALE_ITEMS-1))
        break;
    }
  }
  salelist[n].p = 0;
  return n;
}
void show_item_list(char buffer[], int shop_nr)
{
  int i;
  char tbuf[256];
  struct obj_data *p;

  strcpy(buffer,"You can buy:\n\r");
  for(i=0;salelist[i].p;i++){
    p = salelist[i].p;
    sprintf(tbuf,"%2d: %-30.30s%7d (%2d)\n\r",
      i+1,
      p->short_description,
      (int)(p->obj_flags.cost * shop_index[shop_nr].profit_buy),
      salelist[i].n);
    strcat(buffer,tbuf);
  }
}
void object_identify_to_char(struct char_data *ch, struct obj_data *j)
{
  int i,virtual;
  char buf[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH];
  extern char *item_types[];
  extern char *wear_bits[];
  extern char *extra_bits[];
  extern char *drinks[];

  virtual = (j->item_number >= 0) ? obj_index[j->item_number].virtual : 0;
  sprintf(buf,"%s: ",j->short_description);
  sprinttype(GET_ITEM_TYPE(j),item_types,buf2);
  strcat(buf,buf2);
  strcat(buf,"\n\r");
  send_to_char(buf, ch);
  sprintf(buf,"Wgt: %d, Value: %d, Rent Lev: %d\n\r",
      j->obj_flags.weight,j->obj_flags.cost,j->obj_flags.rentlevel);
  send_to_char(buf, ch);
  send_to_char("Worn: ", ch);
  sprintbit(0x7ffffffe & j->obj_flags.wear_flags,wear_bits,buf);
  strcat(buf,"\n\r");
  send_to_char(buf, ch);
  switch(GET_ITEM_TYPE(j)){
    case ITEM_WEAPON:
      sprintf(buf,"Damage dice: %dd%d\n\r",
        j->obj_flags.value[1],
        j->obj_flags.value[2]);
      break;
    case ITEM_ARMOR:
      sprintf(buf,"Armor value: %d\n\r",
        j->obj_flags.value[0]);
      break;
    default:
      strcpy(buf,"More on this type of item later.\n\r");
      break;
  }
  send_to_char(buf, ch);
  for (i=0;i<MAX_OBJ_AFFECT;++i) {
    if(!j->affected[i].location)
      continue;
    sprinttype(j->affected[i].location,apply_types,buf2);
    sprintf(buf,"  Affects: %s by %d\n\r",buf2,j->affected[i].modifier);
    send_to_char(buf, ch);      
  }
}