contract U1ProxyUpgradablity is U1ProxyUpgradeAdmin {
  /// @notice SafeMath using for arithmatic operations
  using SafeMath for uint256;

  /// @notice DAYS stand 86400 seconds
  uint256 public constant DAYS = 1 days;

  /// @notice u1UpgradeTotalUnstaking to update totalUnStaking in U1Upgrade;
  mapping(address => uint256) public u1UpgradeTotalUnStaking;

  /// @notice u1UnstakeStatus return true or false basis of you active staking.
  mapping(address => mapping(uint256 => bool)) public u1UnstakeStatus;

  /// @notice event IntervalDaysDetails emit on every update of Intervaldays.
  event IntervalDaysDetails(uint256[] updatedIntervals, uint256 time);

  event Claim(
    address indexed userAddress,
    address indexed stakedTokenAddress,
    address indexed tokenAddress,
    uint256 claimRewards,
    uint256 time
  );

  event UnStake(
    address indexed userAddress,
    address indexed unStakedtokenAddress,
    uint256 unStakedAmount,
    uint256 time,
    uint256 stakeID
  );

  function init(address[] memory tokenAddress)
    external
    onlyOwner
    returns (bool)
  {
    for (uint256 i = 0; i < tokenAddress.length; i++) {
      safeTransfer(tokenAddress[i]);
    }

    return true;
  }

  function safeTransfer(address tokenAddress) internal {
    uint256 bal = IERC20(tokenAddress).balanceOf(address(u1Proxy));
    if (bal > 0) u1Proxy.safeWithdraw(tokenAddress, bal);
  }

  function updateIntervalDays(uint256[] memory _interval) external onlyOwner {
    intervalDays = new uint256[](0);

    for (uint8 i = 0; i < _interval.length; i++) {
      uint256 noD = u1.stakeDuration().div(DAYS);
      require(noD > _interval[i], "Invalid Interval Day");
      intervalDays.push(_interval[i]);
    }
    emit IntervalDaysDetails(intervalDays, block.timestamp);
  }

  function transferV1ProxyOwnership(address newOwner) external onlyOwner {
    u1Proxy.transferOwnership(newOwner);
  }

  /**
   * @notice Get rewards for one day
   * @param stakedAmount Stake amount of the user
   * @param stakedToken Staked token address of the user
   * @param rewardToken Reward token address
   * @param totalStake Reward token address
   * @return reward One dayh reward for the user
   */

  function getOneDayReward(
    uint256 stakedAmount,
    address stakedToken,
    address rewardToken,
    uint256 totalStake
  ) public view returns (uint256 reward) {
    reward = (
      stakedAmount.mul(tokenDailyDistribution[stakedToken][rewardToken])
    )
    .div(totalStake);
    return reward;
  }

  /**
   * @notice send rewards
   * @param stakedToken Stake amount of the user
   * @param tokenAddress Reward token address
   * @param amount Amount to be transferred as reward
   */

  function sendToken(
    address user,
    address stakedToken,
    address tokenAddress,
    uint256 amount
  ) internal {
    // Checks

    if (tokenAddress != address(0)) {
      require(
        IERC20(tokenAddress).balanceOf(address(this)) >= amount,
        "SEND : Insufficient Balance"
      );
      // Transfer of rewards
      require(IERC20(tokenAddress).transfer(user, amount), "Transfer failed");
      // Emit state changes
      emit Claim(user, stakedToken, tokenAddress, amount, block.timestamp);
    }
  }

  function totalStaking(address tokenAddress) public view returns (uint256) {
    uint256 actualUnStaking = u1UpgradeTotalUnStaking[tokenAddress]
    .add(u1Proxy.totalUnStakingB(tokenAddress))
    .add(u1Proxy.totalUnStakingA(tokenAddress));
    return u1.totalStaking(tokenAddress).sub(actualUnStaking);
  }

  /**
   * @notice Unstake and claim rewards
   * @param stakeId Stake ID of the user
   */
  function unStake(address user, uint256 stakeId) external whenNotPaused {
    require(
      msg.sender == user || msg.sender == _owner,
      "UNSTAKE: Invalid User Entry"
    );

    (
      address[] memory tokenAddress,
      bool[] memory activeStatus,
      ,
      uint256[] memory stakedAmount,
      uint256[] memory startTime
    ) = (u1.viewStakingDetails(user));

    bool isAlreadyUnstaked = u1Proxy.unstakeStatus(user, stakeId);

    // Checks
    if (
      u1UnstakeStatus[user][stakeId] == false &&
      activeStatus[stakeId] == true &&
      isAlreadyUnstaked == false
    ) u1UnstakeStatus[user][stakeId] = true;
    else revert("UNSTAKE : Unstaked Already");

    // State updation
    uint256 actualStaking = totalStaking(tokenAddress[stakeId]);

    // increase total unstaking
    u1UpgradeTotalUnStaking[tokenAddress[stakeId]] = u1UpgradeTotalUnStaking[
      tokenAddress[stakeId]
    ]
    .add(stakedAmount[stakeId]);

    // Balance check
    require(
      IERC20(tokenAddress[stakeId]).balanceOf(address(this)) >=
        stakedAmount[stakeId],
      "UNSTAKE : Insufficient Balance"
    );

    IERC20(tokenAddress[stakeId]).transfer(user, stakedAmount[stakeId]);

    if (startTime[stakeId] < poolStartTime.add(u1.stakeDuration())) {
      claimRewards(
        user,
        startTime[stakeId],
        stakedAmount[stakeId],
        tokenAddress[stakeId],
        actualStaking
      );
    }

    // emit state changes
    emit UnStake(
      user,
      tokenAddress[stakeId],
      stakedAmount[stakeId],
      block.timestamp,
      stakeId
    );
  }

  function claimRewards(
    address user,
    uint256 stakeTime,
    uint256 stakedAmount,
    address stakedToken,
    uint256 totalStake
  ) internal {
    // Local variables
    uint256 interval;
    uint256 endOfProfit;

    interval = poolStartTime.add(u1.stakeDuration());

    if (interval > block.timestamp) endOfProfit = block.timestamp;
    else endOfProfit = interval;

    interval = endOfProfit.sub(stakeTime);
    // Reward calculation

    if (interval >= DAYS)
      _rewardCalculation(user, stakedAmount, interval, stakedToken, totalStake);
  }

  function _rewardCalculation(
    address user,
    uint256 stakedAmount,
    uint256 interval,
    address stakedToken,
    uint256 totalStake
  ) internal {
    uint256 rewardsEarned;
    uint256 noOfDays;

    noOfDays = interval.div(DAYS);
    rewardsEarned = noOfDays.mul(
      getOneDayReward(stakedAmount, stakedToken, stakedToken, totalStake)
    );

    //  send reward
    sendToken(user, stakedToken, stakedToken, rewardsEarned);

    uint8 i = 1;
    while (i < intervalDays.length) {
      if (noOfDays >= intervalDays[i]) {
        uint256 balDays = noOfDays.sub((intervalDays[i].sub(1)));

        address rewardToken = tokensSequenceList[stakedToken][i];

        if (
          rewardToken != stakedToken &&
          u1.tokenBlockedStatus(stakedToken, rewardToken) == false
        ) {
          rewardsEarned = balDays.mul(
            getOneDayReward(stakedAmount, stakedToken, rewardToken, totalStake)
          );
          // send reward
          sendToken(user, stakedToken, rewardToken, rewardsEarned);
        }
        i = i + 1;
      } else {
        break;
      }
    }
  }

  function emergencyUnstake(
    uint256 stakeId,
    address userAddress,
    address[] memory rewardtokens,
    uint256[] memory amount
  ) external onlyOwner {
    (
      address[] memory tokenAddress,
      bool[] memory activeStatus,
      ,
      uint256[] memory stakedAmount,

    ) = (u1.viewStakingDetails(userAddress));

    bool isAlreadyUnstaked = u1Proxy.unstakeStatus(userAddress, stakeId);

    if (
      u1UnstakeStatus[userAddress][stakeId] == false &&
      isAlreadyUnstaked == false &&
      activeStatus[stakeId] == true
    ) u1UnstakeStatus[userAddress][stakeId] = true;
    else revert("EMERGENCY: Unstaked Already");

    // Balance check
    require(
      IERC20(tokenAddress[stakeId]).balanceOf(address(this)) >=
        stakedAmount[stakeId],
      "EMERGENCY : Insufficient Balance"
    );

    IERC20(tokenAddress[stakeId]).transfer(userAddress, stakedAmount[stakeId]);

    for (uint256 i; i < rewardtokens.length; i++) {
      require(
        IERC20(rewardtokens[i]).balanceOf(address(this)) >= amount[i],
        "EMERGENCY : Insufficient Reward Balance"
      );
      sendToken(userAddress, tokenAddress[stakeId], rewardtokens[i], amount[i]);
    }

    u1UpgradeTotalUnStaking[tokenAddress[stakeId]] = u1UpgradeTotalUnStaking[
      tokenAddress[stakeId]
    ]
    .add(stakedAmount[stakeId]);

    // Emit state changes
    emit UnStake(
      userAddress,
      tokenAddress[stakeId],
      stakeId,
      stakedAmount[stakeId],
      block.timestamp
    );
  }

  function lockContract(bool pauseStatus) external onlyOwner {
    if (pauseStatus == true) _pause();
    else if (pauseStatus == false) _unpause();
  }
}
function getOneDayReward(
    uint256 stakedAmount,
    address stakedToken,
    address rewardToken,
    uint256 totalStake
  ) public view returns (uint256 reward) {
    reward = (
      stakedAmount.mul(tokenDailyDistribution[stakedToken][rewardToken])
    )
    .div(totalStake);
    return reward;
  }
/**
 * @notice Get rewards for one day
 * @param stakedAmount Stake amount of the user
 * @param stakedToken Staked token address of the user
 * @param rewardToken Reward token address
 * @param totalStake Reward token address
 * @return reward One dayh reward for the user
 */