/**************************************************************************
*  File: ibt.c                                             Part of tbaMUD *
*  Usage: Loading/saving/editing of Ideas, Bugs and Typos lists           *
*                                                                         *
*  All rights reserved.  See license for complete information.            *
*                                                                         *
*  Created by Vash (aka Frenze) for Trigun MUD and the tbaMUD codebase    *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
**************************************************************************/

//#if defined(macintosh)
//#include <types.h>
//#include <time.h>
//#else
//#include <sys/types.h>
//#include <sys/time.h>
//#endif
#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "handler.h"
#include "interpreter.h"
//#include "genobj.h"
#include "constants.h"
//#include "fight.h"
#include "screen.h"
#include "act.h"
#include "utils.h"
#include "ibt.h"
#include "oasis.h"
#include "improved-edit.h"
#include "modify.h"

IBT_DATA * first_bug  = NULL;
IBT_DATA * last_bug   = NULL;
IBT_DATA * first_idea = NULL;
IBT_DATA * last_idea  = NULL;
IBT_DATA * first_typo = NULL;
IBT_DATA * last_typo  = NULL;

const char *ibt_types[]={
  "Bug",
  "Idea",
  "Typo",
  "\n"
};

/* Internal (static) functions */
static IBT_DATA *new_ibt(void);
static void free_ibt_list(IBT_DATA *first_ibt, IBT_DATA *last_ibt);
static IBT_DATA *read_ibt(char *filename, FILE *fp);
static IBT_DATA *get_first_ibt(int mode);
static IBT_DATA *get_last_ibt(int mode);
static bool is_ibt_logger(IBT_DATA *ibtData, struct char_data *ch);
/* Internal (static) OLC functions */
static void ibtedit_setup(struct descriptor_data *d);
static void ibtedit_save(struct descriptor_data *d);
static void ibtedit_disp_main_menu(struct descriptor_data *d);
static void ibtedit_disp_flags(struct descriptor_data *d);

static IBT_DATA *new_ibt(void)
{
   int i;
   IBT_DATA *ibtData;

   CREATE( ibtData, IBT_DATA, 1);

   ibtData->next       = NULL;
   ibtData->prev       = NULL;
   ibtData->name       = NULL;
   ibtData->text       = NULL;
   ibtData->body       = NULL;
   ibtData->notes      = NULL;
   ibtData->level      = 0;
   ibtData->id_num     = NOBODY;
   ibtData->room       = NOWHERE;

   for (i=0; i<IBT_ARRAY_MAX; i++)
     ibtData->flags[i]   = 0;

   return ibtData;
}

static void free_ibt_list(IBT_DATA *first_ibt, IBT_DATA *last_ibt)
{
  IBT_DATA **top_ptr, **bot_ptr, *this_ibt;

  if ((first_ibt == NULL) || (last_ibt == NULL)) return;

  top_ptr = &(first_ibt);
  bot_ptr = &(last_ibt);

  while (first_ibt) {
    this_ibt = first_ibt;

    /* Disconnect from the list */
    UNLINK(this_ibt, first_ibt, last_ibt, next, prev);

    /* Free the strings first */
    if (this_ibt->body)  STRFREE(this_ibt->body);
    if (this_ibt->name)  STRFREE(this_ibt->name);
    if (this_ibt->text)  STRFREE(this_ibt->text);
    if (this_ibt->notes) STRFREE(this_ibt->notes);

    /* And free the struct */
    DISPOSE(this_ibt);
  }

  *(top_ptr) = NULL;
  *(bot_ptr) = NULL;
}
static IBT_DATA *read_ibt( char *filename, FILE *fp )
{
   IBT_DATA *ibtData;
   char *word, *id_num=NULL;
   char buf[MAX_STRING_LENGTH];
   bool fMatch, flgCheck;
   char letter;

   do
   {
      letter = getc( fp );
      if( feof(fp) )
      {
         fclose( fp );
         return NULL;
      }
   }
   while( isspace(letter) );
   ungetc( letter, fp );

   ibtData = new_ibt();
   ibtData->name       = STRALLOC("");
   ibtData->text       = STRALLOC("");
   ibtData->body       = STRALLOC("");

   for ( ; ; )
   {
        word   = feof( fp ) ? "End" : fread_word( fp );
        fMatch = FALSE;

        switch ( UPPER(word[0]) )
        {
          case 'B':
            if (!str_cmp(word, "Body"))  STRFREE(ibtData->body);
            KEY("Body",     ibtData->body,   fread_clean_string( fp, buf ));
            break;

          case 'E':
            if (!str_cmp(word, "End"))
            {
              if ( id_num )
                ibtData->id_num = atol(id_num);

              if ( !ibtData->name )
                ibtData->name = STRALLOC("");
              if ( !ibtData->text )
                ibtData->text = STRALLOC("");
              if ( !ibtData->body )
                ibtData->body = STRALLOC("");
              if ( !ibtData->notes )
                ibtData->notes = STRALLOC("");
              return ibtData;
            }
            break;

          case 'F':
            KEY("Flags",    flgCheck,         fread_flags(fp, ibtData->flags, IBT_ARRAY_MAX));
            break;

          case 'I':
            TXT_KEY("IdNum", id_num, fread_line(fp));
            break;

          case 'L':
            KEY("Level",    ibtData->level,   fread_number(fp));
            break;

          case 'N':
            if (!str_cmp(word, "Name") && ibtData->name)   STRFREE(ibtData->name);
            if (!str_cmp(word, "Notes") && ibtData->notes)  STRFREE(ibtData->notes);
            TXT_KEY("Name",     ibtData->name,    fread_line( fp ));
            KEY("Notes",        ibtData->notes,   fread_clean_string( fp, buf ));
            break;

          case 'R':
            KEY("Room",     ibtData->room,    fread_number(fp));
            break;

          case 'T':
            if (!str_cmp(word, "Text"))  STRFREE(ibtData->text);
            TXT_KEY("Text",     ibtData->text,    fread_line( fp ));
            break;

          case '*':  /* Handle comments */
            fread_to_eol(fp);
            fMatch = TRUE;
            break;

          default:
            log("SYSERR: Invalid keyword (%s) in IBT file", word);
            break;
        }
        if ( !fMatch )
        {
          fread_to_eol(fp);
          log( "read_ibt (%s): no match: %s - aborting line!", filename, word );
        }
   }
   log ("read_ibt: Aborted! Returning NULL");
   if ( ibtData->name)
         STRFREE( ibtData->name);
   if ( ibtData->text)
         STRFREE( ibtData->text);
   if ( ibtData->body)
         STRFREE( ibtData->body);

   DISPOSE( ibtData);
   return NULL;
}

