contract KeeperRebase is KeeperCompatibleInterface, ChainlinkClient, Ownable {

    using Chainlink for Chainlink.Request;

    uint256 public FEE = 1 * 10 ** 18;
    bytes32 public JOBID = bytes32("2bde90044c644ac192cc015c9274d870");
    address public constant LINK = 0x514910771AF9Ca656af840dff83E8264EcF986CA;
    address public constant ORACLE = 0x049Bd8C3adC3fE7d3Fc2a44541d955A537c2A484;
    Benchmark public constant BENCHMARK = Benchmark(0x67c597624B17b16fb77959217360B7cD18284253);
    Sync public SYNC = Sync(0x4C3aA5160aE34210CC5B783Cd642e4bAACF34b40);

    uint256 public keeperInterval;
    uint256 public lastRebase;
    int256 public latestPrice;
    int256 public targetPrice;
    bool public rebaseActive;

    event UpkeepPerformed();
    event RebasePerformed(uint256 supplyDelta, bool increaseSupply);

    constructor() {
        setChainlinkToken(LINK);
        setChainlinkOracle(ORACLE);
        lastRebase = block.timestamp;
    }
    
/******************************************/
/*       Administration starts here       */
/******************************************/

    function setJobId(bytes32 _jobId) external onlyOwner {
        JOBID = _jobId;
    } 
    
    function setFee(uint256 _fee) external onlyOwner {
        FEE = _fee;
    } 

    function setKeeperInterval(uint256 _interval) external onlyOwner {
        keeperInterval = _interval;
    } 

    function setTargetPrice(int256 _targetPrice) external onlyOwner {
        targetPrice = _targetPrice;
    } 

    function setSyncContract(address _sync) external onlyOwner {
        SYNC = Sync(_sync);
    }

    function setRebaseActive(bool _active) external onlyOwner {
        rebaseActive = _active;
    }
    
/******************************************/
/*       KeeperNetwork starts here        */
/******************************************/

    function checkUpkeep(bytes calldata checkData) external override returns (bool upkeepNeeded, bytes memory performData) {
        upkeepNeeded = (block.timestamp - lastRebase) > keeperInterval;
    }

    function performUpkeep(bytes calldata performData) external override {
        require((block.timestamp - lastRebase) > keeperInterval, "Too soon.");
        lastRebase = block.timestamp;
        initiateRebase();

        emit UpkeepPerformed();
    }

/******************************************/
/*        MARK Oracle starts here         */
/******************************************/

    function abs(int x) private pure returns (int) {
        return x >= 0 ? x : -x;
    }

    function initiatePublicRebase() external {
        IERC20(LINK).transferFrom(msg.sender, address(this), FEE);
        initiateRebase();
    }
    
    function initiateRebase() internal {
        sendChainlinkRequest(buildChainlinkRequest(JOBID, address(this), this.rebaseCallback.selector), FEE);
    }

    function rebaseCallback(bytes32 _requestId, uint256 _price) external {
        validateChainlinkCallback(_requestId);

        latestPrice = int(_price);
        int256 rebasePercentage = (latestPrice - targetPrice) * 1e18 / targetPrice;
        uint256 absolutePercentage = uint256(abs(rebasePercentage));
        uint256 supplyDelta = BENCHMARK.totalSupply() * absolutePercentage / 1e18;
        bool increaseSupply = rebasePercentage >= 0 ? true : false;
       
        if (rebaseActive == true) {
        BENCHMARK.rebase(supplyDelta, increaseSupply);
        SYNC.syncPools();
        }

        emit RebasePerformed(supplyDelta, increaseSupply);
    }

}
/******************************************/
function setJobId(bytes32 _jobId) external onlyOwner {
    JOBID = _jobId;
} 

/******************************************/