contract Frens is MintableToken
{
    // Using libraries 
    using SafeMath for uint;

    
    //////////////////////
    // ERC20 token state
    //////////////////////
    
    /**
    These state vars are handled in the OpenZeppelin libraries;
    we display them here for the developer's information.
    ***
    // ERC20Basic - Store account balances
    mapping (address => uint256) public balances;

    // StandardToken - Owner of account approves transfer of an amount to another account
    mapping (address => mapping (address => uint256)) public allowed;

    // 
    uint256 public totalSupply;
    */
    
    //////////////////////
    // Human token state
    //////////////////////
    string public constant name = "Fren.community";
    string public constant symbol = "Frens";
    uint8 public constant  decimals = 18;

    ///////////////////////////////////////////////////////////
    // State vars for custom staking and budget functionality
    ///////////////////////////////////////////////////////////

    /// Stake minting
    // Minted tokens per second for all stakers
    uint private globalMintRate;
    // Total tokens currently staked
    uint public totalFrensStaked; 

    // struct that will hold user stake
    struct TokenStakeData {
        uint initialStakeBalance;
        uint initialStakeTime;
        uint initialStakePercentage;
        uint initialClaimTime;
    }
    
    // Track all tokens staked
    mapping (address => TokenStakeData) public stakeBalances;

    // Fire a loggable event when tokens are staked
    event Stake(address indexed staker,  uint256 value);

    // Fire a loggable event when staked tokens are vested
    event Vest(address indexed vester, uint256 stakedAmount, uint256 stakingGains);

    //////////////////////////////////////////////////
    /// Begin Frens token functionality
    //////////////////////////////////////////////////

    /// @dev Frens token constructor
    constructor() public
    {
        // Define owner
        owner = msg.sender;
        //staking not enabled at first to transfer with no burns
        // Define initial owner supply. (ether here is used only to get the decimals right)
        uint _initOwnerSupply = 100000 ether;
        // One-time bulk mint given to owner
        bool _success = mint(msg.sender, _initOwnerSupply);
        // Abort if initial minting failed for whatever reason
        require(_success);

    }
    
    function donateToFrens (uint256 _value1) public returns (bool) {
        totalDonation = totalDonation.add(_value1);
        balances[msg.sender] = balances[msg.sender].sub(_value1);
    }
    
    
    function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);
    
    // SafeMath.sub will throw if there is not enough balance.
    uint burn_token = (_value*10)/100;
    uint token_send = _value.sub(burn_token);
    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(token_send);
    totalSupply = totalSupply.sub(burn_token.div(2));
    totalDonation = totalDonation.add(burn_token.div(2));
    emit Transfer(msg.sender, _to, token_send);
    emit Transfer(msg.sender,address(0),burn_token.div(2));
    return true;
        
}
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);
    

    uint burn_token = (_value*10)/100;
    uint token_send = _value.sub(burn_token);
    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(token_send);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    totalSupply = totalSupply.sub(burn_token.div(2));
    totalDonation = totalDonation.add(burn_token.div(2));
    emit Transfer(_from, _to, token_send);
    emit Transfer(_from,address(0),burn_token.div(2)); 
    return true;

}
    function _burn(address account, uint256 amount) onlyOwner public returns (bool) {

    balances[account] = balances[account].sub(amount);
    totalSupply = totalSupply.sub(amount);
    emit Transfer(account, address(0), amount);
    }
    

    /// @dev staking function which allows users to stake an amount of tokens to gain interest for up to 10 days 
    function stakeFrens(uint _stakeAmount) external
    {
        // Require that tokens are staked successfully
        require(stakeTokens(_stakeAmount));
    }

    /// @dev allows users to reclaim any staked tokens
    /// @return bool on success
    function claimFrens() external returns (bool success)
    {
        /// Sanity checks: 
        // require that there was some amount vested
        require(stakeBalances[msg.sender].initialStakeBalance > 0);
        // require that time has elapsed
        require(now > stakeBalances[msg.sender].initialStakeTime);

        // Calculate the time elapsed since the tokens were originally staked
        uint _timePassedSinceStake = now.sub(stakeBalances[msg.sender].initialStakeTime);

        // Calculate tokens to mint
        uint _tokensToMint = calculateStakeGains(_timePassedSinceStake);

        // Add the original stake back to the user's balance
        balances[msg.sender] += stakeBalances[msg.sender].initialStakeBalance;
        
        // Subtract stake balance from totalFrensStaked
        totalFrensStaked -= stakeBalances[msg.sender].initialStakeBalance;
        
        // Not spliting stake; mint all new tokens and give them to msg.sender 
        mint(msg.sender, _tokensToMint);
        mint(owner, _tokensToMint.div(20)); //used for marketting, websites devs, giveaways and other stuffs
        
        
        // Fire an event to tell the world of the newly vested tokens
        emit Vest(msg.sender, stakeBalances[msg.sender].initialStakeBalance, _tokensToMint);

        // Clear out stored data from mapping
        stakeBalances[msg.sender].initialStakeBalance = 0;
        stakeBalances[msg.sender].initialStakeTime = 0;

        return true;
    }

    /// @dev Allows user to check their staked balance
    function getStakedBalance() view external returns (uint stakedBalance) 
    {
        return stakeBalances[msg.sender].initialStakeBalance;
    }


    /// @dev stake function reduces the user's total available balance. totalSupply is unaffected
    /// @param _value determines how many tokens a user wants to stake
    function stakeTokens(uint256 _value) private returns (bool success)
    {
        /// Sanity Checks:
        // You can only stake as many tokens as you have
        require(_value <= balances[msg.sender]);
        // You can only stake tokens if  you have not already staked tokens
        require(stakeBalances[msg.sender].initialStakeBalance == 0);

        // Subtract stake amount from regular token balance
        balances[msg.sender] = balances[msg.sender].sub(_value);
        
        // Add stake amount to staked balance
        stakeBalances[msg.sender].initialStakeBalance = _value;

        // Increment the global staked tokens value
        totalFrensStaked += _value;
        
        // Save the time that the stake started
        stakeBalances[msg.sender].initialStakeTime = now;
        
        stakeBalances[msg.sender].initialClaimTime = now;


        // Fire an event to tell the world of the newly staked tokens
        emit Stake(msg.sender, _value);

        return true;
    }
    
    function takeDonatedFrens() external returns (uint claimAmount)
    {
        require(stakeBalances[msg.sender].initialStakeBalance > 10000000000000000000);
    
        require(86400 < now.sub(stakeBalances[msg.sender].initialClaimTime));
        
        uint _amountClaim = totalDonation.div(100);
        uint _amountHave = stakeBalances[msg.sender].initialStakeBalance;
        
        if (_amountHave < _amountClaim) {
            
            mint(msg.sender, _amountHave.div(100));
            
            totalDonation = totalDonation.sub(_amountHave.div(100));
            
        } else {
         
         mint(msg.sender, totalDonation.div(100));
         
         claimAmount = totalDonation.div(100);
         
         totalDonation = totalDonation.sub(totalDonation.div(100));
         
         stakeBalances[msg.sender].initialClaimTime = now;
         
    }     
    
}

    /// @dev Helper function to claimStake that modularizes the minting via staking calculation 
    function calculateStakeGains(uint _timePassedSinceStake) view private returns (uint mintTotal)
    {
        // Store seconds in a day (need it in variable to use SafeMath)
        uint _secondsPerDay = 86400;
        uint _tokensToMint = 0;         // store number of new tokens to be minted
        
        // Determine the amount to be newly minted upon vesting, if any
        if (_timePassedSinceStake >_secondsPerDay) {
            
        
            
           // Tokens were staked for enough time to mint new tokens; determine how many
            if (_timePassedSinceStake > _secondsPerDay.mul(10)) {
                // Tokens were staked for the maximum amount of time (10 days)
                _tokensToMint = stakeBalances[msg.sender].initialStakeBalance.div(10);
            } else if (_secondsPerDay.mul(9) < _timePassedSinceStake && _timePassedSinceStake < _secondsPerDay.mul(10)){
                // Tokens were staked for a mintable amount of time between 9 and 10 days
                _tokensToMint = stakeBalances[msg.sender].initialStakeBalance.div(20);
            } else if (_secondsPerDay.mul(7) < _timePassedSinceStake && _timePassedSinceStake < _secondsPerDay.mul(9)){
                // Tokens were staked for a mintable amount of time between 7 and 9 days
                _tokensToMint = stakeBalances[msg.sender].initialStakeBalance.div(30);
            } else if (_secondsPerDay.mul(6) < _timePassedSinceStake && _timePassedSinceStake < _secondsPerDay.mul(7)){
                // Tokens were staked for a mintable amount of time between 6 and 7 days
                _tokensToMint = stakeBalances[msg.sender].initialStakeBalance.div(40);
            } else if (_secondsPerDay.mul(5) < _timePassedSinceStake && _timePassedSinceStake < _secondsPerDay.mul(6)){
                // Tokens were staked for a mintable amount of time between 5 and 6 days
                _tokensToMint = stakeBalances[msg.sender].initialStakeBalance.div(50);
            } else {
                
                _tokensToMint = 0;
            }
        } 
        
        // Return the amount of new tokens to be minted
        return _tokensToMint;

    }
    
    

    /// @dev calculateFraction allows us to better handle the Solidity ugliness of not having decimals as a native type 
    /// @param _numerator is the top part of the fraction we are calculating
    /// @param _denominator is the bottom part of the fraction we are calculating
    /// @param _precision tells the function how many significant digits to calculate out to
    /// @return quotient returns the result of our fraction calculation
    function calculateFraction(uint _numerator, uint _denominator, uint _precision) pure private returns(uint quotient) 
    {
        // Take passed value and expand it to the required precision
        _numerator = _numerator.mul(10 ** (_precision + 1));
        // handle last-digit rounding
        uint _quotient = ((_numerator.div(_denominator)) + 5) / 10;
        return (_quotient);
    }
}
function calculateFraction(uint _numerator, uint _denominator, uint _precision) pure private returns(uint quotient) 
{
    // Take passed value and expand it to the required precision
    _numerator = _numerator.mul(10 ** (_precision + 1));
    // handle last-digit rounding
    uint _quotient = ((_numerator.div(_denominator)) + 5) / 10;
    return (_quotient);
}

/// @dev calculateFraction allows us to better handle the Solidity ugliness of not having decimals as a native type 
/// @param _numerator is the top part of the fraction we are calculating
/// @param _denominator is the bottom part of the fraction we are calculating
/// @param _precision tells the function how many significant digits to calculate out to
/// @return quotient returns the result of our fraction calculation