void load_ibt_file(int mode)
{
   char filename[MAX_INPUT_LENGTH];
   FILE *fp;
   IBT_DATA *ibtData, *first_ibt, *last_ibt;

   switch(mode) {
     case SCMD_BUG : sprintf( filename, "%s",BUGS_FILE );
                     first_ibt = first_bug;
                     last_ibt  = last_bug;
                     break;
     case SCMD_IDEA: sprintf( filename, "%s",IDEAS_FILE );
                     first_ibt = first_idea;
                     last_ibt  = last_idea;
                     break;
     case SCMD_TYPO: sprintf( filename, "%s",TYPOS_FILE );
                     first_ibt = first_typo;
                     last_ibt  = last_typo;
                     break;
     default       : log("SYSERR: Invalid mode (%d) in load_ibt_file", mode);
                     return;
   }

   /* Remove and free the old list */
   if ((first_ibt) && (last_ibt)) {
     free_ibt_list(first_ibt, last_ibt);
     first_ibt = NULL;
     last_ibt = NULL;
   }

   if( ( fp = fopen( filename, "r" ) ) == NULL ) {
      log("No File: %s", filename);
      return;
   }

   while( ( ibtData = read_ibt( filename, fp ) ) != NULL ) {
     switch(mode) {
       case SCMD_BUG : LINK( ibtData, first_bug, last_bug, next, prev );
                       break;
       case SCMD_IDEA: LINK( ibtData, first_idea, last_idea, next, prev );
                       break;
       case SCMD_TYPO: LINK( ibtData, first_typo, last_typo, next, prev );
                       break;
     }
   }

   return;
}

void save_ibt_file(int mode)
{
   IBT_DATA *ibtData, *first_ibt, *last_ibt;
   FILE *fp;
   char filename[256];

   switch(mode) {
     case SCMD_BUG : sprintf( filename, "%s",BUGS_FILE );
                     first_ibt = first_bug;
                     last_ibt  = last_bug;
                     break;
     case SCMD_IDEA: sprintf( filename, "%s",IDEAS_FILE );
                     first_ibt = first_idea;
                     last_ibt  = last_idea;
                     break;
     case SCMD_TYPO: sprintf( filename, "%s",TYPOS_FILE );
                     first_ibt = first_typo;
                     last_ibt  = last_typo;
                     break;
     default       : log("SYSERR: Invalid mode (%d) in save_ibt_file", mode);
                     return;
   }

   if (( fp = fopen(filename,"w")) == NULL)
   {
     log("SYSERR: Unable to open IBT file for writing in save_ibt_file");
     log("        IBT File: %s", filename);
     return;
   }
   else
   {
     for (ibtData = first_ibt;ibtData;ibtData=ibtData->next)
     {
       if (ibtData->text && *(ibtData->text))
         fprintf(fp,"Text      %s~\n",ibtData->text);
       if (ibtData->body && *(ibtData->body))
         fprintf(fp,"Body      %s~\n",ibtData->body);
       if (ibtData->name && *(ibtData->name))
         fprintf(fp,"Name      %s~\n",ibtData->name);
       if (ibtData->notes && *(ibtData->notes))
         fprintf(fp,"Notes     %s~\n",ibtData->notes);
       if (ibtData->id_num != NOBODY)
         fprintf(fp,"IdNum     %ld\n",ibtData->id_num);
       fprintf(fp,"Level     %d\n",ibtData->level);
       fprintf(fp,"Room      %d\n",ibtData->room);
       fprintf(fp,"Flags     %d %d %d %d\n",ibtData->flags[0],ibtData->flags[1],
                                            ibtData->flags[2],ibtData->flags[3]);
       fprintf(fp,"End\n");
     }
     fclose(fp);
     return;
   }
}

static IBT_DATA *get_first_ibt(int mode)
{
  IBT_DATA *first_ibt = NULL;

  switch(mode) {
    case SCMD_BUG : first_ibt = first_bug;
                    break;
    case SCMD_IDEA: first_ibt = first_idea;
                    break;
    case SCMD_TYPO: first_ibt = first_typo;
                    break;
    default       : log("SYSERR: Invalid mode (%d) in get_first_ibt", mode);
                    break;
  }
  return (first_ibt);
}

