contract RemeCoin is MintableToken, PausableToken, StandardBurnableToken, DetailedERC20 {
    event EnabledFees();
    event DisabledFees();
    event FeeChanged(uint256 fee);
    event FeeThresholdChanged(uint256 feeThreshold);
    event FeeBeneficiaryChanged(address indexed feeBeneficiary);
    event EnabledWhitelist();
    event DisabledWhitelist();
    event ChangedWhitelistManager(address indexed whitelistManager);
    event AddedRecipientToWhitelist(address indexed recipient);
    event AddedSenderToWhitelist(address indexed sender);
    event RemovedRecipientFromWhitelist(address indexed recipient);
    event RemovedSenderFromWhitelist(address indexed sender);

    // If the token whitelist feature is enabled or not.
    bool public whitelist = true;

    // Address of the whitelist manager.
    address public whitelistManager;

    // Addresses that can receive tokens.
    mapping(address => bool) public whitelistedRecipients;

    // Addresses that can send tokens.
    mapping(address => bool) public whitelistedSenders;

    // Fee taken from transfers.
    uint256 public fee;

    // If the fee mechanism is enabled.
    bool public feesEnabled;

    // Address of the fee beneficiary.
    address public feeBeneficiary;

    // Value from which the fee mechanism applies.
    uint256 public feeThreshold;

    /**
     * @dev Initialize the RemeCoin and transfer the initialBalance to the
     *      initialAccount.
     * @param _initialAccount The account that will receive the initial balance.
     * @param _initialBalance The initial balance of tokens.
     * @param _fee uint256 The fee percentage to be applied. Has 4 decimals.
     * @param _feeBeneficiary address The beneficiary of the fees.
     * @param _feeThreshold uint256 The amount of when the transfers fees will be applied.
     */
    constructor(
        address _initialAccount,
        uint256 _initialBalance,
        uint256 _fee,
        address _feeBeneficiary,
        uint256 _feeThreshold
    )
        DetailedERC20("REME Coin", "REME", 18)
        public
    {
        require(_fee != uint256(0) && _fee <= uint256(100 * (10 ** 4)));
        require(_feeBeneficiary != address(0));
        require(_feeThreshold != uint256(0));

        fee = _fee;
        feeBeneficiary = _feeBeneficiary;
        feeThreshold = _feeThreshold;

        totalSupply_ = _initialBalance;
        balances[_initialAccount] = _initialBalance;
        emit Transfer(address(0), _initialAccount, _initialBalance);
    }

    /**
     * @dev Throws if called by any account other than the whitelistManager.
     */
    modifier onlyWhitelistManager() {
        require(msg.sender == whitelistManager);
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract has transfer fees enabled.
     */
    modifier whenFeesEnabled() {
        require(feesEnabled);
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract has transfer fees disabled.
     */
    modifier whenFeesDisabled() {
        require(!feesEnabled);
        _;
    }

    /**
     * @dev Enable the whitelist feature.
     */
    function enableWhitelist() external onlyOwner {
        require(
            !whitelist,
            'Whitelist is already enabled'
        );

        whitelist = true;
        emit EnabledWhitelist();
    }
    
    /**
     * @dev Enable the whitelist feature.
     */
    function disableWhitelist() external onlyOwner {
        require(
            whitelist,
            'Whitelist is already disabled'
        );

        whitelist = false;
        emit DisabledWhitelist();
    }

    /**
     * @dev Change the whitelist manager address.
     * @param _whitelistManager address
     */
    function changeWhitelistManager(address _whitelistManager) external onlyOwner
    {
        require(_whitelistManager != address(0));

        whitelistManager = _whitelistManager;

        emit ChangedWhitelistManager(whitelistManager);
    }

    /**
     * @dev Add recipient to the whitelist.
     * @param _recipient address of the recipient
     */
    function addRecipientToWhitelist(address _recipient) external onlyWhitelistManager
    {
        require(
            !whitelistedRecipients[_recipient],
            'Recipient already whitelisted'
        );

        whitelistedRecipients[_recipient] = true;

        emit AddedRecipientToWhitelist(_recipient);
    }

    /**
     * @dev Add sender to the whitelist.
     * @param _sender address of the sender
     */
    function addSenderToWhitelist(address _sender) external onlyWhitelistManager
    {
        require(
            !whitelistedSenders[_sender],
            'Sender already whitelisted'
        );

        whitelistedSenders[_sender] = true;

        emit AddedSenderToWhitelist(_sender);
    }

    /**
     * @dev Remove recipient from the whitelist.
     * @param _recipient address of the recipient
     */
    function removeRecipientFromWhitelist(address _recipient) external onlyWhitelistManager
    {
        require(
            whitelistedRecipients[_recipient],
            'Recipient not whitelisted'
        );

        whitelistedRecipients[_recipient] = false;

        emit RemovedRecipientFromWhitelist(_recipient);
    }

    /**
     * @dev Remove sender from the whitelist.
     * @param _sender address of the sender
     */
    function removeSenderFromWhitelist(address _sender) external onlyWhitelistManager
    {
        require(
            whitelistedSenders[_sender],
            'Sender not whitelisted'
        );

        whitelistedSenders[_sender] = false;

        emit RemovedSenderFromWhitelist(_sender);
    }

    /**
     * @dev Called by owner to enable fee take
     */
    function enableFees()
        external
        onlyOwner
        whenFeesDisabled
    {
        feesEnabled = true;
        emit EnabledFees();
    }

    /**
     * @dev Called by owner to disable fee take
     */
    function disableFees()
        external
        onlyOwner
        whenFeesEnabled
    {
        feesEnabled = false;
        emit DisabledFees();
    }

    /**
     * @dev Called by owner to set fee percentage.
     * @param _fee uint256 The new fee percentage.
     */
    function setFee(uint256 _fee)
        external
        onlyOwner
    {
        require(_fee != uint256(0) && _fee <= 100 * (10 ** 4));
        fee = _fee;
        emit FeeChanged(fee);
    }

    /**
     * @dev Called by owner to set fee beeneficiary.
     * @param _feeBeneficiary address The new fee beneficiary.
     */
    function setFeeBeneficiary(address _feeBeneficiary)
        external
        onlyOwner
    {
        require(_feeBeneficiary != address(0));
        feeBeneficiary = _feeBeneficiary;
        emit FeeBeneficiaryChanged(feeBeneficiary);
    }

    /**
     * @dev Called by owner to set fee threshold.
     * @param _feeThreshold uint256 The new fee threshold.
     */
    function setFeeThreshold(uint256 _feeThreshold)
        external
        onlyOwner
    {
        require(_feeThreshold != uint256(0));
        feeThreshold = _feeThreshold;
        emit FeeThresholdChanged(feeThreshold);
    }

    /**
     * @dev transfer token for a specified address
     * @param _to The address to transfer to.
     * @param _value The amount to be transferred.
     */
    function transfer(address _to, uint256 _value) public returns (bool)
    {
        if (whitelist) {
            require (
                whitelistedSenders[msg.sender]
                || whitelistedRecipients[_to]
                || msg.sender == owner
                || _to == owner,
                'Sender or recipient not whitelisted'
            );
        }

        uint256 _feeTaken;

        if (msg.sender != owner && msg.sender != feeBeneficiary) {
            (_feeTaken, _value) = applyFees(_value);
        }

        if (_feeTaken > 0) {
            require (super.transfer(feeBeneficiary, _feeTaken) && super.transfer(_to, _value));

            return true;
        }

        return super.transfer(_to, _value);
    }

    /**
     * @dev Transfer tokens from one address to another
     * @param _from address The address which you want to send tokens from
     * @param _to address The address which you want to transfer to
     * @param _value uint256 the amount of tokens to be transferred
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool)
    {
        if (whitelist) {
            require (
                whitelistedSenders[_from]
                || whitelistedRecipients[_to]
                || _from == owner
                || _to == owner,
                'Sender or recipient not whitelisted'
            );
        }

        uint256 _feeTaken;
        (_feeTaken, _value) = applyFees(_value);
        
        if (_feeTaken > 0) {
            require (super.transferFrom(_from, feeBeneficiary, _feeTaken) && super.transferFrom(_from, _to, _value));
            
            return true;
        }

        return super.transferFrom(_from, _to, _value);
    }

    /**
     * @dev Called internally for applying fees to the transfer value.
     * @param _value uint256
     */
    function applyFees(uint256 _value)
        internal
        view
        returns (uint256 _feeTaken, uint256 _revisedValue)
    {
        _revisedValue = _value;

        if (feesEnabled && _revisedValue >= feeThreshold) {
            _feeTaken = _revisedValue.mul(fee).div(uint256(100 * (10 ** 4)));
            _revisedValue = _revisedValue.sub(_feeTaken);
        }
    }
}
/**
 * @title RemeCoin
 * @author https://bit-sentinel.com
 */
function setFee(uint256 _fee)
    external
    onlyOwner
{
    require(_fee != uint256(0) && _fee <= 100 * (10 ** 4));
    fee = _fee;
    emit FeeChanged(fee);
}

/**
 * @dev Called by owner to set fee percentage.
 * @param _fee uint256 The new fee percentage.
 */