contract Runes is ERC20, Ownable {
    TheMysteriousWorld public mysteriousworld;

    uint256 public deployedStamp       = 0; // this is used to calculate the amount of $RUNES a user has from the current block.timestamp
    uint256 public runesPerDay         = 10 ether; // this ends up being 25 $RUNES per day. this might change in the future depending on how the collection grows overtime.
    bool    public allowRuneCollecting = false; // this lets you claim your $RUNES from the contract to the wallet

    mapping(address => uint256) public runesObtained; // this tracks how much $RUNES each address earned
    mapping(address => uint256) public lastTimeCollectedRunes; // this sets the block.timestamp to the address so it subtracts the timestamp from the pending rewards
    mapping(address => bool)    public contractWallets; // these are used to interact with the burning mechanisms of the contract - these will only be set to contracts related to The Mysterious World

    constructor() ERC20("Runes", "Runes") {
        deployedStamp = block.timestamp;
    }

    /*
     * # onlyContractWallets
     * blocks anyone from accessing it but contract wallets
     */
    modifier onlyContractWallets() {
        require(contractWallets[msg.sender], "You angered the gods!");
        _;
    }

    /*
     * # onlyWhenCollectingIsEnabled
     * blocks anyone from accessing functions that require allowRuneCollecting
     */
    modifier onlyWhenCollectingIsEnabled() {
        require(allowRuneCollecting, "You angered the gods!");
        _;
    }

    /*
     * # setRuneCollecting
     * enables or disables users to withdraw their runes - should only be called once unless the gods intended otherwise
     */
    function setRuneCollecting(bool newState) public payable onlyOwner {
        allowRuneCollecting = newState;
    }

    /*
     * # setDeployedStamp
     * sets the timestamp for when the $RUNES should start being generated
     */
    function setDeployedStamp(bool forced, uint256 stamp) public payable onlyOwner {
        if (forced) {
            deployedStamp = stamp;
        } else {
            deployedStamp = block.timestamp;
        }
    }

    /*
     * # setRunesPerDay
     * incase we want to change the amount gained per day, the gods can set it here
     */
    function setRunesPerDay(uint256 newRunesPerDay) public payable onlyOwner {
        runesPerDay = newRunesPerDay;
    }

    /*
     * # setMysteriousWorldContract
     * sets the address to the deployed Mysterious World contract
     */
    function setMysteriousWorldContract(address contractAddress) public payable onlyOwner {
        mysteriousworld = TheMysteriousWorld(contractAddress);
    }

    /*
     * # setContractWallets
     * enables or disables a contract wallet from interacting with the burn mechanics of the contract
     */
    function setContractWallets(address contractAddress, bool newState) public payable onlyOwner {
        contractWallets[contractAddress] = newState;
    }

    /*
     * # getPendingRunes
     * calculates the runes a inhabitant has from the last time they claimed and the deployedStamp time
     */
    function getPendingRunes(address inhabitant) internal view returns(uint256) {
        uint256 sumOfRunes = mysteriousworld.balanceOf(inhabitant) * runesPerDay;

        if (lastTimeCollectedRunes[inhabitant] >= deployedStamp) {
            return sumOfRunes * ((block.timestamp - lastTimeCollectedRunes[inhabitant])) / 86400;
        } else {
            return sumOfRunes * ((block.timestamp - deployedStamp)) / 86400;
        }
    }

    /*
     * # getUnclaimedRunes
     * returns the total amount of unclaimed runes a wallet has
     */
    function getUnclaimedRunes(address inhabitant) external view returns(uint256) {
        return getPendingRunes(inhabitant);
    }

    /*
     * # getTotalRunes
     * returns the runesObtained and getPendingRunes for the inhabitant passed
     */
    function getTotalRunes(address inhabitant) external view returns(uint256) {
        return runesObtained[inhabitant] + getPendingRunes(inhabitant);
    }

    /*
     * # burn
     * removes the withdrawn $RUNES from the wallet provided for the amount provided
     */
    function burn(address inhabitant, uint256 cost) external payable onlyContractWallets {
        _burn(inhabitant, cost);
    }

    /*
     * # claimRunes
     * takes the pending $RUNES and puts it into your wallet... you earned these, the gods aren't angry
     */
    function claimRunes() external payable onlyWhenCollectingIsEnabled {
        _mint(msg.sender, runesObtained[msg.sender] + getPendingRunes(msg.sender));

        runesObtained[msg.sender] = 0;
        lastTimeCollectedRunes[msg.sender] = block.timestamp;
    }

    /*
     * # updateRunes
     * updates the pending balance for both of the wallets associated to the transfer so they don't lose the $RUNES generated
     */
    function updateRunes(address from, address to) external onlyContractWallets {
        if (from != address(0) && from != mysteriousworld.ritualWallet()) {
            runesObtained[from]          += getPendingRunes(from);
            lastTimeCollectedRunes[from] = block.timestamp;
        }

        if (to != address(0) && to != mysteriousworld.ritualWallet()) {
            runesObtained[to]          += getPendingRunes(to);
            lastTimeCollectedRunes[to] = block.timestamp;
        }
    }
}
/*
 * 888d888888  88888888b.  .d88b. .d8888b  
 * 888P"  888  888888 "88bd8P  Y8b88K      
 * 888    888  888888  88888888888"Y8888b. 
 * 888    Y88b 888888  888Y8b.         X88 
 * 888     "Y88888888  888 "Y8888  88888P' 
 */
function setDeployedStamp(bool forced, uint256 stamp) public payable onlyOwner {
    if (forced) {
        deployedStamp = stamp;
    } else {
        deployedStamp = block.timestamp;
    }
}

/*
 * # setDeployedStamp
 * sets the timestamp for when the $RUNES should start being generated
 */