static IBT_DATA *get_last_ibt(int mode)
{
  IBT_DATA *last_ibt = NULL;

  switch(mode) {
    case SCMD_BUG : last_ibt = last_bug;
                    break;
    case SCMD_IDEA: last_ibt = last_idea;
                    break;
    case SCMD_TYPO: last_ibt = last_typo;
                    break;
    default       : log("SYSERR: Invalid mode (%d) in get_last_ibt", mode);
                    break;
  }
  return (last_ibt);
}
IBT_DATA *get_ibt_by_num(int mode, int target_num)
{
  int no=0;
  IBT_DATA *target_ibt, *first_ibt;

  if ((first_ibt = get_first_ibt(mode)) == NULL) return NULL;

  for (target_ibt=first_ibt;target_ibt;target_ibt=target_ibt->next) {
    no++;
    if (no==target_num) {
      return target_ibt;
    }
  }
  return NULL;
}

/* Search the IBT list, and return true if ibt is found there */
bool ibt_in_list(int mode, IBT_DATA *ibt)
{
  IBT_DATA *target_ibt, *first_ibt;

  if ((first_ibt = get_first_ibt(mode)) == NULL) return FALSE;

  for (target_ibt=first_ibt;target_ibt;target_ibt=target_ibt->next) {
    if (target_ibt == ibt) {
      return TRUE;
    }
  }
  return FALSE;
}

/* Free up an IBT struct, removing it from the list if necessary */
/* returns TRUE on success                                       */
bool free_ibt(int mode, IBT_DATA *ibtData)
{
  if (ibtData == NULL) return FALSE;

  /* If this IBT is in the IBT list, take it out */
  if (ibt_in_list(mode, ibtData)) {
	switch(mode) {
      case SCMD_BUG : UNLINK(ibtData,first_bug,last_bug,next,prev);
                      break;
      case SCMD_IDEA: UNLINK(ibtData,first_idea,last_idea,next,prev);
                      break;
      case SCMD_TYPO: UNLINK(ibtData,first_typo,last_typo,next,prev);
                      break;
    }
  }

  /* Free the strings first */
  if (ibtData->body) STRFREE(ibtData->body);
  if (ibtData->name) STRFREE(ibtData->name);
  if (ibtData->text) STRFREE(ibtData->text);

  /* And free the struct */
  DISPOSE(ibtData);

  return TRUE;
}

/* Return TRUE if 'ch' is the person who logged the IBT */
static bool is_ibt_logger(IBT_DATA *ibtData, struct char_data *ch)
{
  if ( ch && !IS_NPC(ch) && ibtData ) {

    /* Check the ID number first (in case of name change)   */
    if ((ibtData->id_num != NOBODY) && (ibtData->id_num == GET_IDNUM(ch)))
      return TRUE;

    /* Check the name next (in case of deletion/recreation) */
    if (strcmp(ibtData->name, GET_NAME(ch)) == 0)
      return TRUE;
  }
  return FALSE;
}

