/
ScryMUD/mud/
ScryMUD/mud/grrmud/Boards/
ScryMUD/mud/grrmud/Help/
ScryMUD/mud/grrmud/Pfiles/
ScryMUD/mud/grrmud/PlayerSacks/
ScryMUD/mud/grrmud/PlayerShops/
ScryMUD/mud/grrmud/help_filter/
ScryMUD/mud/hegemon/
ScryMUD/mud/hegemon/data/
ScryMUD/mud/hegemon/data/help/battle/
ScryMUD/mud/hegemon/data/help/client/
ScryMUD/mud/hegemon/data/help/communications/
ScryMUD/mud/hegemon/data/help/skills/
ScryMUD/mud/hegemon/data/help/spells/
ScryMUD/mud/include/
ScryMUD/mud/lib/
ScryMUD/mud/lib/bitfield/
ScryMUD/mud/lib/log/
ScryMUD/mud/lib/string2/
// $Id: BugEntry.cc,v 1.9.2.1 1999/07/28 04:48:01 gibbsn Exp $
// $Revision: 1.9.2.1 $  $Author: gibbsn $ $Date: 1999/07/28 04:48:01 $

//
//ScryMUD Server Code
//Copyright (C) 1998  Ben Greear
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// To contact the Author, Ben Greear:  greear@cyberhighway.net, (preferred)
//                                     greearb@agcs.com
// Meaningless test comment added by angelbob: 7/26/99
//

#include "BugEntry.h"
#include "critter.h"
#include "misc2.h"

int BugEntry::_cnt = 0;
int BugCollection::_cnt = 0;


char* BugEntry::state_str[] = {
   "open",
   "assigned",
   "retest",
   "closed",
   "defer",
   "all",
   "none",
   "high_state",
  };

char* BugEntry::html_color[] = {
   "<font color=\"#000000\"> ", //open (black)
   "<font color=\"#0000cc\"> ", //assigned (blue)
   "<font color=\"#cc0000\"> ", //retest (red)
   "<font color=\"#00cc00\"> ", //closed (green)
   "<font color=\"#000000\"> ", //all (black)  //these last 3 not used now.
   "<font color=\"#000000\"> ", //none (black)
   "<font color=\"#000000\"> " //high_state (black)
  };

char* BugCollection::ct_strs[] = {
   "BUGS",
   "IDEAS"
};

///*************************************************************///
///********************* CommentEntry  *****************************///
///*************************************************************///

CommentEntry::CommentEntry(const char* d, const char* rr, const char* r)
      : date(d), reporter(rr), report(r) {
   html_safe_report = report.sterilizeForHtml();
   heg_safe_report = report;
   parse_communication(heg_safe_report);
}//constructor


int CommentEntry::read(ifstream& dafile) {
   char buf[100];
   int sent;
   dafile >> sent;
   if (sent == -1)
     return -1;

   date.Termed_Read(dafile);

   dafile >> reporter;
   dafile.getline(buf, 99);
   
   report.Termed_Read(dafile);

   html_safe_report = report.sterilizeForHtml();
   heg_safe_report = report;
   parse_communication(heg_safe_report);

   if (dafile)
     return 0;
   return -1;
}

int CommentEntry::write(ofstream& dafile) {
   dafile << 1 << endl; //sentinal
   dafile << date << "\n~\n";
   dafile << reporter << endl;
   dafile << report << "\n~\n";
   if (dafile)
     return 0;
   return -1;
}//

int CommentEntry::writeSentinal(ofstream& dafile) {
   dafile << -1 << endl;
   if (dafile)
     return 0;
   return -1;
}//writeSentinal

int CommentEntry::writeHtml(ofstream& dafile, const char* fnt) {
   dafile << "<tr><td nowrap>" << fnt << date << "</font></td> <td>" 
          << fnt << reporter << "</font></td> <td>" << fnt
          << html_safe_report << "</font></td></tr>" << endl;
   return 0;
}

String CommentEntry::toStringHeg() {
   String buf(200);
   Sprintf(buf, "<BUG_COMMENT_ENTRY \"%S\" %S>%S</BUG_COMMENT_ENTRY>",
           &date, &reporter, &heg_safe_report);
   return buf;
}


String CommentEntry::toString() {
   String buf(200);
   Sprintf(buf, "   %S: %S\n\t%S\n", &date, &reporter, &report);
   return buf;
}//toString



///*************************************************************///
///********************* BugEntry  *****************************///
///*************************************************************///

