contract StakingTokenV2 is IStakingToken, ReentrancyGuard {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    struct stakingInfo {
        uint256 amount;
        uint256 releaseDate;
    }
    /* ========== STATE VARIABLES ========== */

    address public owner;
    IERC20 public stakingToken;
    uint256 public stakingDuration;
    uint256 private _totalSupply;
    uint256 private _totalRewardsRequired;
    mapping(address => stakingInfo) private _balances;
    uint256 private basePct;

    /* ========== CONSTRUCTOR ========== */

    constructor(
        address _stakingToken,
        uint256 _stakingDuration,
        uint256 _baseApy,
        address _owner
    ) {
        require(_stakingDuration > 0, 'stakingDuration=0');
        stakingToken = IERC20(_stakingToken);
        stakingDuration = _stakingDuration;
        owner = _owner;
        basePct = _baseApy * 100;
    }

    modifier onlyOwner() {
        require(owner == msg.sender, 'Ownable: caller is not the owner');
        _;
    }

    /* ========== VIEWS ========== */

    function totalSupply() external view override returns (uint256) {
        return _totalSupply;
    }

    function totalRewardsRequired() external view returns (uint256) {
        return _totalRewardsRequired;
    }

    function amountToContractRequired() external view returns (uint256) {
        uint256 balance = stakingToken.balanceOf(address(this));
        return (_totalRewardsRequired > 0 && _totalRewardsRequired > balance.sub(_totalSupply)) ? _totalRewardsRequired.sub(balance.sub(_totalSupply)) : 0;
    }

    function balanceOf(address account) external view override returns (uint256) {
        return _balances[account].amount;
    }

    function canWithdraw(address account) external view override returns (bool) {
        return _balances[account].amount > 0;
    }

    function canStake(address account) external view override returns (bool) {
        return _balances[account].releaseDate == 0;
    }

    function canExtend(address account) external view override returns (bool) {
        return _balances[account].amount > 0 && block.timestamp > _balances[account].releaseDate;
    }

    function endStakingTime(address account) external view override returns (uint256) {
        return _balances[account].releaseDate;
    }

    function stake(uint256 amount) external override nonReentrant {
        require(amount > 0, 'Cannot stake 0.');
        require(_balances[msg.sender].releaseDate == 0, 'Already staked.');
        require(_balances[msg.sender].amount == 0, 'Already  staked.');
        _balances[msg.sender].amount = amount;
        _balances[msg.sender].releaseDate = block.timestamp + stakingDuration;
        _totalSupply = _totalSupply.add(amount);
        _totalRewardsRequired = _totalRewardsRequired.add(_getRewardsAmount(msg.sender));
        stakingToken.safeTransferFrom(msg.sender, address(this), amount);
        emit Staked(msg.sender, amount);
    }

    function _getRewardsAmount(address addr) private view returns (uint256) {
        uint256 amount = _balances[addr].amount;
        uint256 amountCoef = 100;
        uint256 individualAPY = basePct.mul(amountCoef).mul(1 ether);
        return ((amount.mul(individualAPY).div(365 days)).mul(stakingDuration)).div(1000000 * 1 ether);
    }

    function extend() public override nonReentrant {
        require(_balances[msg.sender].amount > 0, 'Cannot extend 0');
        require(block.timestamp > _balances[msg.sender].releaseDate);
        _balances[msg.sender].releaseDate = block.timestamp + stakingDuration;
        uint256 rewardAmount = _getRewardsAmount(msg.sender);

        _balances[msg.sender].amount = _balances[msg.sender].amount.add(rewardAmount);
        //increasing _totalRewardsRequired instead of _totalSupply as there is no additional transfer to the contract address
        _totalRewardsRequired = _totalRewardsRequired.add(rewardAmount);

        emit Extended(msg.sender);
    }

    function withdraw() public override nonReentrant {
        require(_balances[msg.sender].amount > 0, 'Cannot withdraw 0');
        uint256 individualAPYReward = 0;
        uint256 rewardAmount = _getRewardsAmount(msg.sender);
        if(block.timestamp > _balances[msg.sender].releaseDate)
        {
            individualAPYReward = rewardAmount;
        }

        uint256 amount = _balances[msg.sender].amount;

        _totalSupply = _totalSupply.sub(amount);
        _totalRewardsRequired = _totalRewardsRequired.sub(rewardAmount);
        _balances[msg.sender].amount = 0;
        _balances[msg.sender].releaseDate = 0;
        stakingToken.safeTransfer(msg.sender, amount.add(individualAPYReward));
        emit Withdrawn(msg.sender, amount, individualAPYReward);
    }

    function emergencyWithdraw(address _token) public override onlyOwner nonReentrant {
        IERC20(_token).transfer(msg.sender, IERC20(_token).balanceOf(address(this)));
    }

    /* ========== EVENTS ========== */
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount, uint256 reward);
    event Extended(address indexed user);
}
// Dependency file: contracts\StakingTokenV2.sol
// pragma solidity ^0.8.4;
// import 'C:\Projects\VRM.FarmingStaking.Contracts\node_modules\@openzeppelin\contracts\utils\math\Math.sol';
// import 'C:\Projects\VRM.FarmingStaking.Contracts\node_modules\@openzeppelin\contracts\utils\math\SafeMath.sol';
// import 'C:\Projects\VRM.FarmingStaking.Contracts\node_modules\@openzeppelin\contracts\token\ERC20\IERC20.sol';
// import 'C:\Projects\VRM.FarmingStaking.Contracts\node_modules\@openzeppelin\contracts\token\ERC20\extensions\IERC20Metadata.sol';
// import 'C:\Projects\VRM.FarmingStaking.Contracts\node_modules\@openzeppelin\contracts\token\ERC20\utils\SafeERC20.sol';
// import 'C:\Projects\VRM.FarmingStaking.Contracts\node_modules\@openzeppelin\contracts\security\ReentrancyGuard.sol';
// Inheritance
// import 'contracts\interfaces\IStakingToken.sol';
function totalSupply() external view override returns (uint256) {
    return _totalSupply;
}

/* ========== VIEWS ========== */