contract Bamboo is ERC20("Bamboo Token", "$BAMBOO"), Ownable {
	using SafeMath for uint256;
    
    uint256 public claimUnitPerPeriod = 10;
    uint256 public claimPeriod = 1 days;
	uint256 public claimEndTime = now + 365 * 10 days;
    
	mapping(uint256 => uint256) public lastClaimedTime;
	mapping(uint256 => address) public lastClaimer;
	mapping(address => bool) public locked;
	

	PandaNFT public  PandaNFTContract;

	event ClaimedReward(address indexed user, uint256 tokenId, uint256 reward);
    
    
	constructor(address _panda_contract_address) public{
		PandaNFTContract = PandaNFT(_panda_contract_address);
	}
    
    function setPandaContractAddress(address _panda_contract_address) public onlyOwner{
        PandaNFTContract = PandaNFT(_panda_contract_address);
    }
    
    function setClaimEndTime(uint256 _time) public onlyOwner{
        claimEndTime = _time;    
    }
    
    function setClaimPeriod(uint256 _period) public onlyOwner{
        claimPeriod = _period;
    }
    
    function setClaimUnitPerPeriod(uint256 _amount) public onlyOwner {
        claimUnitPerPeriod = _amount;
    }
    
    function _checkHolder(address _holder) internal view returns (bool) {
        if (PandaNFTContract.balanceOf(_holder) > 0)
            return true;
        else 
            return false;
    }
    
    // function _checkOwnerOfToken(address _holder, uint256 _tokenId) internal view returns (bool) {
    //     if (PandaNFTContract.ownerOf(_tokenId) == _holder)
    //         return true;
    //     else 
    //         return false;
    // }
    
    function _checkClaimTime(uint256 _tokenId) internal view returns (bool) {
        if (lastClaimedTime[_tokenId] + claimPeriod <= now ) {
            return true;
        } else {
            return false;
        }        
    }
    
    function _getAccmulatedDays(uint256 _tokenId, address _holder) internal view returns (uint256) {
         if (lastClaimer[_tokenId] == _holder ) {
             return now.sub(lastClaimedTime[_tokenId]).div(claimPeriod);  
         } else {
             return 1;
         } 
    }
    
    function _claimReward(uint256 _tokenId, address _holder) internal {
        if (_checkClaimTime(_tokenId)){
            uint256 howManyDaysAccumulated = _getAccmulatedDays(_tokenId, _holder);
            uint256 claimAmount = howManyDaysAccumulated * claimUnitPerPeriod * (uint(10)**decimals());
            _mint(_holder, claimAmount);
            lastClaimedTime[_tokenId] = now;
            lastClaimer[_tokenId] = _holder;
            emit ClaimedReward(_holder, _tokenId, claimAmount);
        }
    }
    
    function _lock(address _holder) internal {
       locked[_holder] = true;   
    }
    
    function _unlock(address _holder) internal {
        locked[_holder] = false;  
    }
    
    function _isLocked(address _holder) internal view returns(bool) {
        if (locked[_holder] == true) return true;
        else return false;
    }
    
    function claim() external {
       require(now <= claimEndTime, "Claim period is ended!");    
       require(_checkHolder(msg.sender), "You have not a holder");    
       require(_isLocked(msg.sender) == false, "Risky For Rentrance attack!");    
       _lock(msg.sender);
       uint256 tokenCount = PandaNFTContract.balanceOf(msg.sender);
       for (uint256 i=0; i < tokenCount; i++){
           uint256 tokenId = PandaNFTContract.tokenOfOwnerByIndex(msg.sender, i);
           _claimReward(tokenId, msg.sender);
       }
       _unlock(msg.sender);
    }
    
    function _calcClaimableAmount(address _holder, uint256 _tokenId) internal view returns(uint256){
         if (_checkClaimTime(_tokenId)){
            uint256 howManyDaysAccumulated = _getAccmulatedDays(_tokenId, _holder);
            uint256 claimAmount = howManyDaysAccumulated * claimUnitPerPeriod * (uint(10)**decimals());
            return claimAmount;
        } else return 0;
    }
    
    function getHolderClaimableAmountForTokenId(address _holder, uint256 _tokenId) public view returns(uint256) {
		if (now > claimEndTime) return 0;
		if (!_checkHolder(_holder)) return 0;
        uint amount = _calcClaimableAmount(_holder, _tokenId);
        return amount; 
	}
	
    function getHolderClaimableTotalAmount(address _holder) public view returns(uint256) {
		if (now > claimEndTime) return 0;
		if (!_checkHolder(_holder)) return 0;
		uint256 tokenCount = PandaNFTContract.balanceOf(_holder);
		uint256 claimableAmount = 0; 
        for (uint256 i=0; i < tokenCount; i++){
           uint256 tokenId = PandaNFTContract.tokenOfOwnerByIndex(_holder, i);
           uint amount = _calcClaimableAmount(_holder, tokenId);
           claimableAmount = claimableAmount.add(amount);
        }
        return claimableAmount; 
	}
	
    function mint(address _to, uint256 _amount) public onlyOwner {
		_mint(_to, _amount);
	}
	
    function burn(address _from, uint256 _amount) public onlyOwner {
		_burn(_from, _amount);
	}
}
function _checkClaimTime(uint256 _tokenId) internal view returns (bool) {
    if (lastClaimedTime[_tokenId] + claimPeriod <= now ) {
        return true;
    } else {
        return false;
    }        
}

// function _checkOwnerOfToken(address _holder, uint256 _tokenId) internal view returns (bool) {
//     if (PandaNFTContract.ownerOf(_tokenId) == _holder)
//         return true;
//     else 
//         return false;
// }