contract MarketPlaceBase is Ownable {

    // Represents an sale on an NFT
    struct Sale {
        // Current owner of NFT
        address seller;
        // Price (in wei) 
        uint128 price;
        // Time when sale started
        // NOTE: 0 if this sale has been concluded
        uint64 startedAt;
    }
    
    struct Affiliates {
        address affiliate_address;
        uint64 commission;
        uint64 pricecut;
    }
    
    //Affiliates[] affiliates;
    // Reference to contract tracking NFT ownership
    ERC721 public nonFungibleContract;

    // Cut owner takes on each sale, measured in basis points (1/100 of a percent).
    // Values 0-10,000 map to 0%-100%
    uint256 public ownerCut;

    //map the Affiliate Code to the Affiliate
    mapping (uint256 => Affiliates) codeToAffiliate;

    // Map from token ID to their corresponding sale.
    mapping (uint256 => Sale) tokenIdToSale;

    event SaleCreated(uint256 tokenId, uint256 price);
    event SaleSuccessful(uint256 tokenId, uint256 price, address buyer);
    event SaleCancelled(uint256 tokenId);

    /// @dev Returns true if the claimant owns the token.
    /// @param _claimant - Address claiming to own the token.
    /// @param _tokenId - ID of token whose ownership to verify.
    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
    }

    /// @dev Escrows the NFT, assigning ownership to this contract.
    /// Throws if the escrow fails.
    /// @param _owner - Current owner address of token to escrow.
    /// @param _tokenId - ID of token whose approval to verify.
    function _escrow(address _owner, uint256 _tokenId) internal {
        // it will throw if transfer fails
        nonFungibleContract.transferFrom(_owner, this, _tokenId);
    }

    /// @dev Transfers an NFT owned by this contract to another address.
    /// Returns true if the transfer succeeds.
    /// @param _receiver - Address to transfer NFT to.
    /// @param _tokenId - ID of token to transfer.
    function _transfer(address _receiver, uint256 _tokenId) internal {
        // it will throw if transfer fails
        nonFungibleContract.transfer(_receiver, _tokenId);
    }

    /// @dev Adds an sale to the list of open sales. Also fires the
    ///  SaleCreated event.
    /// @param _tokenId The ID of the token to be put on sale.
    /// @param _sale Sale to add.
    function _addSale(uint256 _tokenId, Sale _sale) internal {
        

        tokenIdToSale[_tokenId] = _sale;

        SaleCreated(
            uint256(_tokenId),
            uint256(_sale.price)
        );
    }

    /// @dev Cancels a sale unconditionally.
    function _cancelSale(uint256 _tokenId, address _seller) internal {
        _removeSale(_tokenId);
        _transfer(_seller, _tokenId);
        SaleCancelled(_tokenId);
    }

    /// @dev Computes the price and transfers winnings.
    /// Does NOT transfer ownership of token.
    function _bid(uint256 _tokenId, uint256 _bidAmount)
        internal
        returns (uint256)
    {
        // Get a reference to the sale struct
        Sale storage sale = tokenIdToSale[_tokenId];

        // Explicitly check that this sale is currently live.
        // (Because of how Ethereum mappings work, we can't just count
        // on the lookup above failing. An invalid _tokenId will just
        // return a sale object that is all zeros.)
        require(_isOnSale(sale));

        // Check that the bid is greater than or equal to the current price
        uint256 price = sale.price;
        require(_bidAmount >= price);

        // Grab a reference to the seller before the sale struct
        // gets deleted.
        address seller = sale.seller;

        // The bid is good! Remove the sale before sending the fees
        // to the sender so we can't have a reentrancy attack.
        _removeSale(_tokenId);

        // Transfer proceeds to seller (if there are any!)
        if (price > 0) {
            // Calculate the Marketplace's cut.
            // (NOTE: _computeCut() is guaranteed to return a
            // value <= price, so this subtraction can't go negative.)
            uint256 marketplaceCut = _computeCut(price);
            uint256 sellerProceeds = price - marketplaceCut;

            // NOTE: Doing a transfer() in the middle of a complex
            // method like this is generally discouraged because of
            // reentrancy attacks and DoS attacks if the seller is
            // a contract with an invalid fallback function. We explicitly
            // guard against reentrancy attacks by removing the auction
            // before calling transfer(), and the only thing the seller
            // can DoS is the sale of their own asset! (And if it's an
            // accident, they can call cancelAuction(). )
            seller.transfer(sellerProceeds);
        }

        // Calculate any excess funds included with the bid. If the excess
        // is anything worth worrying about, transfer it back to bidder.
        // NOTE: We checked above that the bid amount is greater than or
        // equal to the price so this cannot underflow.
        uint256 bidExcess = _bidAmount - price;

        // Return the funds. Similar to the previous transfer, this is
        // not susceptible to a re-entry attack because the auction is
        // removed before any transfers occur.
        msg.sender.transfer(bidExcess);

        // Tell the world!
        SaleSuccessful(_tokenId, price, msg.sender);

        return price;
    }

    /// @dev Removes a sale from the list of open sales.
    /// @param _tokenId - ID of NFT on sale.
    function _removeSale(uint256 _tokenId) internal {
        delete tokenIdToSale[_tokenId];
    }

    /// @dev Returns true if the NFT is on sale.
    /// @param _sale - Sale to check.
    function _isOnSale(Sale storage _sale) internal view returns (bool) {
        return (_sale.startedAt > 0);
    }


    /// @dev Computes owner's cut of a sale.
    /// @param _price - Sale price of NFT.
    function _computeCut(uint256 _price) internal view returns (uint256) {
        // NOTE: We don't use SafeMath (or similar) in this function because
        //  all of our entry functions carefully cap the maximum values for
        //  currency (at 128-bits), and ownerCut <= 10000 (see the require()
        //  statement in the Marketplace constructor). The result of this
        //  function is always guaranteed to be <= _price.
        return _price * ownerCut / 10000;
    }
    function _computeAffiliateCut(uint256 _price,Affiliates affiliate) internal view returns (uint256) {
        // NOTE: We don't use SafeMath (or similar) in this function because
        //  all of our entry functions carefully cap the maximum values for
        //  currency (at 128-bits), and ownerCut <= 10000 (see the require()
        //  statement in the Marketplace constructor). The result of this
        //  function is always guaranteed to be <= _price.
        return _price * affiliate.commission / 10000;
    }
    /// @dev Adds an affiliate to the list.
    /// @param _code The referall code of the affiliate.
    /// @param _affiliate Affiliate to add.
    function _addAffiliate(uint256 _code, Affiliates _affiliate) internal {
        codeToAffiliate[_code] = _affiliate;
   
    }
    
    /// @dev Removes a affiliate from the list.
    /// @param _code - The referall code of the affiliate.
    function _removeAffiliate(uint256 _code) internal {
        delete codeToAffiliate[_code];
    }
    
    
    //_bidReferral(_tokenId, msg.value);
    /// @dev Computes the price and transfers winnings.
    /// Does NOT transfer ownership of token.
    function _bidReferral(uint256 _tokenId, uint256 _bidAmount,Affiliates _affiliate)
        internal
        returns (uint256)
    {
        
        // Get a reference to the sale struct
        Sale storage sale = tokenIdToSale[_tokenId];

        //Only Owner of Contract can sell referrals
        require(sale.seller==owner);

        // Explicitly check that this sale is currently live.
        // (Because of how Ethereum mappings work, we can't just count
        // on the lookup above failing. An invalid _tokenId will just
        // return a sale object that is all zeros.)
        require(_isOnSale(sale));
        // Check that the bid is greater than or equal to the current price
        
        uint256 price = sale.price;
        
        //deduce the affiliate pricecut
        price=price * _affiliate.pricecut / 10000;  
        require(_bidAmount >= price);

        // Grab a reference to the seller before the sale struct
        // gets deleted.
        address seller = sale.seller;
        address affiliate_address = _affiliate.affiliate_address;
        
        // The bid is good! Remove the sale before sending the fees
        // to the sender so we can't have a reentrancy attack.
        _removeSale(_tokenId);

        // Transfer proceeds to seller (if there are any!)
        if (price > 0) {
            // Calculate the Marketplace's cut.
            // (NOTE: _computeCut() is guaranteed to return a
            // value <= price, so this subtraction can't go negative.)
            uint256 affiliateCut = _computeAffiliateCut(price,_affiliate);
            uint256 sellerProceeds = price - affiliateCut;

            // NOTE: Doing a transfer() in the middle of a complex
            // method like this is generally discouraged because of
            // reentrancy attacks and DoS attacks if the seller is
            // a contract with an invalid fallback function. We explicitly
            // guard against reentrancy attacks by removing the auction
            // before calling transfer(), and the only thing the seller
            // can DoS is the sale of their own asset! (And if it's an
            // accident, they can call cancelAuction(). )
            seller.transfer(sellerProceeds);
            affiliate_address.transfer(affiliateCut);
        }

        // Calculate any excess funds included with the bid. If the excess
        // is anything worth worrying about, transfer it back to bidder.
        // NOTE: We checked above that the bid amount is greater than or
        // equal to the price so this cannot underflow.
        uint256 bidExcess = _bidAmount - price;

        // Return the funds. Similar to the previous transfer, this is
        // not susceptible to a re-entry attack because the auction is
        // removed before any transfers occur.
        msg.sender.transfer(bidExcess);

        // Tell the world!
        SaleSuccessful(_tokenId, price, msg.sender);

        return price;
    }
}
/// @title MarketPlace core
/// @dev Contains models, variables, and internal methods for the marketplace.
/// @notice We omit a fallback function to prevent accidental sends to this contract.
function _escrow(address _owner, uint256 _tokenId) internal {
    // it will throw if transfer fails
    nonFungibleContract.transferFrom(_owner, this, _tokenId);
}

/// @dev Escrows the NFT, assigning ownership to this contract.
/// Throws if the escrow fails.
/// @param _owner - Current owner address of token to escrow.
/// @param _tokenId - ID of token whose approval to verify.