pragma solidity ^0.6.0;

contract RHBay {
    
    struct item {
        int initTimestamp;
        int endTimestamp;
        address payable highestBidderAddress;
        uint256 highestBidderBid;
    }
    
    mapping (string => item) private items;
    address[] verifiedList; //This is the list of addresses who have sent to contract the amount of ether nessesary to complete the lab
    
    address owner; // who owns the contract
    
    /*
    * constructor(): sets up two auctions upon construction of the smart contract
    * @arg nothing
    * @returns nothing
    */
    constructor() public {
        owner = msg.sender;
        updateOrAddItem("F-CSSE490", 604800); // 604800 seconds = 7 days
        updateOrAddItem("A-CSSE490", 1800); // 60 seconds * 30 min = 1800 seconds (30 min)
    }
    
    /*
    * updateOrAddItem(): adds/updates an item to be up for bid.
    * @arg itemName - the item name that you want to add.
    * @arg timeOfAuction - the length in seconds of the auction.
    * @returns nothing
    */
    function updateOrAddItem(string memory itemName, uint timeOfAuction) public{
        require(msg.sender == owner, "You can't do this unless you own the contract");
        require(int(now) >= items[itemName].endTimestamp); //the item must not exist or already done
        items[itemName].initTimestamp = int(now);
        items[itemName].endTimestamp = int(now) + int(timeOfAuction);
        items[itemName].highestBidderAddress = payable(address(owner)); 
          //payable(address(0xe04fce5AEC019aE45237B778FAF7C06aF8a0243B)); // Prof's address
          //payable(address(0x066e27E3b75Be9f0c450AD6af18dB49b250547C1)); // TA's address
        items[itemName].highestBidderBid = 0;
    }

    /*
    * bid(): allows the caller to bid on an item.
    * @arg itm - the item name you want to bid on.
    * @returns nothing
    */
    function bid(string memory itm) public payable {
        require(msg.value > items[itm].highestBidderBid); // Is the new value higher?
        require(int(now) <= items[itm].endTimestamp); // Is there still time?
        bool isSuccessfulRefund = items[itm].highestBidderAddress.send(items[itm].highestBidderBid); // refund the bidder who was out bid.
        require(isSuccessfulRefund);
        items[itm].highestBidderAddress = msg.sender; // New highest bidder
        items[itm].highestBidderBid = msg.value;
    }
    
    /*
    * finishItem(): allows the item to restart and reward the winner. Can be called by anyone to restart.
    *               An item cannot be finished/restarted until the endTimestamp is reached.
    * @arg itm - the item name you want to reward the current winner and restart the bid.
    * @returns nothing
    */
    function finishItem(string memory itm) public {
        require(int(now) > items[itm].endTimestamp); //Did the item finish?
        address payable backup = items[itm].highestBidderAddress;
        backup.transfer(1000 ether); // reward the winner!
        
    }
    
    /*
    * getCurrentHighestBidder(): allows the caller to get the highest bidder's address.
    * @arg itm - the item name you want to check.
    * @returns address - the address of the highest bidder's address.
    */
    function getCurrentHighestBidder(string memory itm) public view returns(address){
        return items[itm].highestBidderAddress;
    }
    
    /*
    * getCurrentHighestBid(): allows the caller to get the highest bidder's bid.
    * @arg itm - the item name you want to check.
    * @returns uint256 - the address of the highest bidder's bid.
    */
    function getCurrentHighestBid(string memory itm) public view returns(uint256){
        return items[itm].highestBidderBid;
    }
    
    /*
    * getTimeLeft(): allows the caller to the time left on an item. (negative means it is over)
    * @arg itm - the item name you want to check.
    * @returns int - the time left in seconds.
    */
    function getTimeLeft(string memory itm) public view returns(int){
        return (items[itm].endTimestamp - int(now));
    }
    
    /*
    * getAllVerified(): allows the caller to get the list if all people who won the bid.
    * @arg nothing
    * @returns address[] - the addresses of the people who deposited the 1000 ether.
    */
    function getAllVerified() public view returns(address[] memory) {
        return verifiedList;
    }
    
    /*
    * verifyGrade(): allows the caller to pay the 1000 ether to prove they won the lab.
    * @arg nothing
    * @returns nothing
    */
    function verifyGrade() public payable{
        require(msg.value >= (1000 ether));
        verifiedList.push(msg.sender);
    }
    
    
    ///////////////////// FUNCTIONS FOR MANAGING CONTRACT ETH BALANCE ////////////////////////////////
    function fund() public payable { 
        require(msg.sender == owner, "fund() is for owner only, do you want 'verifyGrade()'?");
    }
    
    function withdraw(uint256 amt) public {
        require(msg.sender == owner, "Only owner can withdraw ether");
        payable(msg.sender).transfer(amt);
    }
    
    function destroy() public {
        require(msg.sender == owner);
        selfdestruct(payable(owner));
    }
    
    function getBalance() public view returns(uint256) {
        require(msg.sender == owner, "Only owner can get the balance");
        return address(this).balance;
    }
}