contract PixelAuction is PixelMinting {

    // Represents an auction on an NFT
    struct Auction {
         // Current state of the auction.
        address highestBidder;
        uint highestBid;
        uint256 endTime;
        bool live;
    }

    // Map from token ID to their corresponding auction.
    mapping (uint256 => Auction) tokenIdToAuction;
    // Allowed withdrawals of previous bids
    mapping (address => uint) pendingReturns;

    // Duration of an auction
    uint256 public duration = 60 * 60 * 24 * 4;
    // Auctions will be enabled later
    bool public auctionsEnabled = false;

    // Change the duration for new auctions
    function setDuration(uint _duration) external onlyAuthority {
        duration = _duration;
    }

    // Enable or disable auctions
    function setAuctionsEnabled(bool _auctionsEnabled) external onlyAuthority {
        auctionsEnabled = _auctionsEnabled;
    }

    // create a new auctions for a given pixel, only owner or authority can do this
    // The authority will only do this if pixels are misused or lost
    function createAuction(
        uint256 _tokenId
    )
        external payable
    {
        // only authority or owner can start auction
        require(auctionsEnabled);
        require(_owns(msg.sender, _tokenId) || msg.sender == authorityAddress);
        // No auction is currently running
        require(!tokenIdToAuction[_tokenId].live);

        uint startPrice = pixelPrice;
        if (msg.sender == authorityAddress) {
            startPrice = 0;
        }

        require(msg.value == startPrice);
        // this prevents transfers during the auction
        pixelIndexToApproved[_tokenId] = address(this);

        tokenIdToAuction[_tokenId] = Auction(
            msg.sender,
            startPrice,
            block.timestamp + duration,
            true
        );
        AuctionStarted(_tokenId);
    }

    // bid for an pixel auction
    function bid(uint256 _tokenId) external payable {
        // No arguments are necessary, all
        // information is already part of
        // the transaction. The keyword payable
        // is required for the function to
        // be able to receive Ether.
        Auction storage auction = tokenIdToAuction[_tokenId];

        // Revert the call if the bidding
        // period is over.
        require(auction.live);
        require(auction.endTime > block.timestamp);

        // If the bid is not higher, send the
        // money back.
        require(msg.value > auction.highestBid);

        if (auction.highestBidder != 0) {
            // Sending back the money by simply using
            // highestBidder.send(highestBid) is a security risk
            // because it could execute an untrusted contract.
            // It is always safer to let the recipients
            // withdraw their money themselves.
            pendingReturns[auction.highestBidder] += auction.highestBid;
        }
        
        auction.highestBidder = msg.sender;
        auction.highestBid = msg.value;

        HighestBidIncreased(_tokenId, msg.sender, msg.value);
    }

    /// Withdraw a bid that was overbid.
    function withdraw() external returns (bool) {
        uint amount = pendingReturns[msg.sender];
        if (amount > 0) {
            // It is important to set this to zero because the recipient
            // can call this function again as part of the receiving call
            // before `send` returns.
            pendingReturns[msg.sender] = 0;

            if (!msg.sender.send(amount)) {
                // No need to call throw here, just reset the amount owing
                pendingReturns[msg.sender] = amount;
                return false;
            }
        }
        return true;
    }

    // /// End the auction and send the highest bid
    // /// to the beneficiary.
    function endAuction(uint256 _tokenId) external {
        // It is a good guideline to structure functions that interact
        // with other contracts (i.e. they call functions or send Ether)
        // into three phases:
        // 1. checking conditions
        // 2. performing actions (potentially changing conditions)
        // 3. interacting with other contracts
        // If these phases are mixed up, the other contract could call
        // back into the current contract and modify the state or cause
        // effects (ether payout) to be performed multiple times.
        // If functions called internally include interaction with external
        // contracts, they also have to be considered interaction with
        // external contracts.

        Auction storage auction = tokenIdToAuction[_tokenId];

        // 1. Conditions
        require(auction.endTime < block.timestamp);
        require(auction.live); // this function has already been called

        // 2. Effects
        auction.live = false;
        AuctionEnded(_tokenId, auction.highestBidder, auction.highestBid);

        // 3. Interaction
        address owner = pixelIndexToOwner[_tokenId];
        // transfer money without 
        uint amount = auction.highestBid * 9 / 10;
        pendingReturns[owner] += amount;
        authorityBalance += (auction.highestBid - amount);
        // transfer token
        _transfer(owner, auction.highestBidder, _tokenId);

       
    }

    // // Events that will be fired on changes.
    event AuctionStarted(uint256 _tokenId);
    event HighestBidIncreased(uint256 _tokenId, address bidder, uint amount);
    event AuctionEnded(uint256 _tokenId, address winner, uint amount);


    /// @dev Returns auction info for an NFT on auction.
    /// @param _tokenId - ID of NFT on auction.
    function getAuction(uint256 _tokenId)
        external
        view
        returns
    (
        address highestBidder,
        uint highestBid,
        uint endTime,
        bool live
    ) {
        Auction storage auction = tokenIdToAuction[_tokenId];
        return (
            auction.highestBidder,
            auction.highestBid,
            auction.endTime,
            auction.live
        );
    }

    /// @dev Returns the current price of an auction.
    /// @param _tokenId - ID of the token price we are checking.
    function getHighestBid(uint256 _tokenId)
        external
        view
        returns (uint256)
    {
        Auction storage auction = tokenIdToAuction[_tokenId];
        return auction.highestBid;
    }
}
/// @title all functions for managing pixel auctions
function bid(uint256 _tokenId) external payable {
    // No arguments are necessary, all
    // information is already part of
    // the transaction. The keyword payable
    // is required for the function to
    // be able to receive Ether.
    Auction storage auction = tokenIdToAuction[_tokenId];

    // Revert the call if the bidding
    // period is over.
    require(auction.live);
    require(auction.endTime > block.timestamp);

    // If the bid is not higher, send the
    // money back.
    require(msg.value > auction.highestBid);

    if (auction.highestBidder != 0) {
        // Sending back the money by simply using
        // highestBidder.send(highestBid) is a security risk
        // because it could execute an untrusted contract.
        // It is always safer to let the recipients
        // withdraw their money themselves.
        pendingReturns[auction.highestBidder] += auction.highestBid;
    }
        
    auction.highestBidder = msg.sender;
    auction.highestBid = msg.value;

    HighestBidIncreased(_tokenId, msg.sender, msg.value);
}

// bid for an pixel auction