contract MonetaryPolicy is Ownable {
    using SafeMath for uint256;
    using SafeMathInt for int256;
    using UInt256Lib for uint256;

    event LogRebase(
        uint256 indexed epoch,
        uint256 exchangeRate,
        uint256 cpi,
        int256 requestedSupplyAdjustment,
        uint256 timestampSec
    );

    IRebased public rebased;

    // Provides the current CPI as a 20 decimal fixed point number.
    IOracle public cpiOracle;

    // Market oracle provides the token/USD exchange rate as an 18 decimal fixed point number.
    IOracle public marketOracle;

    // CPI value at the time of launch, as an 18 decimal fixed point number.
    uint256 private baseCpi;

    // If the current exchange rate is within this fractional distance from the target, no supply
    // update is performed. Fixed point number--same format as the rate.
    // (ie) abs(rate - targetRate) / targetRate < deviationThreshold, then no supply change.
    // DECIMALS Fixed point number.
    uint256 public deviationThreshold;

    // The rebase lag parameter, used to dampen the applied supply adjustment by 1 / rebaseLag
    // Check setRebaseLag comments for more details.
    // Natural number, no decimal places.
    uint256 public rebaseLag;

    // More than this much time must pass between rebase operations.
    uint256 public minRebaseTimeIntervalSec;

    // Block timestamp of last rebase operation
    uint256 public lastRebaseTimestampSec;

    // The number of rebase cycles since inception
    uint256 public epoch;

    uint256 private constant DECIMALS = 18;

    // Due to the expression in computeSupplyDelta(), MAX_RATE * MAX_SUPPLY must fit into an int256.
    // Both are 18 decimals fixed point numbers.
    uint256 private constant MAX_RATE = 10**6 * 10**DECIMALS;
    // MAX_SUPPLY = MAX_INT256 / MAX_RATE
    uint256 private constant MAX_SUPPLY = ~(uint256(1) << 255) / MAX_RATE;

    constructor(uint256 baseCpi_) public {
        deviationThreshold = 5 * 10 ** (DECIMALS-2);

        rebaseLag = 20;
        minRebaseTimeIntervalSec = 12 hours;
        lastRebaseTimestampSec = 0;
        epoch = 0;
        
        baseCpi = baseCpi_;
    }

    /**
     * @notice Returns true if at least minRebaseTimeIntervalSec seconds have passed since last rebase.
     *
     */
     
    function canRebase() public view returns (bool) {
        
        return (lastRebaseTimestampSec.add(minRebaseTimeIntervalSec) < now);
    }

    /**
     * @notice Initiates a new rebase operation, provided the minimum time period has elapsed.
     *
     */
     
    function rebase() external {

        require(canRebase(), "Insufficient time has passed since last rebase");
        lastRebaseTimestampSec = now;

        epoch = epoch.add(1);
        
        (uint256 cpi, uint256 exchangeRate, uint256 targetRate, int256 supplyDelta) = getRebaseValues();

        uint256 supplyAfterRebase = rebased.rebase(epoch, supplyDelta);
        assert(supplyAfterRebase <= MAX_SUPPLY);
        
        emit LogRebase(epoch, exchangeRate, cpi, supplyDelta, now);
    }
    
    /**
     * @notice Calculates the supplyDelta and returns the current set of values for the rebase
     *
     * @dev The supply adjustment equals (_totalSupply * DeviationFromTargetRate) / rebaseLag
     *      Where DeviationFromTargetRate is (MarketOracleRate - targetRate) / targetRate
     *      and targetRate is CpiOracleRate / baseCpi
     * 
     */    
    
    function getRebaseValues() public view returns (uint256, uint256, uint256, int256) {
        uint256 cpi;
        bool cpiValid;
        (cpi, cpiValid) = cpiOracle.getData();
        
        require(cpiValid);

        uint256 targetRate = cpi.mul(10 ** DECIMALS).div(baseCpi);

        uint256 exchangeRate;
        bool rateValid;
        
        (exchangeRate, rateValid) = marketOracle.getData();
        
        require(rateValid);

        if (exchangeRate > MAX_RATE) {
            exchangeRate = MAX_RATE;
        }

        int256 supplyDelta = computeSupplyDelta(exchangeRate, targetRate);

        // Apply the Dampening factor.
        supplyDelta = supplyDelta.div(rebaseLag.toInt256Safe());

        if (supplyDelta > 0 && rebased.totalSupply().add(uint256(supplyDelta)) > MAX_SUPPLY) {
            supplyDelta = (MAX_SUPPLY.sub(rebased.totalSupply())).toInt256Safe();
        }

       return (cpi, exchangeRate, targetRate, supplyDelta);
    }


    /**
     * @return Computes the total supply adjustment in response to the exchange rate
     *         and the targetRate.
     */
    function computeSupplyDelta(uint256 rate, uint256 targetRate)
        internal
        view
        returns (int256)
    {
        if (withinDeviationThreshold(rate, targetRate)) {
            return 0;
        }

        // supplyDelta = totalSupply * (rate - targetRate) / targetRate
        int256 targetRateSigned = targetRate.toInt256Safe();
        return rebased.totalSupply().toInt256Safe()
            .mul(rate.toInt256Safe().sub(targetRateSigned))
            .div(targetRateSigned);
    }

    /**
     * @param rate The current exchange rate, an 18 decimal fixed point number.
     * @param targetRate The target exchange rate, an 18 decimal fixed point number.
     * @return If the rate is within the deviation threshold from the target rate, returns true.
     *         Otherwise, returns false.
     */
    function withinDeviationThreshold(uint256 rate, uint256 targetRate)
        internal
        view
        returns (bool)
    {
        uint256 absoluteDeviationThreshold = targetRate.mul(deviationThreshold)
            .div(10 ** DECIMALS);

        return (rate >= targetRate && rate.sub(targetRate) < absoluteDeviationThreshold)
            || (rate < targetRate && targetRate.sub(rate) < absoluteDeviationThreshold);
    }
    
    /**
     * @notice Sets the reference to the Rebased token governed.
     *         Can only be called once during initialization.
     * 
     * @param rebased_ The address of the Rebased ERC20 token.
     */
    function setRebased(IRebased rebased_)
        external
        onlyOwner
    {
        require(rebased == IRebased(0)); 
        rebased = rebased_;    
    }
    
    /**
     * @notice Sets the reference to the CPI oracle.
     * @param cpiOracle_ The address of the cpi oracle contract.
     */
    function setCpiOracle(IOracle cpiOracle_)
        external
        onlyOwner
    {
        cpiOracle = cpiOracle_;
    }

    /**
     * @notice Sets the reference to the market oracle.
     * @param marketOracle_ The address of the market oracle contract.
     */
    function setMarketOracle(IOracle marketOracle_)
        external
        onlyOwner
    {
        marketOracle = marketOracle_;
    }

}
/**
 * @title Rebased Monetary Supply Policy
 * @dev This is a simplified version of the uFragments Ideal Money protocol a.k.a. Ampleforth.
 *      uFragments operates symmetrically on expansion and contraction. It will both split and
 *      combine coins to maintain a stable unit price.
 *
 *      This component regulates the token supply of the uFragments ERC20 token in response to
 *      market oracles.
 */
function withinDeviationThreshold(uint256 rate, uint256 targetRate)
    internal
    view
    returns (bool)
{
    uint256 absoluteDeviationThreshold = targetRate.mul(deviationThreshold)
        .div(10 ** DECIMALS);

    return (rate >= targetRate && rate.sub(targetRate) < absoluteDeviationThreshold)
        || (rate < targetRate && targetRate.sub(rate) < absoluteDeviationThreshold);
}

/**
 * @param rate The current exchange rate, an 18 decimal fixed point number.
 * @param targetRate The target exchange rate, an 18 decimal fixed point number.
 * @return If the rate is within the deviation threshold from the target rate, returns true.
 *         Otherwise, returns false.
 */