contract PepeCore is PullPayment, OwnerRegistry, SaleRegistry, ArtistRegistry {
  using SafeMath for uint256;

  uint256 constant public totalTxFeePercent = 4;

  ////////////////////
  // Shareholder stuff
  ////////////////////

  // Only 3 equal shareholders max allowed on this contract representing the three equal-partner founders
  // involved in its inception
  address public shareholder1;
  address public shareholder2;
  address public shareholder3;

  // 0 -> 3 depending on contract state. I only use uint256 so that I can use SafeMath...
  uint256 public numShareholders = 0;

  // Used to set initial shareholders
  function addShareholderAddress(address newShareholder) external onlyOwner {
    // Don't let shareholder be address(0)
    require(newShareholder != address(0));

    // Contract owner can't be a shareholder
    require(newShareholder != owner);

    // Must be an open shareholder spot!
    require(shareholder1 == address(0) || shareholder2 == address(0) || shareholder3 == address(0));

    if (shareholder1 == address(0)) {
      shareholder1 = newShareholder;
      numShareholders = numShareholders.add(1);
    } else if (shareholder2 == address(0)) {
      shareholder2 = newShareholder;
      numShareholders = numShareholders.add(1);
    } else if (shareholder3 == address(0)) {
      shareholder3 = newShareholder;
      numShareholders = numShareholders.add(1);
    }
  }

  // Splits the amount specified among shareholders equally
  function payShareholders(uint256 amount) internal {
    // If no shareholders, shareholder fees will be held in contract to be withdrawable by owner
    if (numShareholders > 0) {
      uint256 perShareholderFee = amount.div(numShareholders);

      if (shareholder1 != address(0)) {
        asyncSend(shareholder1, perShareholderFee);
      }

      if (shareholder2 != address(0)) {
        asyncSend(shareholder2, perShareholderFee);
      }

      if (shareholder3 != address(0)) {
        asyncSend(shareholder3, perShareholderFee);
      }
    }
  }

  ////////////////
  // Admin actions
  ////////////////

  function withdrawContractBalance() external onlyOwner {
    uint256 contractBalance = address(this).balance;
    uint256 withdrawableBalance = contractBalance.sub(totalPayments);

    // No withdrawal necessary if <= 0 balance
    require(withdrawableBalance > 0);

    msg.sender.transfer(withdrawableBalance);
  }

  function addCard(bytes32 sig,
                   address artist,
                   uint256 txFeePercent,
                   uint256 genesisSalePercent,
                   uint256 numToAdd,
                   uint256 startingPrice) external onlyOwner {
    addCardToRegistry(owner, sig, numToAdd);

    addArtistToRegistry(sig, artist, txFeePercent, genesisSalePercent);

    postGenesisSales(sig, startingPrice, numToAdd);
  }

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

  function createSale(bytes32 sig, uint256 price) external {
    // Can't sell a card for 0... May want other limits in the future
    require(price > 0);

    // Can't sell a card you don't own
    require(getNumSigsOwned(sig) > 0);

    // Can't post a sale if you have one posted already! Unless you're the contract owner
    require(msg.sender == owner || _addressToSigToSalePrice[msg.sender][sig] == 0);

    postSale(msg.sender, sig, price);
  }

  function removeSale(bytes32 sig) public {
    // Can't cancel a sale that doesn't exist
    require(_addressToSigToSalePrice[msg.sender][sig] > 0);

    cancelSale(msg.sender, sig);
  }

  function computeTxFee(uint256 price) private pure returns (uint256) {
    return (price * totalTxFeePercent) / 100;
  }

  // If card is held by contract owner, split among artist + shareholders
  function paySellerFee(bytes32 sig, address seller, uint256 sellerProfit) private {
    if (seller == owner) {
      address artist = getArtist(sig);
      uint256 artistFee = computeArtistGenesisSaleFee(sig, sellerProfit);
      asyncSend(artist, artistFee);

      payShareholders(sellerProfit.sub(artistFee));
    } else {
      asyncSend(seller, sellerProfit);
    }
  }

  // Simply pay out tx fees appropriately
  function payTxFees(bytes32 sig, uint256 txFee) private {
    uint256 artistFee = computeArtistTxFee(sig, txFee);
    address artist = getArtist(sig);
    asyncSend(artist, artistFee);

    payShareholders(txFee.sub(artistFee));
  }

  // Handle wallet debit if necessary, pay out fees, pay out seller profit, cancel sale, transfer card
  function buy(bytes32 sig) external payable {
    address seller;
    uint256 price;
    (seller, price) = getBestSale(sig);

    // There must be a valid sale for the card
    require(price > 0 && seller != address(0));

    // Buyer must have enough Eth via wallet and payment to cover posted price
    uint256 availableEth = msg.value.add(payments[msg.sender]);
    require(availableEth >= price);

    // Debit wallet if msg doesn't have enough value to cover price
    if (msg.value < price) {
      asyncDebit(msg.sender, price.sub(msg.value));
    }

    // Split out fees + seller profit
    uint256 txFee = computeTxFee(price);
    uint256 sellerProfit = price.sub(txFee);

    // Pay out seller (special logic for seller == owner)
    paySellerFee(sig, seller, sellerProfit);

    // Pay out tx fees
    payTxFees(sig, txFee);

    // Cancel sale
    cancelSale(seller, sig);

    // Transfer single sig ownership in registry
    registryTransfer(seller, msg.sender, sig, 1);
  }

  // Can also be used in airdrops, etc.
  function transferSig(bytes32 sig, uint256 count, address newOwner) external {
    uint256 numOwned = getNumSigsOwned(sig);

    // Can't transfer cards you don't own
    require(numOwned >= count);

    // If transferring from contract owner, cancel the proper number of sales if necessary
    if (msg.sender == owner) {
      uint256 remaining = numOwned.sub(count);

      if (remaining < _ownerSigToNumSales[sig]) {
        uint256 numSalesToCancel = _ownerSigToNumSales[sig].sub(remaining);

        for (uint256 i = 0; i < numSalesToCancel; i++) {
          removeSale(sig);
        }
      }
    } else {
      // Remove existing sale if transferring all owned cards
      if (numOwned == count && _addressToSigToSalePrice[msg.sender][sig] > 0) {
        removeSale(sig);
      }
    }

    // Transfer in registry
    registryTransfer(msg.sender, newOwner, sig, count);
  }
}
function payTxFees(bytes32 sig, uint256 txFee) private {
    uint256 artistFee = computeArtistTxFee(sig, txFee);
    address artist = getArtist(sig);
    asyncSend(artist, artistFee);

    payShareholders(txFee.sub(artistFee));
  }
// Simply pay out tx fees appropriately