BugEntry::state BugEntry::getState(const String& fer_state) {
   if (fer_state.Strlen() == 0) {
      return all;
   }

   for (int i = 0; i<high_state; i++) {
      if (strcmp(fer_state, state_str[i]) == 0) {
         return (state)(i);
      }
   }//for

   return none;
}

BugEntry::BugEntry(const BugEntry& src) //copy constructor
   : bug_num(src.bug_num), room_num(src.room_num), cur_state(src.cur_state), 
     create_date(src.create_date), reporter(src.reporter),
     assigned_to(src.assigned_to), title(src.title),
     html_safe_title(src.html_safe_title),
     heg_safe_title(src.heg_safe_title) {
        
   clear_ptr_list(reports);

   Cell<CommentEntry*> cll(src.reports);
   CommentEntry* ptr;
   while ((ptr = cll.next())) {
      reports.append(new CommentEntry(*ptr));
   }
   _cnt++;
}//copy constructor


BugEntry::BugEntry(int bn, const char* _reporter, const char* _date, 
                   const char* _title, int rn)
      : bug_num(bn), room_num(rn), cur_state(open), 
        create_date(_date), reporter(_reporter),
        assigned_to("ADMIN"), title(_title) {

   html_safe_title = title.sterilizeForHtml();
   heg_safe_title = title;
   parse_communication(heg_safe_title);
   _cnt++;
}//Constructor

    
int BugEntry::clear() {
   bug_num = room_num = 0;
   cur_state = open;
   flags.Clear();
   create_date.Clear();
   reporter.Clear();
   assigned_to.Clear();
   clear_ptr_list(reports);
   title.Clear();
   html_safe_title.Clear();
   heg_safe_title.Clear();
   return 0;
}//clear

BugEntry& BugEntry::operator=(const BugEntry& src) {
   bug_num = src.bug_num;
   room_num = src.bug_num;
   cur_state = src.cur_state;
   flags = src.flags;
   create_date = src.create_date;
   reporter = src.reporter;
   assigned_to = src.assigned_to;
   title = src.title;
   html_safe_title = src.html_safe_title;
   heg_safe_title = src.heg_safe_title;
   clear_ptr_list(reports);
   
   Cell<CommentEntry*> cll(src.reports);
   CommentEntry* ptr;
   while ((ptr = cll.next())) {
      reports.append(new CommentEntry(*ptr));
   }
   return *this;
}//operatoe=


void BugEntry::appendComment(const CommentEntry& re) {
   reports.append(new CommentEntry(re));
}

void BugEntry::appendComment(const char* date, const char* report,
                             const char* reporter) {
   reports.append(new CommentEntry(date, reporter, report));
}

//does checking needed
int BugEntry::changeState(state new_state, int imm_level, const String& name) {
   
   if ((imm_level < 9) && (strcasecmp(name, assigned_to) != 0)) {
      return -EPERM;
   }

   // This is the only valid state transition for closed, so it's not in
   // the switch statement below...
   if (new_state == open) {
      cur_state = open;
      return 0;
   }

   // Don't restrict me cause I'm lazy!!
   if (imm_level > 5) {
      cur_state = new_state;
      return 0;
   }

   switch (cur_state)
     {
      case open:
         if ((new_state == assigned) || (new_state == defer)) {
            cur_state = new_state;
            return 0;
         }
         else {
            return -EINVAL;
         }
     case assigned:
        if (new_state == retest) {
           cur_state = retest;
           return 0;
        }
        else {
           return -EINVAL;
        }
     case retest:
         if (new_state == closed) {
           cur_state = closed;
           return 0;
        }
        else {
           return -EINVAL;
        }
     default:
         return -EINVAL;
     }//switch
}//stateTransition


int BugEntry::read(ifstream& dafile) {
   char buf[100];
   clear();

   dafile >> bug_num;

   // Terminator reached.
   if (bug_num == -1) {
      return -1;
   }

   int cs;
   dafile >> room_num >> cs;
   cur_state = (state)(cs);
   dafile.getline(buf, 99);

   flags.Read(dafile);

   dafile.getline(buf, 99);
   create_date = buf;
   create_date.Strip();

   dafile >> reporter;
   dafile.getline(buf, 99);

   dafile >> assigned_to;
   dafile.getline(buf, 99);

   title.Termed_Read(dafile);

   html_safe_title = title.sterilizeForHtml();
   heg_safe_title = title;
   parse_communication(heg_safe_title);

   CommentEntry re;
   while (re.read(dafile) >= 0) {
      reports.append(new CommentEntry(re));
   }
   return bug_num;
}

