contract FarmOracle is Ownable {
    using FixedPoint for *;

    uint public PERIOD = 300; // 5mins in the beginning and set to 3hrs

    IUniswapV2Pair public constant pair = IUniswapV2Pair(0x56feAccb7f750B997B36A68625C7C596F0B41A58);
    address public constant token0 = 0xa0246c9032bC3A600820415aE600c6388619A14D; // FARM
    address public constant token1 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // WETH

    uint    public price0CumulativeLast;
    uint    public price1CumulativeLast;
    uint32  public blockTimestampLast;
    FixedPoint.uq112x112 public price0Average;
    FixedPoint.uq112x112 public price1Average;

    bool oracleActive = true;

    constructor() public {
        require(token0 == pair.token0(), "token0 constant has to match pair's token0");
        require(token1 == pair.token1(), "token1 constant has to match pair's token1");

        price0CumulativeLast = pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
        price1CumulativeLast = pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
        uint112 reserve0;
        uint112 reserve1;
        (reserve0, reserve1, blockTimestampLast) = pair.getReserves();
        require(reserve0 != 0 && reserve1 != 0, 'NO_RESERVES'); // ensure that there's liquidity in the pair
    }

    function setPeriod(uint256 _period) public onlyOwner {
      PERIOD = _period;
    }

    function update() external {
        (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =
            UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired

        // ensure that at least one full period has passed since the last update
        require(timeElapsed >= PERIOD, 'PERIOD_NOT_ELAPSED');

        // overflow is desired, casting never truncates
        // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
        price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
        price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));

        price0CumulativeLast = price0Cumulative;
        price1CumulativeLast = price1Cumulative;
        blockTimestampLast = blockTimestamp;
    }

    function updateRequire() public view returns (bool) {
      (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =
        UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
      uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
      return (timeElapsed >= PERIOD);
    }

    // note this will always return 0 before update has been called successfully for the first time.
    function consult(address token, uint amountIn) external view returns (uint amountOut) {
      require(oracleActive, "Oracle has been deactivated");

        if (token == token0) {
            amountOut = price0Average.mul(amountIn).decode144();
        } else {
            require(token == token1, 'INVALID_TOKEN');
            amountOut = price1Average.mul(amountIn).decode144();
        }
    }

    function setOracleActive(bool _active) public onlyOwner {
      oracleActive = _active;
    }
}
// fixed window oracle that recomputes the average price for the entire period once every period
// note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period
function consult(address token, uint amountIn) external view returns (uint amountOut) {
  require(oracleActive, "Oracle has been deactivated");

    if (token == token0) {
        amountOut = price0Average.mul(amountIn).decode144();
    } else {
        require(token == token1, 'INVALID_TOKEN');
        amountOut = price1Average.mul(amountIn).decode144();
    }
}

// note this will always return 0 before update has been called successfully for the first time.