contract TokenFarm {
    using SafeMath for uint256;
	modifier onlyOwner {
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        _;
    }
	modifier onlyModerators {
        require(
            Admins[msg.sender]==true,
            "Only owner can call this function."
        );
        _;
    }

    // These get stored on the blockchain
    // Owner is the person who deployes the contract
    address public owner;
    string public name = "Dapp Token Farm";
    IERC20Mintable public dappToken;

    
    struct staker {
        uint256 id;
        mapping(address=> uint256) balance;
        mapping(address=> uint256) timestamp;
        mapping(address=> uint256) timefeestartstamp; 

    }

    struct rate {
        uint256 rate;
        bool exists;
    }

    // people that have ever staked
    mapping(address => uint256) public totalStaked;
    address[] private tokenPools; 
    mapping(address => staker) stakers;
    mapping(address => rate) public RatePerCoin;
	mapping (address=>bool) Admins;
    uint256 public minimumDaysLockup=3;
    uint256 public penaltyFee=10;
    // This is passing in addresses, I can find them manually
    constructor(IERC20Mintable _dapptoken, address _spiritclashtoken)
        public
    {
        dappToken = _dapptoken;
        owner = msg.sender;
        Admins[msg.sender]=true;
		setCoinRate(_spiritclashtoken,80000);
    }

    // 1. Stakes Tokens(Deposit)
    function stakeTokens(uint256 _amount,IERC20 token) external { // remove payable
        // Require amount greater than 0
		require(RatePerCoin[address(token)].exists==true,"token doesnt have a rate");
        require(_amount > 0, "amount cannot be 0");
        if (stakers[msg.sender].balance[address(token)] > 0) {
            claimToken(address(token));
        }

        // Transfer Mock Dai tokens to this contract for staking
        token.transferFrom(msg.sender, address(this), _amount);

        // Update staking balance
        stakers[msg.sender].balance[address(token)] = stakers[msg.sender].balance[address(token)].add( _amount);
        totalStaked[address(token)] = totalStaked[address(token)].add( _amount);
        stakers[msg.sender].timestamp[address(token)] = block.timestamp;
        stakers[msg.sender].timefeestartstamp[address(token)] = block.timestamp;
    }

    //  Unstaking Tokens(Withdraw)

    function unstakeToken(IERC20 token) external {
        // Fetch staking balance
        uint256 balance = stakers[msg.sender].balance[address(token)];

        // Require amount greater then 0
        require(balance > 0, "staking balance cannot be 0");
        // Check to see if the sender has waited longer then 3 days before withdrawl
        if( (block.timestamp.sub(stakers[msg.sender].timefeestartstamp[address(token)])) < (minimumDaysLockup*24*60*60)){  
            uint256 fee = (balance.mul(100).div(penaltyFee)).div(100);
            token.transfer(owner, fee);
            balance = balance.sub(fee); 
        }
        claimTokens();

        // Reset staking balance
        stakers[msg.sender].balance[address(token)] = 0;
        totalStaked[address(token)] = totalStaked[address(token)].sub(balance);
        //transfer unstaked coins
        token.transfer(msg.sender, balance);
    }

    function unstakeTokens() external{ // this is fine
        
        claimTokens();
        for (uint i=0; i< tokenPools.length; i++){
            uint256 balance = stakers[msg.sender].balance[tokenPools[i]];
            if(balance > 0){
                // Check to see if the sender has waited longer then 3 days before withdrawl
                totalStaked[tokenPools[i]] = totalStaked[tokenPools[i]].sub(balance);
                stakers[msg.sender].balance[tokenPools[i]] = 0;
                if((block.timestamp.sub(stakers[msg.sender].timefeestartstamp[tokenPools[i]])) < (minimumDaysLockup*24*60*60)){
                    uint256 fee = (balance.mul(100).div(penaltyFee)).div(100);
                    balance = balance.sub(fee);
                    IERC20(tokenPools[i]).transfer(owner, fee);
                }
                IERC20(tokenPools[i]).transfer(msg.sender, balance);
            }
        }
    }

	function earned(address token) public view returns(uint256){ // this is fine
        uint256 multiplier =100000000;
		return (stakers[msg.sender].balance[token]* 
            (RatePerCoin[token].rate) * //coin earn rate in percentage so should be divided by 100
                ( 
                    (stakers[msg.sender].balance[token]*multiplier)/(totalStaked[token]) //calculate token share percentage
                )/(365*24*60*60)// 31 536 000
                ///seconds per year
            *(
                block.timestamp.sub(stakers[msg.sender].timestamp[token]) // get time
                
            )
        )/multiplier/10;
	}


    function claimTokens() public { // This function looks good to me.
        uint256 rewardbal=0;
		for (uint i=0; i< tokenPools.length; i++){
            address token = tokenPools[i];
            if(stakers[msg.sender].balance[token]>0){
                uint256 earnings = earned(token);
                stakers[msg.sender].timestamp[token]=block.timestamp;
                rewardbal= rewardbal.add(earnings);
            }
        }
        IERC20Mintable(dappToken).mint(msg.sender, rewardbal);
    }
    function claimToken(address token) public { // For sure an issue
        require(stakers[msg.sender].balance[token]>0,"you have no balance and cant claim");
        uint256 earnings = earned(token);
        stakers[msg.sender].timestamp[token]=block.timestamp;
        IERC20Mintable(dappToken).mint(msg.sender, earnings);
    }
    
    function setMinimumLockup(uint256 _days) external onlyModerators {
        minimumDaysLockup =_days;
    }
    
    function setPenaltyFee(uint256 _fee) external onlyModerators {
        penaltyFee =_fee;
    }

    function transferOwnership(address _newOwner) external onlyOwner{
        owner=_newOwner;
    }

    function setCoinRate(address coin,uint256 Rate) public onlyModerators {
        RatePerCoin[coin].rate =Rate;
        if(RatePerCoin[coin].exists == false){
            tokenPools.push(coin);
            RatePerCoin[coin].exists = true;
        }
    }

	function setAdmin(address addy,bool value) external onlyOwner{
		Admins[addy]= value;
	}
    function stakingBalance(address token) external view returns(uint256) {
        return stakers[msg.sender].balance[token];
    }
}
function unstakeToken(IERC20 token) external {
    // Fetch staking balance
    uint256 balance = stakers[msg.sender].balance[address(token)];

    // Require amount greater then 0
    require(balance > 0, "staking balance cannot be 0");
    // Check to see if the sender has waited longer then 3 days before withdrawl
    if( (block.timestamp.sub(stakers[msg.sender].timefeestartstamp[address(token)])) < (minimumDaysLockup*24*60*60)){  
        uint256 fee = (balance.mul(100).div(penaltyFee)).div(100);
        token.transfer(owner, fee);
        balance = balance.sub(fee); 
    }
    claimTokens();

    // Reset staking balance
    stakers[msg.sender].balance[address(token)] = 0;
    totalStaked[address(token)] = totalStaked[address(token)].sub(balance);
    //transfer unstaked coins
    token.transfer(msg.sender, balance);
}

//  Unstaking Tokens(Withdraw)