contract Inflation is
    SafeMath,
    InflationFace
{
    address public immutable override RIGO_TOKEN_ADDRESS;
    address public immutable override STAKING_PROXY_ADDRESS;

    uint256 public override slot;
    uint256 public override epochLength;

    uint32 internal immutable PPM_DENOMINATOR = 10**6; // 100% in parts-per-million
    uint256 internal immutable ANNUAL_INFLATION_RATE = 2 * 10**4; // 2% annual inflation

    uint256 private epochEndTime;

    modifier onlyStakingProxy {
        _assertCallerIsStakingProxy();
        _;
    }

    constructor(
        address _rigoTokenAddress,
        address _stakingProxyAddress
    ) {
        RIGO_TOKEN_ADDRESS = _rigoTokenAddress;
        STAKING_PROXY_ADDRESS = _stakingProxyAddress;
    }

    /*
     * CORE FUNCTIONS
     */
    /// @dev Allows staking proxy to mint rewards.
    /// @return mintedInflation Number of allocated tokens.
    function mintInflation()
        external
        override
        onlyStakingProxy
        returns (uint256 mintedInflation)
    {
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp < epochEndTime) {
            revert("NOT_ENOUGH_TIME_ERROR");
        }

        (uint256 epochDurationInSeconds, , , , ) = IStaking(STAKING_PROXY_ADDRESS).getParams();

        // sanity check for epoch length queried from staking
        if (epochLength != epochDurationInSeconds) {
            if (epochDurationInSeconds < 5 days || epochDurationInSeconds > 90 days) {
                revert("STAKING_EPOCH_TIME_ANOMALY_DETECTED_ERROR");
            } else {
                epochLength = epochDurationInSeconds;
            }
        }

        uint256 epochInflation = getEpochInflation();

        // solhint-disable-next-line not-rely-on-time
        epochEndTime = block.timestamp + epochLength;
        slot = safeAdd(slot, 1);

        // mint rewards
        RigoTokenFace(RIGO_TOKEN_ADDRESS).mintToken(
            STAKING_PROXY_ADDRESS,
            epochInflation
        );
        return (mintedInflation = epochInflation);
    }

    /*
     * CONSTANT PUBLIC FUNCTIONS
     */
    /// @dev Returns whether an epoch has ended.
    /// @return Bool the epoch has ended.
    function epochEnded()
        external
        override
        view
        returns (bool)
    {
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp >= epochEndTime) {
            return true;
        } else return false;
    }

    /// @dev Returns how long until next claim.
    /// @return Number in seconds.
    function timeUntilNextClaim()
        external
        view
        override
        returns (uint256)
    {
        /* solhint-disable not-rely-on-time */
        if (block.timestamp < epochEndTime) {
            return (epochEndTime - block.timestamp);
        } else return (uint256(0));
        /* solhint-disable not-rely-on-time */
    }

    /// @dev Returns the epoch inflation.
    /// @return Value of units of GRG minted in an epoch.
    function getEpochInflation()
        public
        view
        override
        returns (uint256)
    {
        uint256 epochInflation = 
            safeDiv(
                safeDiv(
                    safeMul(
                        RigoTokenFace(RIGO_TOKEN_ADDRESS).totalSupply(),
                        safeMul(
                            ANNUAL_INFLATION_RATE,
                            epochLength
                        )
                    ),
                    PPM_DENOMINATOR
                ),
                365 days
            );

        return epochInflation;
    }

    /*
     * INTERNAL METHODS
     */
    /// @dev Asserts that the caller is the Staking Proxy.
    function _assertCallerIsStakingProxy()
        internal
        view
    {
        if (msg.sender != STAKING_PROXY_ADDRESS) {
            revert("CALLER_NOT_STAKING_PROXY_ERROR");
        }
    }
}
// solhint-disable-next-line
function getEpochInflation()
    public
    view
    override
    returns (uint256)
{
    uint256 epochInflation = 
        safeDiv(
            safeDiv(
                safeMul(
                    RigoTokenFace(RIGO_TOKEN_ADDRESS).totalSupply(),
                    safeMul(
                        ANNUAL_INFLATION_RATE,
                        epochLength
                    )
                ),
                PPM_DENOMINATOR
            ),
            365 days
        );

    return epochInflation;
}

/// @dev Returns the epoch inflation.
/// @return Value of units of GRG minted in an epoch.