library SaleListLib {
  address public constant nullAddress = address(0);

  struct SaleList {
    address head;

    mapping(address => address) sellerListMapping;
    mapping(address => uint) sellerToPrice;
  }

  function getBest(SaleList storage self) public view returns (address, uint) {
    address head = self.head;
    return (head, self.sellerToPrice[head]);
  }

  function addSale(SaleList storage self, address seller, uint price) public {
    require(price != 0);
    require(seller != nullAddress);

    if (_contains(self, seller)) {
      removeSale(self, seller);
    }

    self.sellerToPrice[seller] = price;
    if (self.head == nullAddress || price <= self.sellerToPrice[self.head]) {
      self.sellerListMapping[seller] = self.head;
      self.head = seller;
    } else {
      address prev = self.head;
      address cur = self.sellerListMapping[prev];

      while (cur != nullAddress) {
        if (price <= self.sellerToPrice[cur]) {
          self.sellerListMapping[prev] = seller;
          self.sellerListMapping[seller] = cur;

          break;
        }

        prev = cur;
        cur = self.sellerListMapping[cur];
      }

      // Insert value greater than all values in list
      if (cur == nullAddress) {
        self.sellerListMapping[prev] = seller;
      }
    }
  }

  function removeSale(SaleList storage self, address seller) public returns (bool) {
    require(seller != nullAddress);

    if (!_contains(self, seller)) {
      return false;
    }

    if (seller == self.head) {
      self.head = self.sellerListMapping[seller];
      _remove(self, seller);
    } else {
      address prev = self.head;
      address cur = self.sellerListMapping[prev];

      // TODO: Make SURE that initialized mapping with address vals initializes those vals to address(0)
      // NOTE: Redundant check (prev != seller)
      while (cur != nullAddress && prev != seller) {
        if (cur == seller) {
          self.sellerListMapping[prev] = self.sellerListMapping[seller];
          _remove(self, seller);

          break;
        }

        prev = cur;
        cur = self.sellerListMapping[cur];
      }

      // NOTE: Redundant check
      if (cur == nullAddress) {
        return false;
      }
    }

    return true;
  }

  // NOTE: This is a purely internal method that *only* zeros out sellerListMapping and sellerToPrice
  function _remove(SaleList storage self, address seller) internal {
    self.sellerToPrice[seller] = 0;
    self.sellerListMapping[seller] = nullAddress;
  }

  function _contains(SaleList storage self, address seller) view internal returns (bool) {
    return self.sellerToPrice[seller] != 0;
  }
}
/**
 * Sorted list of sales for use in the marketplace. Sorting is maintained by linked list.
 */
function _remove(SaleList storage self, address seller) internal {
  self.sellerToPrice[seller] = 0;
  self.sellerListMapping[seller] = nullAddress;
}

// NOTE: This is a purely internal method that *only* zeros out sellerListMapping and sellerToPrice