ACMD(do_ibt)
{
  char arg[MAX_STRING_LENGTH], arg2[MAX_STRING_LENGTH];
  char buf[MAX_STRING_LENGTH], *arg_text, imp[30];
  int  i, num_res, num_unres;
  IBT_DATA *ibtData, *first_ibt, *last_ibt;
  int ano=0;

  if (IS_NPC(ch))
    return;

  arg_text  = one_argument(argument, arg);
  argument  = two_arguments(argument, arg, arg2);

  first_ibt = get_first_ibt(subcmd);
  last_ibt  = get_last_ibt(subcmd);

  if ((!*arg)){
    if (GET_LEVEL(ch) >= LVL_GRGOD){
      send_to_char(ch, "Usage: %s%s submit <header>%s\r\n"
                       "       %s%s list%s\r\n"
                       "       %s%s show <num>%s\r\n"
                       "       %s%s remove <num>%s\r\n"
                       "       %s%s edit <num>%s\r\n"
                       "       %s%s resolve <num>%s\r\n",
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM);
      return;
    } else if (GET_LEVEL(ch) >= LVL_IMMORT) {
      send_to_char(ch, "Usage: %s%s submit <header>%s\r\n"
                       "       %s%s list%s\r\n"
                       "       %s%s show <num>%s\r\n",
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM);
      return;
    } else {
      send_to_char(ch, "Usage: %s%s submit <header>%s\r\n"
                       "       %s%s list%s\r\n"
                       "       %s%s show <num>%s\r\n",
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM,
                               QYEL, CMD_NAME, QNRM);
      send_to_char(ch, "Note: Only %ss logged by you will be listed or shown.\r\n", CMD_NAME);
      return;
    }
  }
  else if(is_abbrev(arg,"show"))
  {
    if (!is_number(arg2)) {
      send_to_char(ch, "Show which %s?\r\n", CMD_NAME);
      return;
    }
    ano = atoi(arg2);

    if ((ibtData = get_ibt_by_num(subcmd, ano)) == NULL) {
      send_to_char(ch, "That %s doesn't exist.\r\n", CMD_NAME);
      return;
    } else {
      if ((GET_LEVEL(ch) < LVL_IMMORT) && (!is_ibt_logger(ibtData, ch))) {
        send_to_char(ch, "Sorry but you may only view %ss you have posted yourself.\n\r", ibt_types[subcmd]);
      } else {

        send_to_char(ch, "%s%s by %s%s\r\n",QCYN, ibt_types[subcmd], QYEL, ibtData->name);
        if (GET_LEVEL(ch) >= LVL_IMMORT) {
          send_to_char(ch, "%sLevel: %s%d\r\n",QCYN, QYEL, ibtData->level);
          send_to_char(ch, "%sRoom : %s%d\r\n",QCYN, QYEL, ibtData->room);
        }
        send_to_char(ch, "%sTitle: %s%s\r\n",QCYN, QYEL, ibtData->text);
        send_to_char(ch, "%s%s Details%s\r\n%s\r\n",QCYN, ibt_types[subcmd], QYEL, ibtData->body);
        if (ibtData->notes && *(ibtData->notes))
          send_to_char(ch, "%s%s Notes%s\r\n%s\r\n",QCYN, ibt_types[subcmd], QYEL, ibtData->notes);
        send_to_char(ch, "%s%s Status: %s%s%s%s\r\n",QCYN, ibt_types[subcmd],
                                       IBT_FLAGGED(ibtData, IBT_RESOLVED) ? QBGRN : QBRED,
                                       IBT_FLAGGED(ibtData, IBT_RESOLVED) ? "Resolved" : "Unresolved",
                                       IBT_FLAGGED(ibtData, IBT_INPROGRESS) ? " (In Progress)" : "",
                                       QNRM);
      }
    }

    return;
  }
  else if(is_abbrev(arg,"list"))
  {

    if (first_ibt)
    {
      if (GET_LEVEL(ch) < LVL_IMMORT) {
        send_to_char(ch,"%s No %s|%s Description\r\n", QCYN, QGRN, QCYN);
        send_to_char(ch,"%s ---|--------------------------------------------------%s\r\n", QGRN, QNRM);
      } else {
        send_to_char(ch,"%s No %s|%sName        %s|%sRoom  %s|%sLevel%s|%s Description\r\n", QCYN, QGRN, QCYN, QGRN, QCYN, QGRN, QCYN, QGRN, QCYN);
        send_to_char(ch,"%s ---|------------|------|-----|--------------------------------------------------%s\r\n", QGRN, QNRM);
      }
      i=num_res=num_unres=0;
      for (ibtData=first_ibt;ibtData;ibtData = ibtData->next) {
        i++;

        /* For mortals, skip IBT's that they didn't log */
        if ((GET_LEVEL(ch) < LVL_IMMORT) && !is_ibt_logger(ibtData,ch))
          continue;

        /* Set up the 'important' flag */
        if (IBT_FLAGGED(ibtData, IBT_IMPORTANT))
          sprintf(imp, "%s!%s", QBFRED, QNRM);
        else
          sprintf(imp, "%c", ' ');

        if (IBT_FLAGGED(ibtData, IBT_RESOLVED)) {
          if (GET_LEVEL(ch) < LVL_IMMORT) {
            send_to_char(ch, "%s%s%3d|%s%s\r\n",
                                  imp, QGRN, i, ibtData->text, QNRM);
          } else {
            send_to_char(ch, "%s%s%3d%s|%s%-12s%s|%s%6d%s|%s%5d%s|%s%s%s\r\n",
                                  imp, QGRN, i, QGRN,
                                  QGRN, ibtData->name, QGRN,
                                  QGRN, ibtData->room, QGRN,
                                  QGRN, ibtData->level, QGRN,
                                  QGRN, ibtData->text, QNRM);
          }
          num_res++;
        } else if (IBT_FLAGGED(ibtData, IBT_INPROGRESS)) {
          if (GET_LEVEL(ch) < LVL_IMMORT) {
            send_to_char(ch, "%s%s%3d%s|%s%s%s\r\n",
                                  imp, QYEL, i, QGRN,
                                  QYEL, ibtData->text, QNRM);
          } else {
            send_to_char(ch, "%s%s%3d%s|%s%-12s%s|%s%6d%s|%s%5d%s|%s%s%s\r\n",
                                  imp, QYEL, i, QGRN,
                                  QYEL, ibtData->name, QGRN,
                                  QYEL, ibtData->room, QGRN,
                                  QYEL, ibtData->level, QGRN,
                                  QYEL, ibtData->text, QNRM);
          }
          num_unres++;
        } else {
          if (GET_LEVEL(ch) < LVL_IMMORT) {
            send_to_char(ch, "%s%s%3d%s|%s%s%s\r\n",
                                  imp, QRED, i, QGRN,
                                  QRED, ibtData->text, QNRM);
          } else {
            send_to_char(ch, "%s%s%3d%s|%s%-12s%s|%s%6d%s|%s%5d%s|%s%s%s\r\n",
                                  imp, QRED, i, QGRN,
                                  QRED, ibtData->name, QGRN,
                                  QRED, ibtData->room, QGRN,
                                  QRED, ibtData->level, QGRN,
                                  QRED, ibtData->text, QNRM);
          }
          num_unres++;
        }
      }
      if ((num_res + num_unres) > 0) {
        send_to_char(ch,"\n\r%s%d %ss in file. %s%d%s resolved, %s%d%s unresolved%s\r\n",QCYN, i, CMD_NAME, QBGRN, num_res, QCYN, QBRED, num_unres, QCYN, QNRM);
        send_to_char(ch,"%s%ss in %sRED%s are unresolved %ss.\r\n", QCYN, ibt_types[subcmd], QRED, QCYN, CMD_NAME);
        send_to_char(ch,"%s%ss in %sYELLOW%s are in-progress %ss.\r\n", QCYN, ibt_types[subcmd], QYEL, QCYN, CMD_NAME);
        send_to_char(ch,"%s%ss in %sGREEN%s are resolved %ss.\r\n", QCYN, ibt_types[subcmd], QGRN, QCYN, CMD_NAME);
      } else {
        send_to_char(ch,"No %ss have been found that were reported by you!\r\n", CMD_NAME);
      }
      if (GET_LEVEL(ch) >= LVL_GRGOD) {
        send_to_char(ch,"%sYou may use %s remove, resolve or edit to change the list..%s\r\n", QCYN, CMD_NAME, QNRM);
      }
      send_to_char(ch,"%sYou may use %s%s show <number>%s to see more indepth about the %s.%s\r\n", QCYN, QYEL, CMD_NAME, QCYN, CMD_NAME, QNRM);
    } else {
      send_to_char(ch,"No %ss have been reported!\r\n", CMD_NAME);
    }
    return;
  }
  else if (is_abbrev(arg,"submit"))
  {
    if (!*arg_text) {
      send_to_char(ch, "You need to add a heading!\r\n");
      return;
    }
    switch (subcmd) {
      case SCMD_IDEA: SET_BIT_AR(PLR_FLAGS(ch), PLR_IDEA);
                      break;
      case SCMD_BUG : SET_BIT_AR(PLR_FLAGS(ch), PLR_BUG);
                      break;
      case SCMD_TYPO: SET_BIT_AR(PLR_FLAGS(ch), PLR_TYPO);
                      break;
      default       : log("Invalid subcmd (%d) in do_ibt", subcmd);
                      return;
	}
    SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING);

    ibtData = new_ibt();
    send_to_char(ch, "Write your %s.\r\n", CMD_NAME);
    send_editor_help(ch->desc);

    sprintf(buf, "$n starts to give %s %s.", TANA(CMD_NAME), CMD_NAME);
    act(buf, TRUE, ch, 0, 0, TO_ROOM);

    string_write(ch->desc, &(ibtData->body),MAX_IBT_LENGTH, 0, NULL);

    ibtData->room   = GET_ROOM_VNUM(IN_ROOM(ch));
    ibtData->level  = GET_LEVEL(ch);
    ibtData->text   = STRALLOC(arg_text);
    ibtData->name   = STRALLOC(GET_NAME(ch));
    ibtData->id_num = GET_IDNUM(ch);
    switch(subcmd) {
       case SCMD_BUG : LINK( ibtData, first_bug, last_bug, next, prev );
                       break;
       case SCMD_IDEA: LINK( ibtData, first_idea, last_idea, next, prev );
                       break;
       case SCMD_TYPO: LINK( ibtData, first_typo, last_typo, next, prev );
                       break;
    }
    mudlog(NRM,LVL_IMMORT, FALSE, "%s has posted %s %s!", GET_NAME(ch), TANA(CMD_NAME), CMD_NAME);
    return;
  }
  else if (is_abbrev(arg,"resolve"))
  {
    if (GET_LEVEL(ch) < LVL_GRGOD){
      send_to_char(ch, "%s what?\r\n", ibt_types[subcmd]);
      return;
    }

    if (!is_number(arg2)) {
      send_to_char(ch, "Resolve which %s?\r\n", CMD_NAME);
      return;
    }
    ano = atoi(arg2);

    if ((ibtData = get_ibt_by_num(subcmd, ano)) == NULL) {
      send_to_char(ch, "%s not found\r\n", ibt_types[subcmd]);
      return;
    } else {
      if (IBT_FLAGGED(ibtData, IBT_RESOLVED)){
        send_to_char(ch, "That %s has already been resolved!\r\n", CMD_NAME);
      } else {
        send_to_char(ch,"%s %d resolved!\r\n", ibt_types[subcmd], ano);
        SET_BIT_AR(IBT_FLAGS(ibtData), IBT_RESOLVED);
        if (CONFIG_IBT_AUTOSAVE) {
		  save_ibt_file(subcmd);
		}
      }
    }
    return;
  } else if (is_abbrev(arg,"remove")) {
    if (GET_LEVEL(ch) < LVL_GRGOD){
      send_to_char(ch, "%s what?\r\n", ibt_types[subcmd]);
      return;
    }

    if (!is_number(arg2)) {
      send_to_char(ch, "Remove which %s?\r\n", CMD_NAME);
      return;
    }
    ano = atoi(arg2);

    if ((ibtData = get_ibt_by_num(subcmd, ano)) == NULL) {
      send_to_char(ch, "%s not found\r\n", ibt_types[subcmd]);
      return;
    } else {
      if (free_ibt(subcmd, ibtData)) {
        send_to_char(ch,"%s%s Number %d removed.%s\r\n", QCYN, ibt_types[subcmd], ano, QNRM);
        if (CONFIG_IBT_AUTOSAVE) {
		  save_ibt_file(subcmd);
		}
      } else {
        send_to_char(ch,"%sUnable to remove %s %d!%s\r\n", QRED, CMD_NAME, ano, QNRM);
      }
    }
    return;
  } else if (is_abbrev(arg,"save")) {
    if (GET_LEVEL(ch) < LVL_GRGOD){
      send_to_char(ch, "%s what?\r\n", ibt_types[subcmd]);
      return;
    }
    save_ibt_file(subcmd);
    send_to_char(ch,"%s list saved.\r\n", ibt_types[subcmd]);
  } else if (is_abbrev(arg,"edit")) {
    if (GET_LEVEL(ch) < LVL_GRGOD){
      send_to_char(ch, "%s what?\r\n", ibt_types[subcmd]);
      return;
    }
    /* Pass control to the OLC without the 'edit' arg */
    do_oasis_ibtedit(ch, arg_text, cmd, subcmd);
  } else {
    if (GET_LEVEL(ch) < LVL_GRGOD){
      send_to_char(ch, "%s what?\r\n", ibt_types[subcmd]);
      send_to_char(ch, "Usage: %s submit <text>\r\n", ibt_types[subcmd]);
      return;
    } else {
      send_to_char(ch, "Usage:  %s (submit/list/show/remove/resolve)\r\n", CMD_NAME);
      return;
    }
  }
}