int BugEntry::write(ofstream& dafile) {
   dafile << bug_num << " " << room_num << " " << (int)cur_state
          << " bug_num, room_num, state\n";

   flags.Write(dafile);

   dafile << create_date << endl;
   dafile << reporter << endl;
   dafile << assigned_to << endl;

   dafile << title << "\n~\n";
   
   Cell<CommentEntry*> cll(reports);
   CommentEntry* ptr;
   while ((ptr = cll.next())) {
      ptr->write(dafile);
   }
   CommentEntry::writeSentinal(dafile);

   if (dafile)
     return 0;
   return -1;
};


// Write a table row entry...
int BugEntry::writeHtml(ofstream& dafile) {
   String buf(200);

   if (!reports.isEmpty()) {
      dafile << "\n<table width=\"95%\" border=3>
<caption align=top><strong>";
   }
   else {
      dafile << "<center><b>\n";
   }

   dafile << html_color[cur_state];

   Sprintf(buf, "[%i] %P08[%s]%P17 <u>%S</u>  %P35By %S %P60Room: %i<br>\n<font size += 1>%S</font>",
           bug_num, state_str[cur_state], &create_date, &reporter,
           room_num, &html_safe_title);
   
   dafile << buf << "</font>\n";
   
   if (!reports.isEmpty()) {
      dafile << "</strong></caption><h4>\n";

      Cell<CommentEntry*> cll(reports);
      CommentEntry* ptr;
      int did_one = FALSE;
      while ((ptr = cll.next())) {
         did_one = TRUE;
         ptr->writeHtml(dafile, html_color[cur_state]);
      }
   
   
      dafile << "</h4></table>\n";
   }//if
   else {
      dafile << "</b></center>\n";
   }

   if (dafile)
     return 0;
   return -1;
};   


/** This is w/out hegemon markup */
String BugEntry::toString() {
   String retval(1000);

   if (reports.isEmpty()) {
      Sprintf(retval,
              "[%i] %P08[%s] %S\n\t  Submitted by %S on %S: in room _%i_.  Assigned to: %S\n",
              bug_num, state_str[cur_state], &title, &reporter, &create_date,
              room_num, &assigned_to);
   }
   else {
      Sprintf(retval,
              "* [%i] %P08[%s] %S\n\t  Submitted by %S on %S: in room _%i_.  Assigned to: %S\n",
              bug_num, state_str[cur_state], &title, &reporter, &create_date,
              room_num, &assigned_to);
   }

   Cell<CommentEntry*> cll(reports);
   CommentEntry* ptr;
   while ((ptr = cll.next())) {
      retval += ptr->toString();
   }

   return retval;
}//toString


/** This is w/out hegemon markup */
String BugEntry::toStringBrief() {
   String retval(1000);

   if (reports.isEmpty()) {
      Sprintf(retval, "[%i] %P08[%s] %S\n\t  Submitted by %S on %S: in room %i.  Assigned to: %S\n",
              bug_num, state_str[cur_state], &heg_safe_title, &reporter,
              &create_date, room_num, &assigned_to);
   }
   else {
      Sprintf(retval, "** [%i] %P08[%s] %S\n\t  Submitted by %S on %S: in room %i.  Assigned to: %S\n",
              bug_num, state_str[cur_state], &heg_safe_title, &reporter,
              &create_date, room_num, &assigned_to);
   }

   return retval;
}//toStringBrief


/** This is with hegemon markup */
String BugEntry::toStringHeg(const char* col_type) {
   String retval(1000);
   String buf(100);

   Sprintf(retval, "<BUG_TITLE>%S</BUG_TITLE>", &heg_safe_title);
   Sprintf(buf, "<BUG_ENTRY %i %s \"%S\" %S %i %S %s>",
           bug_num, state_str[cur_state], &create_date, &reporter, room_num,
           &assigned_to, col_type);

   retval += buf;

   Cell<CommentEntry*> cll(reports);
   CommentEntry* ptr;
   while ((ptr = cll.next())) {
      retval += ptr->toStringHeg();
   }

   retval += "</BUG_ENTRY>";

   return retval;
}//toStringHeg


int BugEntry::canComment(int imm_level, const String& name) {
   // Only way to comment on a closed one btw.
   if (imm_level >= 9) {
      return TRUE;
   }

   if (cur_state == assigned) {
      return (strcasecmp(name, assigned_to) == 0);
   }

   // retest and open can be commented on by anyone.
   return TRUE;
}//canComment


