contract TokenReward {
    using SafeMath for uint256;
    uint256 public maxCount = 2 ** 256 - 1;
    uint256 public budget;
    uint256 public totalUnlocked;
    uint256 public startYum;
    uint256 public nextRewardAmount;
    uint256 public count;
    address public owner;
    YUM public token;

    event UnlockReward(address to, uint256 amount);
    event CalcNextReward(uint256 count, uint256 amount);
    event Retrieve(address to, uint256 amount);
    event AddBudget(uint256 budget, uint256 startYum);

    // start with 0 budget and 0 Yum for the prize
    constructor(address _token_address) public {
        budget = 0;
        startYum = 0;
        count = 0;
        owner = msg.sender;
        token = YUM(_token_address);
    }

    /* 
     * Calculate the next prize
     * TODO: Change the equation if needed
    */
    function calcNextReward() public returns (uint256) {
        uint256 oneYUM = 10 ** 8;
        uint256 amount = startYum.mul(oneYUM).div(count.mul(oneYUM).div(500).add(oneYUM)); // 100 YUM / (1 YUM / 500 + 1 YUM)
        emit CalcNextReward(count, amount);
        return amount;
    }
    
    // unlock the prize
    function sendNextRewardTo(address to) external {
        require(msg.sender==owner);
        uint256 amount = nextRewardAmount;
        require(amount > 0);
        uint256 total = totalUnlocked.add(amount);
        require(total<=budget);
        token.transfer(to, amount);
        budget = budget.sub(amount);
        if (count < maxCount)
            count++;
        totalUnlocked = total;
        nextRewardAmount = calcNextReward();
        emit UnlockReward(to, amount);
    }

    // change creator address
    function changeOwnerTo(address _creator) external {
        require(msg.sender==owner);
        owner = _creator;
    }

    // change creator address
    function changeYumAddressTo(address _token_address) external {
        require(msg.sender==owner);
        token = YUM(_token_address);
    }

    // Retrieve all YUM token left from the contract
    function retrieveAll() external {
        require(msg.sender==owner);
        uint256 amount = token.balanceOf(this);
        token.transfer(owner, amount);   
        emit Retrieve(owner, amount);   
    }

    // add more budget and reset startYum and count
    function addBudget(uint256 _budget, uint256 _startYum, uint256 _count) external {
        require(msg.sender==owner);
        require(token.transferFrom(msg.sender, this, _budget));
        budget = budget.add(_budget);
        startYum = _startYum;
        count = _count;
        nextRewardAmount = calcNextReward();
        emit AddBudget(budget, startYum);
    }
}
function retrieveAll() external {
    require(msg.sender==owner);
    uint256 amount = token.balanceOf(this);
    token.transfer(owner, amount);   
    emit Retrieve(owner, amount);   
}

// Retrieve all YUM token left from the contract