//
// example of using recursive linked-list class:
//
// define and illustrate use of "shopping list" class.
//
// version 2
//
// changes from version 1:
//   use "typedef".
//   print items in same order added.
//   make sure items are unique.
//   add replaceItem() function.
//   change main program to interactive test.
//
#include <iostream.h>
#include <stdlib.h>             // has EXIT_SUCCESS
#include <string>
#include "seq.h"                // has Seq template class

//
// "shopping list" class
//
class ShopList {

private:
  typedef Seq<string> slist;	// shorthand for Seq<string>

public:

  // public interface for class:

  // constructor.
  // post:  constructed ShopList has no items.
  ShopList() {
    items = slist();
  }

  // function to print items.
  // post:  all items in list printed to stdout, in
  //          order in which they were added.
  void printItems() const {
    print(items);
  }

  // function to search for item.
  // post:  returns true if itm is in list, false otherwise.
  bool hasItem(const string & itm) const {
    return has(itm, items);
  }

  // function to add item.
  // pre:  items in list are unique.
  // post:  "itm" added to list at "end" if not already present.
  //          note that items in list are still unique.
  void addItem(const string & itm) {
    if (!hasItem(itm))
      items = slist(itm, items);
  }

  // function to remove item.
  // pre:  items in list are unique.
  // post:  "itm" removed from list if present.  note that items
  //          in list are still unique.
  void removeItem(const string & itm) {
    items = remove(itm, items);
  }

  // function to replace item.
  // pre:  items in list are unique.
  // post:  if "newItm" already in list, "oldItm" removed;
  //          otherwise "oldItm" replaced by "newItm" if in list.
  //          note that items in list are still unique.
  void replaceItem(const string & oldItm, const string & newItm) {
    if (hasItem(newItm))
      removeItem(oldItm);
    else
      items = replace(oldItm, newItm, items);
  }

private:

  // list of items, in reverse order of addition.
  slist items;

  // helper functions:

  // function to print list of strings.
  // post:  l printed to stdout, in reverse order.
  static void print(const slist & l) {
    if (l.empty())
      return;
    else {
      print(l.tl());
      cout << l.hd() << endl;
    }
  }

  // function to find item in list of strings.
  // post:  returns true if itm is in l, false otherwise.
  static bool has(const string & itm, const slist & l) {
    if (l.empty())
      return false;
    else
      return (l.hd() == itm) || has(itm, l.tl());
  }

  // function to remove item from list of strings.
  // pre:  items in l are unique.
  // post:  returns l' = l with itm removed.  note that items in
  //          l' are unique.
  static slist remove(const string & itm, const slist & l) {
    if (l.empty())
      return l;
    else if (l.hd() == itm)
      return l.tl();
    else
      return slist(l.hd(), remove(itm, l.tl()));
  }

  // function to replace item in list of strings.
  // pre:  items in l are unique, newItm is not already in l.
  // post:  returns l' = l with oldItm replaced by newItm if present.
  static slist replace(const string & oldItm, const string & newItm,
		       const slist & l) {
    if (l.empty())
      return l;
    else if (l.hd() == oldItm)
      return slist(newItm, l.tl());
    else
      return slist(l.hd(), replace(oldItm, newItm, l.tl()));
  }
};

//
// main program:
//
// allows interactive testing of ShopList class functions.
//
int main(void) {

  ShopList myList;

  char command;
  string t1;
  string t2;
  char prompt[] = 
    "What do you want to do?\n"
    "  a to add item\n"
    "  d to remove item\n"
    "  r to replace item\n"
    "  q to quit\n"
    "Your choice?  ";
  
  cout << "Current list:\n";
  myList.printItems();
  cout << prompt;
  while ((cin >> command) && (command != 'q')) {
    cin.ignore(10000, '\n');	// discard EOF so we can use getline
    if (command == 'a') {
      cout << "Item to add?  ";
      getline(cin, t1);
      myList.addItem(t1);
    }
    else if (command == 'd') {
      cout << "Item to remove?  ";
      getline(cin, t1);
      myList.removeItem(t1);
    }
    else if (command == 'r') {
      cout << "Item to replace?  ";
      getline(cin, t1);
      cout << "Item to replace it with?  ";
      getline(cin, t2);
      myList.replaceItem(t1, t2);
    }
    else
      cout << "Unrecognized command.\n";
      cout << "Current list:\n";
      myList.printItems();
      cout << prompt;
  }

  return EXIT_SUCCESS;
}