// Bug/typo/idea tracking system
// Based, in part, on the interface idea at Tsunami.
// Used in conjunction with the "bug", "idea", "typo", and "bugs" commands
// Deathknight -- May 13, 1996
#include <mudlib.h>
inherit DAEMON ;
/* These are only used once, so we #define them */
#define RESTORE_BTI  "/data/bugs"
#define BTI_DATA_FILE  "data/bugs"
/*
 * Structure for this mapping:
 *   int number : ({ int type, string location_filename,
 *                   string assigned_to, mixed * comment_array })
 *
 * Where type is:  1-bug, 2-typo, 3-idea
 *
 * Where comment_array is:
 *   ({ string name, int date, string comment, ... })
 *
 * (bti stands for Bugs/Typos/Ideas)
 */
mapping bti;
int next_number;
void create() {
    ::create() ;
    next_number = 1;
    bti = ([]);
    restore_object (RESTORE_BTI);
}
void save_me () {
    save_object (BTI_DATA_FILE);
}
int add_new (string comment) {
    int bti_type;
    object env, tp;
    string env_str;
    tp = this_player();
    if (!tp || !interactive (tp))  return 0;
    env = environment (tp);
    if (!env)  env_str = "(Nowhere)";
    else env_str = file_name (env);
    if (query_verb() == "bug")  bti_type = 1;
    else if (query_verb() == "typo")  bti_type = 2;
    else if (query_verb() == "idea")  bti_type = 3;
    else return 0;
    if (bti[next_number]) {
        write ("Internal bug daemon error!\n");
        return 0;
    }
    bti[next_number++] = ({ bti_type, env_str, 0,
      ({ tp->query("name"), time(), comment }) });
    save_me();
    return next_number-1;
}
string bug_type (int type) {
    switch (type) {
      case 1:  return "bug";
      case 2:  return "typo";
      case 3:  return "idea";
      default:  return "internal error!";
    }
}
void list_bug (int bug_num) {
    mixed *bug, *comments;
    int x;
    bug = bti[bug_num];
    if (!bug) {
        if (bug_num >= next_number)
            write ("That number has not yet been reached.\n");
        else write ("That bug has already been concluded.\n");
        return;
    }
    
    comments = bug[3];
    printf ("Number:  %d\n"
            "Date:        %s\n"
            "Type:        %s\n"
            "Location:    %s\n"
            "Originator:  %s\n"
            "Assigned to: %s\n"
      "------------------------------------------------------------",
      bug_num, ctime (comments[1]), bug_type (bug[0]), bug[1],
      comments[0][0], (bug[2] ? bug[2]:"(unassigned)") );
    
    for (x=0; x<sizeof (comments); x+=3) {
        printf ("%s: %s  %s\n", ctime (comments[x+1]),
          comments[x], comments[x+2]);
    }
}
int add_comment (int bug_num, string comment) {
    object tp;
    tp = this_player();
    if (!tp || !interactive (tp))  return 0;
    if (!bti[bug_num]) {
        write ("Invalid bug number.\n");
        return 0;
    }
    bti[bug_num][3] += ({ tp->query("name"), time(), comment });
    save_me();
    return bug_num;
}
int assign_bug (int bug_num, string name) {
    object tp;
    tp = this_player();
    if (!tp || (!adminp (tp) && name != 0))  return 0;
    if (!bti[bug_num]) {
        write ("Invalid bug number.\n");
        return 0;
    }
    if (!name && bti[bug_num][2] != tp->query("name") && !adminp (tp)) {
        write ("You can only resolve your own bugs.\n");
        return 0;
    }
/* Should we check if name's a valid user? */
    
    bti[bug_num][2] = name;
    bti[bug_num][3] += ({ tp->query("name"), time(),
      (name ? "assigned to "+name:"resolved") });
    save_me();
    return bug_num;
}