#include "buglist.h"

// allocate, initialize, and return bug
buglist::bug* buglist::create(string name, string desc, string file)
{
   bug* b = new bug;
   b->name = name;
   b->desc = desc;
   b->file = file;
   b->next = nullptr;
   b->prev = nullptr;
   return b;
}

// find and return pointer to named bug, null if not found
buglist::bug* buglist::getByName(string name)
{
   bug* b = front;
   while (b) {
      if (b->name == name) {
         return b;
      }
      b = b->next;
   }
   return b;
}

// display the string fields for the passed bug in the format
// name(file): desc (all one line, no newline/endl at the end)
void buglist::prtBug(string name, string desc, string file)
{
   cout << name << "(" << file << "): " << desc;
}

// if not null then call prtBug on fields (does nothing otherwise)
void buglist::prtBug(bug* bptr)
{
   if (bptr) {
      prtBug(bptr->name, bptr->desc, bptr->file);
   }
}

// generates an empty buglist
buglist::buglist()
{
   front = nullptr;
   back = nullptr;
   numBugs = 0;
}

// initializes this list as a duplicate of the provided buglist
buglist::buglist(const buglist &b)
{
   // start with current list empty
   front = nullptr;
   back = nullptr;
   numBugs = 0;

   // duplicate/insert bugs one by one from front to back, using insertBack
   // (more efficient than sorted insert since we know these belong at end)
   bug* bcurr = b.front;
   while (bcurr) {
      insertBack(bcurr->name, bcurr->desc, bcurr->file);
      bcurr = bcurr->next;
   }
}

// delete each bug in the list
buglist::~buglist()
{
   bug* b = front;
   while (b) {
      bug* old = b;
      b = b->next;
      delete old;
   }
}

// creates and inserts new bug at back of list
void buglist::insertBack(string name, string desc, string file)
{
   bug* b = create(name, desc, file);
   b->prev = back;
   if (back) {
      back->next = b;
   } else {
      front = b;
   }
   back = b;
   numBugs++;
}

// display all bugs in the list, front to back, use prtBug
void buglist::prtAll()
{
   bug* b = front;
   while (b) {
      cout << "   ";
      prtBug(b);
      cout << endl;
      b = b->next;
   }
}

// display all bugs (front to back) related to the named file, use prtBug
void buglist::prtFile(string file)
{
   bug* b = front;
   while (b) {
      if (b->file == file) {
         cout << "   ";
         prtBug(b);
         cout << endl;
      }
      b = b->next;
   }
}

// find bug, fill in the parameters, return true iff successful
bool buglist::lookup(string name, string &desc, string &file)
{
   bug* b = getByName(name);
   if (b) {
      desc = b->desc;
      file = b->file;
      return true;
   }
   return false;
}

// find bug, remove from list, and deallocate, return true iff successful
bool buglist::remove(string name)
{
   bug* b = getByName(name);
   if (!b) {
      return false;
   }
   // special case 1: b is only item in list
   if ((b == front) && (b == back)) {
      front = nullptr;
      back = nullptr;
   }

   // special case 2: b is first item in list (but has items after it)
   else if (b == front) {
      front = b->next;
      front->prev = nullptr;
   }

   // special case 3: b is last item in list (bug has items before it)
   else if (b == back) {
       back = b->prev;
       back->next = nullptr;
   }

   // general case: b is somewhere inside list (has items before and after it)
   else {
      // bypass b
      bug* before = b->prev;
      bug* after = b->next;
      before->next = after;
      after->prev = before;
   }

   // final actions for all cases where b found
   b->next = nullptr;
   b->prev = nullptr;
   numBugs--;
   delete b;
   return true;
}

// create/insert bug (sorted position), return true iff successful
bool buglist::insert(string name, string desc, string file)
{
   // fails if there is already a bug with that name
   bug* b = getByName(name);
   if (b) {
      cerr << "Error: cannot insert bugs with duplicate names" << endl;
      cerr << "   (conflict with ";
      prtBug(b);
      cerr << ")" << endl;
      return false;
   }

   // special case 1: bug belongs at back of list, can just use insertBack
   if (!front || (name >= back->name)) {
      insertBack(name, desc, file);
      return true;
   }

   b = create(name, desc, file);

   // special case 2: bug belongs at front of list, simpler insert
   if (name < front->name) {
      b->next = front;
      front->prev = b;
      front = b;
      numBugs++;
      return true;
   }

   // general case: bug belongs somewhere inside, will have bugs before and after it
   //    so move through the list until we find the spot to insert the new bug
   bug* after = front;
   while (name >= after->name) {
      after = after->next;
   }
   bug* before = after->prev;

   // update the new bug's pointers
   b->next = after;
   b->prev = before;

   // update the pointers to the new bug
   after->prev = b;
   before->next = b;

   // finish up
   numBugs++;
   return true;
}

