contract SaleRegistry is Ownable {
  using SafeMath for uint256;

  /////////
  // Events
  /////////

  event SalePosted(
    address indexed _seller,
    bytes32 indexed _sig,
    uint256 _price
  );

  event SaleCancelled(
    address indexed _seller,
    bytes32 indexed _sig
  );

  ////////
  // State
  ////////

  mapping(bytes32 => SaleListLib.SaleList) _sigToSortedSales;

  mapping(address => mapping(bytes32 => uint256)) _addressToSigToSalePrice;

  // NOTE: Rules are different for contract owner. Can run many sales at a time, all at a single price. This
  // allows multi-sale at genesis time
  mapping(bytes32 => uint256) _ownerSigToNumSales;

  mapping(bytes32 => uint256) public sigToNumSales;

  /////////////
  // User views
  /////////////

  // Returns (seller, price) tuple
  function getBestSale(bytes32 sig) public view returns (address, uint256) {
    return SaleListLib.getBest(_sigToSortedSales[sig]);
  }

  // Returns price that the sender is selling the current sig for (or 0 if not)
  function getMySalePrice(bytes32 sig) public view returns (uint256) {
    return _addressToSigToSalePrice[msg.sender][sig];
  }

  ///////////////
  // User actions
  ///////////////

  // Convenience method used *only* at genesis sale time
  function postGenesisSales(bytes32 sig, uint256 price, uint256 numSales) internal onlyOwner {
    SaleListLib.addSale(_sigToSortedSales[sig], owner, price);
    _addressToSigToSalePrice[owner][sig] = price;

    _ownerSigToNumSales[sig] = _ownerSigToNumSales[sig].add(numSales);
    sigToNumSales[sig] = sigToNumSales[sig].add(numSales);

    emit SalePosted(owner, sig, price);
  }

  // Admin method for re-listing all genesis sales
  function relistGenesisSales(bytes32 sig, uint256 newPrice) external onlyOwner {
    SaleListLib.addSale(_sigToSortedSales[sig], owner, newPrice);
    _addressToSigToSalePrice[owner][sig] = newPrice;

    emit SalePosted(owner, sig, newPrice);
  }

  // NOTE: Only allows 1 active sale per address per sig, unless owner
  function postSale(address seller, bytes32 sig, uint256 price) internal {
    SaleListLib.addSale(_sigToSortedSales[sig], seller, price);
    _addressToSigToSalePrice[seller][sig] = price;

    sigToNumSales[sig] = sigToNumSales[sig].add(1);

    if (seller == owner) {
      _ownerSigToNumSales[sig] = _ownerSigToNumSales[sig].add(1);
    }

    emit SalePosted(seller, sig, price);
  }

  // NOTE: Special remove logic for contract owner's sale!
  function cancelSale(address seller, bytes32 sig) internal {
    if (seller == owner) {
      _ownerSigToNumSales[sig] = _ownerSigToNumSales[sig].sub(1);

      if (_ownerSigToNumSales[sig] == 0) {
        SaleListLib.removeSale(_sigToSortedSales[sig], seller);
        _addressToSigToSalePrice[seller][sig] = 0;
      }
    } else {
      SaleListLib.removeSale(_sigToSortedSales[sig], seller);
      _addressToSigToSalePrice[seller][sig] = 0;
    }
    sigToNumSales[sig] = sigToNumSales[sig].sub(1);

    emit SaleCancelled(seller, sig);
  }
}
function cancelSale(address seller, bytes32 sig) internal {
  if (seller == owner) {
    _ownerSigToNumSales[sig] = _ownerSigToNumSales[sig].sub(1);

    if (_ownerSigToNumSales[sig] == 0) {
      SaleListLib.removeSale(_sigToSortedSales[sig], seller);
      _addressToSigToSalePrice[seller][sig] = 0;
    }
  } else {
    SaleListLib.removeSale(_sigToSortedSales[sig], seller);
    _addressToSigToSalePrice[seller][sig] = 0;
  }
  sigToNumSales[sig] = sigToNumSales[sig].sub(1);

  emit SaleCancelled(seller, sig);
}

// NOTE: Special remove logic for contract owner's sale!