int BugEntry::reAssign(const String& new_name, int imm_level,
                       const String& name) {
   if ((imm_level >= 9) || (strcasecmp(assigned_to, name) == 0)) {
      if (new_name.Strlen() >= 3) {
         assigned_to = new_name;
         return 0;
      }
      else {
         return -EINVAL;
      }
   }
   else {
      return -EPERM;
   }
}//reAssign


///******************************************************************///
///********************* BugCollection  *****************************///
///******************************************************************///

const char* BugCollection::getColTypeName() {
   return ct_strs[type];
}

int BugCollection::write() {
   ofstream dafile(file_name);

   if (dafile) {
      dafile << "_VERSION_  1\n";
      dafile << next_bug_num << endl;

      Cell<BugEntry*> cll(bugs);
      BugEntry* ptr;
      while ((ptr = cll.next())) {
         ptr->write(dafile);
         dafile << endl;
      }//while

      dafile << -1 << " End of Bug Collection" << endl;
      return 0;
   }
   else {
      mudlog << "ERROR:  could not open BugCollection for writing, file -:"
             << file_name << endl;
      return -1;
   }
}//write


int BugCollection::writeHtml() {
   String tmp_name(file_name);
   tmp_name += ".html";

   ofstream dafile(tmp_name);

   if (dafile) {
      dafile << "
<HTML>
<HEAD> 
   <TITLE>  " << coll_name << " </TITLE>
</HEAD>
<BODY BGCOLOR=\"#FFFFFF\">

<CENTER>
<H2>
<a href=http://scry.wanfear.com>ScryMUD "
     << coll_name << "</a></H2></CENTER><P>

This is an automagically generated list of <b>" << coll_name << "</b> entries
from ScryMUD.<P>

<center>
<a href=\"#open\">Open Issues</a> 
<a href=\"#assigned\">Assigned Issues</a> 
<a href=\"#retest\">Issues to Retest</a> 
<a href=\"#closed\">Closed Issues</a>
<a href=\"#defer\">Deferred Issues</a>
</center><P>
";

      Cell<BugEntry*> cll(bugs);
      BugEntry* ptr;

      dafile << "<center>
<a name=\"open\"></a>
<h2><u>Open Issues</u></h2><br>
 <table width=\"100%\"border=3>\n";
      while ((ptr = cll.next())) {
         if (ptr->getState() == BugEntry::open) {
            dafile << "<tr><td>\n";
            ptr->writeHtml(dafile);
            dafile << "</td></tr><P>" << endl;
         }
      }//while

      dafile << "</table><P>
<a name=\"assigned\"></a>
<h2><u>Assigned Issues</u></h2>
 <table width=\"100%\"border=3>\n";
      bugs.head(cll);
      while ((ptr = cll.next())) {
         if (ptr->getState() == BugEntry::assigned) {
            dafile << "<tr><td>\n";
            ptr->writeHtml(dafile);
            dafile << "</td></tr><P>" << endl;
         }
      }//while

      dafile << "</table><P>
<a name=\"retest\"></a>
<h2><u>Issues In Retest</u></h2>
 <table width=\"100%\"border=3>\n";
      bugs.head(cll);
      while ((ptr = cll.next())) {
         if (ptr->getState() == BugEntry::retest) {
            dafile << "<tr><td>\n";
            ptr->writeHtml(dafile);
            dafile << "</td></tr><P>" << endl;
         }
      }//while


      dafile << "</table><P>
<a name=\"defer\"></a>
<h2><u>Issues that have been Deferred</u></h2>
 <table width=\"100%\"border=3>\n";
      bugs.head(cll);
      while ((ptr = cll.next())) {
         if (ptr->getState() == BugEntry::defer) {
            dafile << "<tr><td>\n";
            ptr->writeHtml(dafile);
            dafile << "</td></tr><P>" << endl;
         }
      }//while

      dafile << "</table><P>
<a name=\"closed\"></a>
<h2><u>Closed Issues</u></h2>
 <table width=\"100%\"border=3>\n";
      bugs.head(cll);
      while ((ptr = cll.next())) {
         if (ptr->getState() == BugEntry::closed) {
            dafile << "<tr><td>\n";
            ptr->writeHtml(dafile);
            dafile << "</td></tr><P>" << endl;
         }
      }//while

      dafile << "</table></center>\n\n<hr>
<ADDRESS>
Generated by:<A HREF=\"mailto:greear@cyberhighway.net\">ScryMUD Admin</A>
</ADDRESS></body></html>\n";

      dafile << flush;
      dafile.close();

      String cmd(50);
      Sprintf(cmd, "./mv_logs.bash %S", &tmp_name);
      system(cmd);

      return 0;
   }
   else {
      mudlog << "ERROR:  could not open BugCollection html file -:"
             << tmp_name << endl;
      return -1;
   }
}//writeHtml


