contract Marketplace is Ownable, SellersAuthorization {
  using SafeMath for uint256;

  uint256 public offerIdCounter = 0;
  bool public paused = false;

  event OfferCreated(
    address indexed _tokenAddress,
    address indexed _seller,
    uint256 _tokenId,
    uint256 _tokenAmount,
    uint256 _price,
    uint256 _offerIdCounter
  );
  event OfferCancelled(
    address indexed _tokenAddress,
    uint256 _tokenId,
    uint256 _offerId
  );
  event OfferSuccess(
    address indexed _tokenAddress,
    uint256 _tokenId,
    uint256 _price,
    uint256 _offerIdCounter,
    address _newOwner
  );

  modifier isNotPaused() {
    require(!paused, 'Contract paused');
    _;
  }

  /*
   * @dev hasTokens Makes sure the seller has enough tokens.
   */
  modifier hasTokens(
    address _token,
    address _seller,
    uint256 _tokenId,
    uint256 _tokenAmount
  ) {
    require(
      IERC1155(_token).balanceOf(_seller, _tokenId) >= _tokenAmount,
      'Seller does not own enough tokens'
    );
    _;
  }

  struct Offer {
    address tokenAddress;
    address payable seller;
    uint256 tokenId;
    uint256 tokenAmount;
    uint256 price; // The price for 1 (one) token.
  }

  mapping(uint256 => Offer) private offers;

  /**
   * @dev Creates an offer for the give tokenId.
   * @dev Needs to approve this contract first
   * @param _tokenAddress The token contract address
   * @param _tokenId The token Id on sale.
   * @param _tokenAmount Amount of tokens to sell
   * @param _price The price of the token Id on sale.
   */
  function createOffer(
    address _tokenAddress,
    uint256 _tokenId,
    uint256 _tokenAmount,
    uint256 _price
  )
    public
    hasTokens(_tokenAddress, msg.sender, _tokenId, _tokenAmount)
    isNotPaused
  {
    require(isSeller(msg.sender));
    require(
      IERC1155(_tokenAddress).isApprovedForAll(msg.sender, address(this)),
      'Missing approval'
    );
    require(_price > 0);

    offers[offerIdCounter].tokenAddress = _tokenAddress;
    offers[offerIdCounter].tokenId = _tokenId;
    offers[offerIdCounter].seller = msg.sender;
    offers[offerIdCounter].tokenAmount = _tokenAmount;
    offers[offerIdCounter].price = _price;

    emit OfferCreated(
      _tokenAddress,
      msg.sender,
      _tokenId,
      _tokenAmount,
      _price,
      offerIdCounter
    );

    offerIdCounter = offerIdCounter.add(1);
  }

  /**
   * @dev Removed an offer.
   * @param _offerId The offer Id that has to be removed.
   * @notice Require msg.sender equals to the offer seller.
   */
  function deleteOffer(uint256 _offerId) public isNotPaused {
    require(
      offers[_offerId].seller == msg.sender,
      'Msg.sender is not the seller'
    );
    emit OfferCancelled(
      offers[_offerId].tokenAddress,
      offers[_offerId].tokenId,
      _offerId
    );
    delete (offers[_offerId]);
  }

  /**
   * @dev Allows user to buy token.
   * @param _offerId The offer Id the user wants to buy.
   * @param _tokenAmount The amount of tokens the user wants to buy.
   * @notice Pushes the payment to seller.
   */
  function buyToken(uint256 _offerId, uint256 _tokenAmount)
    public
    payable
    isNotPaused
    hasTokens(
      offers[_offerId].tokenAddress,
      offers[_offerId].seller,
      offers[_offerId].tokenId,
      _tokenAmount
    )
  {
    require(_tokenAmount > 0, 'Token amount cannot be 0');
    require(
      msg.value == offers[_offerId].price.mul(_tokenAmount),
      'Invalid amount'
    );
    require(
      IERC1155(offers[_offerId].tokenAddress).isApprovedForAll(
        offers[_offerId].seller,
        address(this)
      ),
      'Missing approval'
    );

    offers[_offerId].tokenAmount = offers[_offerId].tokenAmount.sub(
      _tokenAmount
    );

    IERC1155(offers[_offerId].tokenAddress).safeTransferFrom(
      offers[_offerId].seller,
      msg.sender,
      offers[_offerId].tokenId,
      _tokenAmount,
      '0x0'
    );

    _pushPayment(offers[_offerId].seller, msg.value);

    emit OfferSuccess(
      offers[_offerId].tokenAddress,
      offers[_offerId].tokenId,
      msg.value,
      _offerId,
      msg.sender
    );
  }

  /**
   * @dev A wrapper for _addSeller ().
   * @param _newSeller The address of the new seller enabled.
   * @notice Function restricted to the contract owner.
   */
  function addSeller(address _newSeller) public onlyOwner {
    require(_addSeller(_newSeller));
  }

  /**
   * @dev A wrapper for _removeSeller ().
   * @param _seller The address of the seller to remove.
   * @notice Function restricted to the contract owner.
   */
  function removeSeller(address _seller) public onlyOwner {
    require(!_removeSeller(_seller));
  }

  /**
   * @dev Returns offer details for given offer id.
   * @param _offerId The offer id.
   * @notice If seller balanceOf is lower than the amount of tokens on sale
   * returns the balanceOf (_seller).
   */
  function getOffer(uint256 _offerId) public view returns (Offer memory) {
    Offer memory _offer = offers[_offerId];
    uint256 balanceOf = IERC1155(_offer.tokenAddress).balanceOf(
      _offer.seller,
      _offer.tokenId
    );
    if (_offer.tokenAmount > balanceOf) {
      _offer.tokenAmount = balanceOf;
    }
    return _offer;
  }

  /**
   * @dev Pause the contract.
   * @param _paused True if should pause.
   */
  function pauseContract(bool _paused) public onlyOwner {
    paused = _paused;
  }

  /**
   * @dev Safe.
   */
  function withdrawAll() public onlyOwner {
    msg.sender.transfer(address(this).balance);
  }
}
function createOffer(
  address _tokenAddress,
  uint256 _tokenId,
  uint256 _tokenAmount,
  uint256 _price
)
  public
  hasTokens(_tokenAddress, msg.sender, _tokenId, _tokenAmount)
  isNotPaused
{
  require(isSeller(msg.sender));
  require(
    IERC1155(_tokenAddress).isApprovedForAll(msg.sender, address(this)),
    'Missing approval'
  );
  require(_price > 0);

  offers[offerIdCounter].tokenAddress = _tokenAddress;
  offers[offerIdCounter].tokenId = _tokenId;
  offers[offerIdCounter].seller = msg.sender;
  offers[offerIdCounter].tokenAmount = _tokenAmount;
  offers[offerIdCounter].price = _price;

  emit OfferCreated(
    _tokenAddress,
    msg.sender,
    _tokenId,
    _tokenAmount,
    _price,
    offerIdCounter
  );

  offerIdCounter = offerIdCounter.add(1);
}

/**
 * @dev Creates an offer for the give tokenId.
 * @dev Needs to approve this contract first
 * @param _tokenAddress The token contract address
 * @param _tokenId The token Id on sale.
 * @param _tokenAmount Amount of tokens to sell
 * @param _price The price of the token Id on sale.
 */