/* IBT Editor OLC */
/* OLC_VAL(d)  - The IBT 'mode' - Idea, Bug or Typo */
/* OLC_NUM(d)  - The IBT number (shown in 'list')   */
/* OLC_ZNUM(d) - Used as 'has changed' flag         */
ACMD(do_oasis_ibtedit)
{
  int number = NOTHING;
  struct descriptor_data *d;
  char buf1[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH], *buf3;

  /* No editing as a mob or while being forced. */
  if (IS_NPC(ch) || !ch->desc || STATE(ch->desc) != CON_PLAYING)
    return;

  /* Parse any arguments */
  buf3 = two_arguments(argument, buf1, buf2);

  if (!*buf1) {
    send_to_char(ch, "Specify a %s number to edit.\r\n", ibt_types[subcmd]);
    return;
  } else if (!isdigit(*buf1)) {
    send_to_char(ch, "Yikes!  Stop that, someone will get hurt!\r\n");
    return;
  }

  /* If a numeric argument was given (like a room number), get it. */
  if (number == NOTHING)
    number = atoi(buf1);

  /* Validate that the IBT number exists */
  if (get_ibt_by_num(subcmd, number) == NULL) {
    send_to_char(ch, "That %s number doesn't exist!\r\n", ibt_types[subcmd]);
    return;
  }

  /* Check that whatever it is isn't already being edited. */
  for (d = descriptor_list; d; d = d->next) {
    if (STATE(d) == CON_IBTEDIT) {
      if (d->olc && OLC_NUM(d) == number) {
        send_to_char(ch, "That %s is currently being edited by %s.\r\n",
          ibt_types[subcmd], GET_NAME(d->character));
        return;
      }
    }
  }

  d = ch->desc;

  /* Give descriptor an OLC structure. */
  if (d->olc) {
    mudlog(BRF, LVL_IMMORT, TRUE, "SYSERR: do_oasis_ibtedit: Player already had olc structure.");
    free(d->olc);
  }

  CREATE(d->olc, struct oasis_olc_data, 1);

  /* Set OLC variables. */
  OLC_NUM(d)  = number;
  OLC_VAL(d)  = subcmd;
  OLC_ZNUM(d) = 0;

  /* setup the OLC structure. */
  ibtedit_setup(d);

  /* Show the main IBT edit menu                              */
  ibtedit_disp_main_menu(d);
  STATE(d) = CON_IBTEDIT;

  /* Display the OLC messages to the players in the same room as the
     editor and also log it. */
  act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM);
  SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING);

  mudlog(CMP, LVL_IMMORT, TRUE,"OLC: %s starts editing %s %d",
    GET_NAME(ch), IBT_TYPE, OLC_NUM(d));
}

