// File: Donation.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract FrogsDonation is Ownable {

    // State Variables //

    uint8 private donationState;
    uint256 public minimumDonation = 0.0069 ether;
    address public treasureContract = 0x34a2989271fe71B27A10AD1d563a3cEa565f2D74;
    address[] private donors;

    mapping(address => uint256) public donations;

    // Struct to structure leaderboard data
    struct donation {
        address donor;
        uint256 amount;
    }

    // GETTER FUNCTIONS //

    /**
     * @dev used to check whether donation is active or not
     * @return bool returns true if donation is active, otherwise false
     */
    function isDonationActive() external view returns (bool) {
        return donationState == 0 ? true : false;
    }

    // ADMIN & SETUP FUNCTIONS //
    
    /**
     * @dev admins can use to toggle donation state controlling donation
     */
    function toggleDonationState() external onlyOwner {
        donationState = donationState == 0 ? 1 : 0;
    }

    /**
     * @dev admins can use to update treasureContract
     * @param _treasureContract address to which treasureContract to be set to
     */
    function setTreasureContract(address _treasureContract) external onlyOwner {
        treasureContract = _treasureContract;
    }

    /**
     * @dev admins can update minimum donation limit set for new donaters
     * @param _minimumDonation new minimum donation admin wants to set
     */
    function setMinimumDonation(uint256 _minimumDonation) external onlyOwner {
        minimumDonation = _minimumDonation;
    }

    // PUBLIC FUNCTIONS //

    /**
     * @dev allows user to donate
     * @param amount amout of eth that sender what's to donate
     */
    function ruggMeDaddy(uint256 amount) external payable {
        require(donationState == 0, "donation is not acitve right now");
        bool alreadyBidder = alreadyBid(msg.sender);
        if(!alreadyBidder) {
            require(amount >= minimumDonation, "cannot donate less than minimum");
            donors.push(msg.sender);
        }
        require(amount == msg.value, "invalid amount sent");
        sendEther();
        donations[msg.sender] += amount;
    }

    /**
     * @dev returns leaderboard of all donations, sorted
     */
    function leaderBoard() external view returns (donation[] memory) {
        uint256 numberOfBidders = donors.length;
        if(numberOfBidders == 0) {
            return new donation[](0);
        }
        donation[] memory leaderboard = new donation[](numberOfBidders);
        for(uint256 i; i < numberOfBidders;) {
            address currentDonor = donors[i];
            leaderboard[i].donor = currentDonor;
            leaderboard[i].amount = donations[currentDonor];
            unchecked{
                i++;
            }
        }

        for(uint256 i; i < numberOfBidders - 1;) {
            for(uint256 j; j < numberOfBidders - i - 1;) {
                if(leaderboard[j].amount < leaderboard[j + 1].amount) {
                    (leaderboard[j], leaderboard[j + 1]) = (leaderboard[j + 1], leaderboard[j]);
                }
                unchecked {
                    j++;
                }
            }
            unchecked {
                i++;
            }
        }
        return leaderboard;
    }

    // MISC. & INTERNAL FUNCTIONS //

    /**
     * @dev checks whether given address has already bid or not
     * @param bidder address which you want to check
     */
    function alreadyBid(address bidder) internal view returns (bool) {
        return donations[bidder] != 0;
    }

    /**
     * @dev transfers msg.value to treasureContract and requires it's success
     */
    function sendEther() internal {
        (bool sent,) = treasureContract.call{value: msg.value}("");
        require(sent, "eth transfer failed");
    }
}
// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}