int BugCollection::read() {
   char buf[100];
   ifstream dafile(file_name);
   int cnt = 0;
   int version_num;
   String version;

   if (dafile) {
      dafile >> version >> version_num;

      if (strcmp(version, "_VERSION_") != 0) {
         mudlog << "ERROR:  BugCollection file -:" << file_name
                << ":- is an incompatible version.\n  It will be ignored "
                << "and destroyed the first time a change is made to the "
                << "bug list.  I'll be able to handle Version Conversion\n"
                << "better next time...\n";
         cerr  << "ERROR:  BugCollection file -:" << file_name
               << ":- is an incompatible version.\n  It will be ignored "
               << "and destroyed the first time a change is made to the "
               << "bug list.  I'll be able to handle Version Conversion\n"
               << "better next time...\n";
      }

      dafile >> next_bug_num;
      dafile.getline(buf, 99);

      BugEntry be;

      while (dafile && (be.read(dafile) != -1)) {
         cnt++;
         bugs.append(new BugEntry(be));
      }
      return cnt;
   }//if
   else {
      return -1;
   }
}//read
   

BugEntry* BugCollection::getBugEntry(int bug_num) {
   Cell<BugEntry*> cll(bugs);
   BugEntry* ptr;
   while ((ptr = cll.next())) {
      if (bug_num == ptr->getBugNum()) {
         return ptr;
      }
   }//while
   return NULL;
}

int BugCollection::changeState(int bug, const String& new_state, int imm_level,
                               const String& name) {
   BugEntry* be = getBugEntry(bug);
   BugEntry::state s = BugEntry::getState(new_state);
   if (be) {
      int retval = be->changeState(s, imm_level, name);
      if (retval >= 0)
        notifyModified();
      return retval;
   }
   return -EINVAL;
}

int BugCollection::reAssign(int bug, const String& new_name, int imm_level,
                            const String& name) {
   BugEntry* be = getBugEntry(bug);
   if (be) {
      int retval = be->reAssign(new_name, imm_level, name);
      if (retval >= 0)
        notifyModified();
      return retval;
   }
   return -EINVAL;
}

int BugCollection::canComment(int bug, int imm_level, const String& name) {
   BugEntry* be = getBugEntry(bug);
   if (be) {
      return be->canComment(imm_level, name);
   }
   return FALSE;
}

int BugCollection::notifyModified() {
   this->write();
   this->writeHtml();
   return 0;
}

int BugCollection::addComment(int bug, const CommentEntry& re, int imm_level,
                              const String& name) {
   BugEntry* be = getBugEntry(bug);
   if (be) {
      if (be->canComment(imm_level, name)) {
         be->appendComment(re);
         notifyModified();
         return 0;
      }
      else {
         return -EINVAL;
      }
   }//if
   else {
      return -EINVAL;
   }
}//addComment


int BugCollection::purgeBug(int num) {
   BugEntry* be = getBugEntry(num);
   if (be) {
      bugs.loseData(be);
      delete be;
      notifyModified();
      return 0;
   }//if we found it
   return -EINVAL;
}

int BugCollection::addBug(BugEntry* be) {
   if (be) {
      bugs.append(be);
      notifyModified();
      return 0;
   }
   return -EINVAL;
}

int BugCollection::addBug(const char* cur_date, int rnum,
                          const String& reporter, const String& report) {
   bugs.append(new BugEntry(next_bug_num, reporter, cur_date, report, rnum));
   next_bug_num++;

   notifyModified();
   return 0;
}


String BugCollection::toString(const String& in_state, int use_heg_markup) {
   String buf(10000);
   
   BugEntry::state s = BugEntry::getState(in_state);

   Cell<BugEntry*> cll(bugs);
   BugEntry* ptr;
   while ((ptr = cll.next())) {
      if ((s == BugEntry::all) || (s == ptr->getState())) {
         if (use_heg_markup) {
            buf.Append(ptr->toStringHeg(ct_strs[type]));
         }
         else {
            buf.Append(ptr->toString());
         }
         buf.Append("\n");
      }
   }
   return buf;
}


String BugCollection::toStringBrief(const String& in_state) {
   String buf(10000);
   
   BugEntry::state s = BugEntry::getState(in_state);

   Cell<BugEntry*> cll(bugs);
   BugEntry* ptr;
   while ((ptr = cll.next())) {
      if ((s == BugEntry::all) || (s == ptr->getState())) {
         buf.Append(ptr->toStringBrief());
      }
   }
   return buf;
}