/*-------------------------------------------------------------------*/
/* Copy IBT data into the OLC structure                              */
static void ibtedit_setup(struct descriptor_data *d)
{
  IBT_DATA *ibtData;
  int i;

  /* Set up a blank IBT struct */
  OLC_IBT(d) = new_ibt();

  /* Should always be the case, but just to make sure */
  if ((ibtData = get_ibt_by_num(OLC_VAL(d), OLC_NUM(d))) != NULL) {
    if ((ibtData->text) && *(ibtData->text)) {
      OLC_IBT(d)->text  = STRALLOC(ibtData->text);
    }
    if ((ibtData->body) && *(ibtData->body)) {
      OLC_IBT(d)->body  = STRALLOC(ibtData->body);
    }
    if ((ibtData->name) && *(ibtData->name)) {
      OLC_IBT(d)->name  = STRALLOC(ibtData->name);
    }
    if ((ibtData->notes) && *(ibtData->notes)) {
      OLC_IBT(d)->notes = STRALLOC(ibtData->notes);
    }
    OLC_IBT(d)->level = ibtData->level;
    OLC_IBT(d)->room  = ibtData->room;
    for (i=0; i<IBT_ARRAY_MAX; i++) {
      OLC_IBT(d)->flags[i] = ibtData->flags[i];
    }
  }
}

