contract PixelcolorsStakingContract is IERC721Receiver, Ownable{ 
   
    IERC721 public NFTContract;
    RewardToken public rewardToken;
          

    mapping(uint256 => address) public stakedaddress;
    mapping(uint256 => uint256) public depositTime; 
    mapping(uint256 => uint256) public lastrewardClaim;
    mapping(uint256 => uint256) public rewardsEarned;
    mapping(address => uint256[]) public tokenIdsStaked;
    mapping(uint256 => uint256) public tokenIndex;


    uint256 public tokenRewardsPerMinute;

    event Staked(address owner, uint256 amount);

    constructor(){
        tokenRewardsPerMinute=2e17;
        NFTContract = IERC721(0x71Cc8b0ce35BeE7467f7162BA201965BB10DFE68);
        rewardToken = RewardToken(0xEd22DCA00B4d71F08bc184574b028c4Dc53479DE);
    }


    function stake(uint256 _tokenId) external {
     _stake(_tokenId,msg.sender);
    }

    function stakeMultiple(uint256[] memory _ids) external {
        address _userAddress = msg.sender;
        for (uint i=0; i<_ids.length; i++){
             _stake(_ids[i],_userAddress);
        }  
    }


    function totalAccumulatedRewards(address _userAddress) external view returns(uint256){
         uint totalRewards;
         uint[] memory _ids =getStakedTokenIDs(_userAddress);
         for (uint i=0; i<_ids.length; i++){
              totalRewards+=getAccumulatedrewardPerTokenStaked(_ids[i]);
         }
        return totalRewards;
    }
 
    
    function getAccumulatedrewardPerTokenStaked(uint256 _tokenId) public view returns(uint256) {
        uint256 rewards = 0;
        
        if (stakedaddress[_tokenId] != address(0)) {
    
        uint256 minStaked = (block.timestamp - depositTime[_tokenId]) / 60;
        
         uint256 minuteslastClaimTime;  
          if (lastrewardClaim[_tokenId] !=0) {
            minuteslastClaimTime = (block.timestamp - lastrewardClaim[_tokenId]) / 60; // 27382745.2167    
          }
          else {
            minuteslastClaimTime = minStaked;    
          }
        
        rewards = (minuteslastClaimTime * tokenRewardsPerMinute);
        if(_tokenId<=45){
            rewards=(rewards*15)/10;
        }
       } 

        return rewards;
    }  

    function getStakedTokenIDs(address _userAddress) public view returns(uint[] memory){
      return  tokenIdsStaked[_userAddress];
    }


    function claim(uint256 _tokenId) external {
      _claim(_tokenId);
    }

    function totalClaim(uint256[] memory _tokenIds) external {
        for(uint256 i=0;i<_tokenIds.length;i++){
            _claim(_tokenIds[i]);
        }
    }

    function unstake(uint256 _tokenId) external {
        _unstake(_tokenId,msg.sender);
    }

    function unstakeMultiple(uint256[] memory _ids) external {
        address _user = msg.sender;
        for (uint i=0; i<_ids.length; i++){
             _unstake(_ids[i],_user);
        }
    }

    function _unstake(uint256 _tokenId,address _userAddress) internal {

        require(stakedaddress[_tokenId] == _userAddress, "user does not stake this id");
        uint getClaimAmnt = getAccumulatedrewardPerTokenStaked(_tokenId);
         
         if (getClaimAmnt > 0) { 
           rewardToken.mint(stakedaddress[_tokenId],getClaimAmnt);
         }  
         
         delete lastrewardClaim[_tokenId];
         delete rewardsEarned[_tokenId];
         delete stakedaddress[_tokenId];
         delete depositTime[_tokenId];  

         uint[] storage arr = tokenIdsStaked[_userAddress];
         uint index = tokenIndex[_tokenId];
         arr[index]=arr[arr.length-1];
         tokenIndex[arr[arr.length-1]]=index;
         arr.pop();
         tokenIdsStaked[_userAddress]=arr;

         NFTContract.safeTransferFrom(
            address(this),
            _userAddress,
            _tokenId
        );
    }



     function _stake(uint256 _tokenId, address _userAddress) internal {
     
     require(NFTContract.ownerOf(_tokenId) == _userAddress, "Sender is not the owner of the tkn"); 
      stakedaddress[_tokenId] = _userAddress;
      depositTime[_tokenId] = block.timestamp;

      uint[] storage arr = tokenIdsStaked[_userAddress];
      uint len = arr.length;
      tokenIndex[_tokenId]=len;
      arr.push(_tokenId);
      tokenIdsStaked[_userAddress]=arr;
                  
        NFTContract.safeTransferFrom(
            _userAddress,
            address(this),
            _tokenId
        );
        emit Staked(_userAddress, _tokenId);
    }

     function _claim(uint256 _tokenId) internal {
      
      require(stakedaddress[_tokenId] != address(0), "Id not staked");
      uint getClaimAmnt = getAccumulatedrewardPerTokenStaked(_tokenId);

      if (getClaimAmnt > 0) {
         
         lastrewardClaim[_tokenId] = block.timestamp;
         rewardsEarned[_tokenId] += getClaimAmnt;
         rewardToken.mint(stakedaddress[_tokenId],getClaimAmnt);
     }  
    }

    //Admin Functions

    function updateTokenRewardPerMinutes(uint _newRewardPerMinute) external onlyOwner{
        tokenRewardsPerMinute=_newRewardPerMinute;
    }


    function updateRewardToken(address _newRewardToken) external onlyOwner{
        rewardToken=RewardToken(_newRewardToken);
    }

    function updateNFTContract(address _newNFTContract) external onlyOwner{
        NFTContract = IERC721(_newNFTContract);
    }
      
    function onERC721Received(address, address, uint, bytes calldata) public pure override returns (bytes4) {
        return 0x150b7a02;
    }


}
function updateTokenRewardPerMinutes(uint _newRewardPerMinute) external onlyOwner{
    tokenRewardsPerMinute=_newRewardPerMinute;
}

//Admin Functions