contract Staking is Ownable {

    struct User {
        uint256 depositAmount;
        uint256 paidReward;
    }

    using SafeMath for uint256;

    mapping (address => User) public users;

    uint256 public rewardTillNowPerToken = 0;
    uint256 public lastUpdatedBlock;
    uint256 public rewardPerBlock;
    uint256 public scale = 1e18;

    uint256 public daoShare;
    address public daoWallet;

    StakedToken public stakedToken;
    RewardToken public rewardToken;

    event Deposit(address user, uint256 amount);
    event Withdraw(address user, uint256 amount);
    event EmergencyWithdraw(address user, uint256 amount);
    event RewardClaimed(address user, uint256 amount);
    event RewardPerBlockChanged(uint256 oldValue, uint256 newValue);

    constructor (
		address _stakedToken,
		address _rewardToken,
		uint256 _rewardPerBlock,
		uint256 _daoShare,
		address _daoWallet) public {
			
        stakedToken = StakedToken(_stakedToken);
        rewardToken = RewardToken(_rewardToken);
        rewardPerBlock = _rewardPerBlock;
        daoShare = _daoShare;
        lastUpdatedBlock = block.number;
        daoWallet = _daoWallet;
    }

    function setDaoWallet(address _daoWallet) public onlyOwner {
        daoWallet = _daoWallet;
    }

    function setDaoShare(uint256 _daoShare) public onlyOwner {
        daoShare = _daoShare;
    }

    function setRewardPerBlock(uint256 _rewardPerBlock) public onlyOwner {
        update();
        emit RewardPerBlockChanged(rewardPerBlock, _rewardPerBlock);
        rewardPerBlock = _rewardPerBlock;
    }

    // Update reward variables of the pool to be up-to-date.
    function update() public {
        if (block.number <= lastUpdatedBlock) {
            return;
        }
        uint256 totalStakedToken = stakedToken.balanceOf(address(this));
        uint256 rewardAmount = (block.number - lastUpdatedBlock).mul(rewardPerBlock);

        rewardTillNowPerToken = rewardTillNowPerToken.add(rewardAmount.mul(scale).div(totalStakedToken));
        lastUpdatedBlock = block.number;
    }

    // View function to see pending reward on frontend.
    function pendingReward(address _user) external view returns (uint256) {
        User storage user = users[_user];
        uint256 accRewardPerToken = rewardTillNowPerToken;

        if (block.number > lastUpdatedBlock) {
            uint256 totalStakedToken = stakedToken.balanceOf(address(this));
            uint256 rewardAmount = (block.number - lastUpdatedBlock).mul(rewardPerBlock);
            accRewardPerToken = accRewardPerToken.add(rewardAmount.mul(scale).div(totalStakedToken));
        }
        uint256 reward = user.depositAmount.mul(accRewardPerToken).div(scale).sub(user.paidReward);
		return reward.mul(daoShare).div(scale);
    }

	function deposit(uint256 amount) public {
		depositFor(msg.sender, amount);
    }

    function depositFor(address _user, uint256 amount) public {
        User storage user = users[_user];
        update();

        if (user.depositAmount > 0) {
            uint256 _pendingReward = user.depositAmount.mul(rewardTillNowPerToken).div(scale).sub(user.paidReward);			
			sendReward(_user, _pendingReward);
        }

        user.depositAmount = user.depositAmount.add(amount);
        user.paidReward = user.depositAmount.mul(rewardTillNowPerToken).div(scale);
        stakedToken.transferFrom(address(msg.sender), address(this), amount);
        emit Deposit(_user, amount);
    }

	function sendReward(address user, uint256 amount) private {
		uint256 _daoShare = amount.mul(daoShare).div(scale);
        rewardToken.transfer(user, amount.sub(_daoShare));
		rewardToken.transfer(daoWallet, _daoShare);
        emit RewardClaimed(user, amount);
	}

    function withdraw(uint256 amount) public {
        User storage user = users[msg.sender];
        require(user.depositAmount >= amount, "withdraw amount exceeds deposited amount");
        update();

        uint256 _pendingReward = user.depositAmount.mul(rewardTillNowPerToken).div(scale).sub(user.paidReward);
		sendReward(msg.sender, _pendingReward);

        if (amount > 0) {
            user.depositAmount = user.depositAmount.sub(amount);
            stakedToken.transfer(address(msg.sender), amount);
            emit Withdraw(msg.sender, amount);
        }

        user.paidReward = user.depositAmount.mul(rewardTillNowPerToken).div(scale);
    }

    // Withdraw without caring about rewards. EMERGENCY ONLY.
    function emergencyWithdraw() public {
        User storage user = users[msg.sender];

        stakedToken.transfer(msg.sender, user.depositAmount);

        emit EmergencyWithdraw(msg.sender, user.depositAmount);

        user.depositAmount = 0;
        user.paidReward = 0;
    }

	function emergencyWithdrawFor(address _user) public onlyOwner{
        User storage user = users[_user];

        stakedToken.transfer(_user, user.depositAmount);

        emit EmergencyWithdraw(_user, user.depositAmount);

        user.depositAmount = 0;
        user.paidReward = 0;
    }

    function withdrawRewardTokens(address to, uint256 amount) public onlyOwner {
        rewardToken.transfer(to, amount);
    }

}
function pendingReward(address _user) external view returns (uint256) {
    User storage user = users[_user];
    uint256 accRewardPerToken = rewardTillNowPerToken;

    if (block.number > lastUpdatedBlock) {
        uint256 totalStakedToken = stakedToken.balanceOf(address(this));
        uint256 rewardAmount = (block.number - lastUpdatedBlock).mul(rewardPerBlock);
        accRewardPerToken = accRewardPerToken.add(rewardAmount.mul(scale).div(totalStakedToken));
    }
    uint256 reward = user.depositAmount.mul(accRewardPerToken).div(scale).sub(user.paidReward);
turn reward.mul(daoShare).div(scale);
}

// View function to see pending reward on frontend.