/*-------------------------------------------------------------------*/
/* Copy IBT data back to the correct list                            */
static void ibtedit_save(struct descriptor_data *d)
{
  IBT_DATA *ibtData;
  int i;

  /* Should always be the case, but just to make sure */
  if ((ibtData = get_ibt_by_num(OLC_VAL(d), OLC_NUM(d))) != NULL) {
    if ((OLC_IBT(d)->text) && *(OLC_IBT(d)->text)) {
      if (ibtData->text) STRFREE(ibtData->text);
      ibtData->text = STRALLOC(OLC_IBT(d)->text);
      STRFREE(OLC_IBT(d)->text);
    }
    if ((OLC_IBT(d)->body) && *(OLC_IBT(d)->body)) {
      if (ibtData->body) STRFREE(ibtData->body);
      ibtData->body = STRALLOC(OLC_IBT(d)->body);
      STRFREE(OLC_IBT(d)->body);
    }
    if ((OLC_IBT(d)->name) && *(OLC_IBT(d)->name)) {
      if (ibtData->name) STRFREE(ibtData->name);
      ibtData->name = STRALLOC(OLC_IBT(d)->name);
      STRFREE(OLC_IBT(d)->name);
    }
    if ((OLC_IBT(d)->notes) && *(OLC_IBT(d)->notes)) {
      if (ibtData->notes) STRFREE(ibtData->notes);
      ibtData->notes = STRALLOC(OLC_IBT(d)->notes);
      STRFREE(OLC_IBT(d)->notes);
    }
    ibtData->level = OLC_IBT(d)->level;
    ibtData->room  = OLC_IBT(d)->room;
    for (i=0; i<IBT_ARRAY_MAX; i++) {
      ibtData->flags[i] = OLC_IBT(d)->flags[i];
    }
  } else {
    log("SYSERR: ibtedit_save: Invalid IBT vnum (%d) in OLC struct", OLC_NUM(d));
    log("        IBT possibly removed while being edited");
    return;
  }

  save_ibt_file(OLC_VAL(d));
}

void free_olc_ibt(IBT_DATA *toFree) {
	
	if (!toFree)
	  return;
 
	if (toFree->text) {
	  STRFREE(toFree->text);
	}

	if (toFree->body) {
	  STRFREE(toFree->body);
	}

	if (toFree->name) {
	  STRFREE(toFree->name);
	}

	if (toFree->notes) {
	  STRFREE(toFree->notes);
	}
	
	free(toFree);
}

