contract AmpleforthConsumer is ChainlinkClient, Ownable {
  bytes32 public id;
  uint256 public payment;
  int256 public currentAnswer;
  uint256 public updatedHeight;
  AmpleforthInterface public ample;
  mapping(address => bool) public authorizedRequesters;

  /**
   * @notice Deploy the contract with a specified address for the LINK
   * and Oracle contract addresses
   * @dev Sets the storage for the specified addresses
   * @param _link The address of the LINK token contract
   * @param _oracle The Oracle contract address to send the request to
   * @param _ample The Ampleforth contract to call
   * @param _id The bytes32 JobID to be executed
   * @param _payment The payment to send to the oracle
   */
  constructor(
    address _link,
    address _oracle,
    address _ample,
    bytes32 _id,
    uint256 _payment
  ) public {
    if (_link == address(0)) {
      setPublicChainlinkToken();
    } else {
      setChainlinkToken(_link);
    }
    _updateRequestDetails(_ample, _oracle, _id, _payment);
  }

  function updateRequestDetails(
    address _ample,
    address _oracle,
    bytes32 _id,
    uint256 _payment
  )
    external
    onlyOwner()
  {
    _updateRequestDetails(_ample, _oracle, _id, _payment);
  }

  function _updateRequestDetails(
    address _ample,
    address _oracle,
    bytes32 _id,
    uint256 _payment
  ) private {
    require(_ample != address(0) && _oracle != address(0), "Cannot use zero address");
    require(!authorizedRequesters[_oracle], "Requester cannot be oracle");
    setChainlinkOracle(_oracle);
    id = _id;
    payment = _payment;
    ample = AmpleforthInterface(_ample);
  }

  /*
   * @notice Creates a request to the stored Oracle contract address
   */
  function requestPushReport()
    external
    ensureAuthorizedRequester()
    returns (bytes32 requestId)
  {
    Chainlink.Request memory req = buildChainlinkRequest(id, this, this.fulfillPushReport.selector);
    requestId = sendChainlinkRequest(req, payment);
  }

  /**
   * @notice Calls the Ampleforth contract's pushReport method with the response
   * from the oracle
   * @param _requestId The ID that was generated for the request
   * @param _data The answer provided by the oracle
   */
  function fulfillPushReport(bytes32 _requestId, int256 _data)
    external
    recordChainlinkFulfillment(_requestId)
  {
    currentAnswer = _data;
    updatedHeight = block.number;
    AmpleforthInterface(ample).pushReport(uint256(_data));
  }

  /**
   * @notice Called by the owner to permission other addresses to generate new
   * requests to oracles.
   * @param _requester the address whose permissions are being set
   * @param _allowed boolean that determines whether the requester is
   * permissioned or not
   */
  function setAuthorization(address _requester, bool _allowed)
    public
    onlyOwner()
  {
    require(_requester != getChainlinkOracle(), "Requester cannot be oracle");
    authorizedRequesters[_requester] = _allowed;
  }

  /**
   * @notice Returns the address of the LINK token
   * @dev This is the public implementation for chainlinkTokenAddress, which is
   * an internal method of the ChainlinkClient contract
   */
  function getChainlinkToken() public view returns (address) {
    return chainlinkTokenAddress();
  }

  /**
   * @notice Returns the address of the stored oracle contract address
   */
  function getChainlinkOracle() public view returns (address) {
    return chainlinkOracleAddress();
  }

  /**
   * @notice Allows the owner to withdraw any LINK balance on the contract
   */
  function withdrawLink() public onlyOwner() {
    LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
    require(link.transfer(msg.sender, link.balanceOf(address(this))), "Unable to transfer");
  }

  /**
   * @notice Call this method if no response is received within 5 minutes
   * @param _requestId The ID that was generated for the request to cancel
   * @param _payment The payment specified for the request to cancel
   * @param _callbackFunctionId The bytes4 callback function ID specified for
   * the request to cancel
   * @param _expiration The expiration generated for the request to cancel
   */
  function cancelRequest(
    bytes32 _requestId,
    uint256 _payment,
    bytes4 _callbackFunctionId,
    uint256 _expiration
  )
    public
    onlyOwner()
  {
    cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration);
  }

  /**
   * @dev Reverts if `msg.sender` is not authorized to make requests.
   */
  modifier ensureAuthorizedRequester() {
    require(authorizedRequesters[msg.sender] || msg.sender == owner, "Unauthorized to create requests");
    _;
  }
}
/**
 * @title AmpleforthConsumer is a contract which requests data from
 * the Chainlink network
 * @dev This contract is designed to work on multiple networks, including
 * local test networks
 */
function requestPushReport()
  external
  ensureAuthorizedRequester()
  returns (bytes32 requestId)
{
  Chainlink.Request memory req = buildChainlinkRequest(id, this, this.fulfillPushReport.selector);
  requestId = sendChainlinkRequest(req, payment);
}

/*
 * @notice Creates a request to the stored Oracle contract address
 */