contract HoldersDay {
    using SafeMath for uint256;
    
    PampToken token;
    address owner;
    Minter public minter;
    StakingContract public stakingContract;
    uint adjustmentFactor;
    
    uint32 public currentHoldersDayRewardedVersion;
    
    bool public enableHoldersDay;                     // once a month, holders receive a nice bump. This is true for 24 hours, once a month only.
    
    mapping (bytes32 => bool) public holdersDayRewarded; // Mapping to test whether an individual received his Holder's Day reward
    
    event HoldersDayEnabled();
    
    event HoldersDayRewarded(uint Amount);
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    constructor() public {
        owner = msg.sender;
        token = PampToken(0xF0FAC7104aAC544e4a7CE1A55ADF2B5a25c65bD1);
        stakingContract = StakingContract(0x738d3CEC4E685A2546Ab6C3B055fd6B8C1198093);
        minter = Minter(0x28e484dBD6BB501D37EFC8cD4b8dc33121cC78be);
        adjustmentFactor = 600;
    }
    
    // This function can be called once a month, when holder's day is enabled
    function claimHoldersDay() external {
        
        require(!getHoldersDayRewarded(msg.sender), "You've already claimed Holder's Day");
        require(enableHoldersDay, "Holder's Day is not enabled");

        (uint startTimestamp, uint lastTimestamp, bool hasMigrated) = stakingContract.getStaker(msg.sender);
        uint daysStaked = block.timestamp.sub(startTimestamp) / 86400;  // Calculate time staked in days
        require(daysStaked >= 30, "You must stake for 30 days to claim holder's day rewards");
        if (enableHoldersDay && daysStaked >= 30) {
            setHoldersDayRewarded(msg.sender);
            uint balance = token.balanceOf(msg.sender);
            uint numTokens = mulDiv(balance, daysStaked, adjustmentFactor);   // Once a month, holders get a nice bump
            uint tenPercent = mulDiv(balance, 1, 10);
        
            if (numTokens > tenPercent) {       // We don't allow a daily reward of greater than ten percent of a holder's balance.
                numTokens = tenPercent;
            }
            
            minter.liquidityRewards(msg.sender, numTokens);
            emit HoldersDayRewarded(numTokens);
        }
        
    }

    function getHoldersDayRewarded(address holder) internal view returns(bool) {
        bytes32 key = keccak256(abi.encodePacked(currentHoldersDayRewardedVersion, holder));
        return holdersDayRewarded[key];
    }

    function setHoldersDayRewarded(address holder) internal {
        bytes32 key = keccak256(abi.encodePacked(currentHoldersDayRewardedVersion, holder));
        holdersDayRewarded[key] = true;
    }

    function deleteHoldersDayRewarded() internal {
        currentHoldersDayRewardedVersion++;
    }
        
    function updateHoldersDay(bool _enableHoldersDay) external onlyOwner {
        enableHoldersDay = _enableHoldersDay;
        if(enableHoldersDay) {
            deleteHoldersDayRewarded();
            emit HoldersDayEnabled();
        }
    }
    
    function updateAdjustmentFactor(uint _adjustmentFactor) external onlyOwner {
        adjustmentFactor = _adjustmentFactor;
    }
    
    function mulDiv (uint x, uint y, uint z) public pure returns (uint) {
          (uint l, uint h) = fullMul (x, y);
          assert (h < z);
          uint mm = mulmod (x, y, z);
          if (mm > l) h -= 1;
          l -= mm;
          uint pow2 = z & -z;
          z /= pow2;
          l /= pow2;
          l += h * ((-pow2) / pow2 + 1);
          uint r = 1;
          r *= 2 - z * r;
          r *= 2 - z * r;
          r *= 2 - z * r;
          r *= 2 - z * r;
          r *= 2 - z * r;
          r *= 2 - z * r;
          r *= 2 - z * r;
          r *= 2 - z * r;
          return l * r;
    }
    
    function fullMul (uint x, uint y) private pure returns (uint l, uint h) {
          uint mm = mulmod (x, y, uint (-1));
          l = x * y;
          h = mm - l;
          if (mm < l) h -= 1;
    }
}
function claimHoldersDay() external {
        
    require(!getHoldersDayRewarded(msg.sender), "You've already claimed Holder's Day");
    require(enableHoldersDay, "Holder's Day is not enabled");

    (uint startTimestamp, uint lastTimestamp, bool hasMigrated) = stakingContract.getStaker(msg.sender);
    uint daysStaked = block.timestamp.sub(startTimestamp) / 86400;  // Calculate time staked in days
    require(daysStaked >= 30, "You must stake for 30 days to claim holder's day rewards");
    if (enableHoldersDay && daysStaked >= 30) {
        setHoldersDayRewarded(msg.sender);
        uint balance = token.balanceOf(msg.sender);
        uint numTokens = mulDiv(balance, daysStaked, adjustmentFactor);   // Once a month, holders get a nice bump
        uint tenPercent = mulDiv(balance, 1, 10);
        
        if (numTokens > tenPercent) {       // We don't allow a daily reward of greater than ten percent of a holder's balance.
            numTokens = tenPercent;
        }
            
        minter.liquidityRewards(msg.sender, numTokens);
        emit HoldersDayRewarded(numTokens);
    }
        
}

// This function can be called once a month, when holder's day is enabled