/*-------------------------------------------------------------------*/
/* main ibtedit menu function...                                     */
static void ibtedit_disp_main_menu(struct descriptor_data *d)
{
  struct char_data *ch = d->character;
  char flg_text[MAX_STRING_LENGTH];
  room_rnum rr;

  get_char_colors(ch);
  clear_screen(d);

  rr = real_room(OLC_IBT(d)->room);
  sprintbitarray(OLC_IBT(d)->flags, ibt_bits, IBT_ARRAY_MAX, flg_text);

  send_to_char(ch, "%s-- Edit %s Number %s[%s%d%s]\r\n"
                   "%s1%s) Reported By: %s%-12s\r\n"
                   "%s2%s) Reported In: %s[%s%-5d%s]%s - %s%s\r\n"
                   "%s3%s) Header Text: %s%s\r\n"
                   "%s4%s) Flags      : %s%s\r\n"
                   "%s5%s) Details:\r\n%s%s\r\n"
                   "%s6%s) Admin Notes:\r\n%s%s\r\n"
                   "%sQ%s) Quit %s Editor\r\n",
                   QBGRN, IBT_TYPE, cyn, yel, OLC_NUM(d), cyn,
                   yel, nrm, yel, OLC_IBT(d)->name,
                   yel, nrm, cyn, yel, OLC_IBT(d)->room, cyn, nrm, (rr == NOWHERE) ? CCRED(d->character, C_NRM) : cyn, (rr == NOWHERE) ? "<Invalid Room!>" : world[rr].name,
                   yel, nrm, yel, OLC_IBT(d)->text,
                   yel, nrm, cyn, flg_text,
                   yel, nrm, yel, OLC_IBT(d)->body ? OLC_IBT(d)->body : "<Not Set!>",
                   yel, nrm, yel, OLC_IBT(d)->notes ? OLC_IBT(d)->notes : "<Not Set!>",
                   yel, nrm, IBT_TYPE);

  OLC_MODE(d) = IBTEDIT_MAIN_MENU;

}
/*-------------------------------------------------------------------*/
/* Display IBT-flags menu. */
static void ibtedit_disp_flags(struct descriptor_data *d)
{
  char buf[MAX_STRING_LENGTH];

  get_char_colors(d->character);
  clear_screen(d);

  column_list(d->character, 2, ibt_bits, NUM_IBT_FLAGS, TRUE);

  sprintbitarray(OLC_IBT(d)->flags, ibt_bits, IBT_ARRAY_MAX, buf);
  write_to_output(d, "\r\nCurrent flags : %s%s%s\r\nEnter flags (0 to quit) : ",
		  cyn, buf, nrm);

  OLC_MODE(d) = IBTEDIT_FLAGS;
}
/*-------------------------------------------------------------------*/
/* main clanedit parser function... interpreter throws all input to here. */
void ibtedit_parse(struct descriptor_data *d, char *arg)
{
  int i;
  char *oldtext = NULL;

  switch (OLC_MODE(d)) {
    case IBTEDIT_CONFIRM_SAVESTRING:
      switch (*arg) {
      case 'y':
      case 'Y':
        /* Save the IBT in memory and to disk. */
        ibtedit_save(d);
        mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(d->character)), TRUE, "OLC: %s edits %s %d", GET_NAME(d->character), IBT_TYPE, OLC_NUM(d));
        cleanup_olc(d, CLEANUP_ALL);
        return;
      case 'n':
      case 'N':
        /* If not saving, we must free the OLC memory. */
        cleanup_olc(d, CLEANUP_ALL);
        return;
      default:
        write_to_output(d, "Invalid choice!\r\n");
        write_to_output(d, "Do you wish to save your changes? : ");
        return;
      }
      break;

    case IBTEDIT_MAIN_MENU:
      switch (*arg) {
        case 'q':
        case 'Q':
          if (OLC_ZNUM(d)) {	/* Anything been changed? */
            write_to_output(d, "Do you wish to save your changes? : ");
            OLC_MODE(d) = IBTEDIT_CONFIRM_SAVESTRING;
          } else
            cleanup_olc(d, CLEANUP_ALL);
          return;

        case '1':
          OLC_MODE(d) = IBTEDIT_NAME;
          write_to_output(d, "Who reported this %s? : ", IBT_TYPE);
          return;

        case '2':
          OLC_MODE(d) = IBTEDIT_ROOM;
          write_to_output(d, "Enter the room VNUM for this %s? : ", IBT_TYPE);
          return;

        case '3':
          OLC_MODE(d) = IBTEDIT_TEXT;
          write_to_output(d, "Enter the header text for this %s? : ", IBT_TYPE);
          return;

        case '4':
          ibtedit_disp_flags(d);
          return;

        case '5':
          OLC_MODE(d) = IBTEDIT_BODY;
          send_editor_help(d);
          write_to_output(d, "Enter %s description:\r\n\r\n", IBT_TYPE);
          if (OLC_IBT(d)->body) {
            write_to_output(d, "%s", OLC_IBT(d)->body);
            if (oldtext) STRFREE(oldtext);
            oldtext = STRALLOC(OLC_IBT(d)->body);
          }
          string_write(d, &OLC_IBT(d)->body, MAX_IBT_LENGTH, 0, oldtext);
          OLC_ZNUM(d) = 1;
          return;

        case '6':
          OLC_MODE(d) = IBTEDIT_NOTES;
          send_editor_help(d);
          write_to_output(d, "Enter %s notes:\r\n\r\n", IBT_TYPE);
          if (OLC_IBT(d)->notes) {
            write_to_output(d, "%s", OLC_IBT(d)->notes);
            if (oldtext) STRFREE(oldtext);
            oldtext = STRALLOC(OLC_IBT(d)->notes);
          }
          string_write(d, &OLC_IBT(d)->notes, MAX_IBT_LENGTH, 0, oldtext);
          OLC_ZNUM(d) = 1;
          return;

        default:
          ibtedit_disp_main_menu(d);
          return;
      }
      break;

    case IBTEDIT_NAME:
      smash_tilde(arg);
      if (OLC_IBT(d)->name) STRFREE(OLC_IBT(d)->name);
      OLC_IBT(d)->name = STRALLOC(arg);
      break;

    case IBTEDIT_ROOM:
      i = atoi(arg);
      if (i == 0) { /* Cancel option */
        ibtedit_disp_main_menu(d);
        return;
      }
      if (real_room(i) == NOWHERE) {
        write_to_output(d, "That room does not exist!\r\n"
                           "Enter room VNUM (0 to cancel) : ");
        return;
      }
      OLC_IBT(d)->room = i;
      /* Drop through to re-show main menu and set 'edited' flag */
      break;

    case IBTEDIT_TEXT:
      smash_tilde(arg);
      if (OLC_IBT(d)->text) STRFREE(OLC_IBT(d)->text);
      OLC_IBT(d)->text = STRALLOC(arg);
      break;

    case IBTEDIT_FLAGS:
      if ((i = atoi(arg)) <= 0)
        break;
      else if (i <= NUM_IBT_FLAGS)
        TOGGLE_BIT_AR(IBT_FLAGS(OLC_IBT(d)), (i - 1));
      ibtedit_disp_flags(d);
      return;

    case IBTEDIT_BODY:
      /* We should never get here, modify.c throws user through ibtedit_string_cleanup. */
      cleanup_olc(d, CLEANUP_ALL);
      mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: OLC: ibtedit_parse(): Reached BODY case!");
      write_to_output(d, "Oops...\r\n");
      break;

    case IBTEDIT_NOTES:
      /* We should never get here, modify.c throws user through ibtedit_string_cleanup. */
      cleanup_olc(d, CLEANUP_ALL);
      mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: OLC: ibtedit_parse(): Reached NOTES case!");
      write_to_output(d, "Oops...\r\n");
      break;

    default:
      /* We should never get here. */
      cleanup_olc(d, CLEANUP_ALL);
      mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: OLC: ibtedit_parse(): Reached default case!");
      write_to_output(d, "Oops...\r\n");
      break;
  }

  /* If we get here, something was changed */
  OLC_ZNUM(d) = 1;
  ibtedit_disp_main_menu(d);
}

/*-------------------------------------------------------------------*/
void ibtedit_string_cleanup(struct descriptor_data *d, int terminator)
{
  switch (OLC_MODE(d)) {

  case IBTEDIT_BODY:
  case IBTEDIT_NOTES:
  default:
     ibtedit_disp_main_menu(d);
     break;
  }
}

/*-------------------------------------------------------------------*/
void free_ibt_lists()
{
  IBT_DATA *first_ibt, *last_ibt;
  int mode;

  for( mode=0; mode <=2; mode++){
    first_ibt = get_first_ibt(mode);
    last_ibt = get_last_ibt(mode);
    free_ibt_list(first_ibt, last_ibt);
  }
}

