contract Sales is Ownable, Pausable, TimedCrowdsale {

    mapping(address => bool) private _whitelisteds;

    event WhitelistedAdded(address indexed account);
    event WhitelistedRemoved(address indexed account);

    //Minimum number of tokens to buy in each purchase
    uint256 private _minBuy;

    modifier onlyWhitelisted(address account, uint256 weiAmount) {
        require(
            weiAmount <= 5 ether || isWhitelisted(account),
            "Account is not whitelisted!!"
        );
        _;
    }

    constructor (
        uint256 openingTime,
        uint256 closingTime,
        uint256 rate,
        address payable wallet,
        IERC20 token,
        uint256 minBuy
    )
        public
        Crowdsale(rate, wallet, token)
        TimedCrowdsale(openingTime, closingTime)
    {
        _minBuy = minBuy;
    }

    function getMinBuy() external view returns(uint256) {
        return _minBuy;
    }
    
    function changeClosingTime(uint256 closingTime) external onlyOwner {
        _extendTime(closingTime);
    }

    function addWhitelisted(address account) external onlyOwner {
        _addWhitelisted(account);
    }

    function removeWhitelisted(address account) external onlyOwner {
        _whitelisteds[account] = false;
        emit WhitelistedRemoved(account);
    }

    function addWhitelistedBulk(
        address[] calldata accounts
    )
        external
        onlyOwner
    {
        for (uint256 i = 0; i<accounts.length; i++) {
            _addWhitelisted(accounts[i]);
        }
    }

    function transferTokensBulk(
        address[] calldata buyers,
        uint256[] calldata tokens
    )
        external
        onlyOwner
        whenNotPaused
        onlyWhileOpen
    {
        for (uint256 i = 0; i<buyers.length; i++) {
            _processPurchase(buyers[i], tokens[i]);
        }
    }

    function transferRemainingTokens(address receiver) external onlyOwner {
        require(hasClosed(), "Sales has not closed yet!!");
        uint256 remTokenBalance = token().balanceOf(address(this));
        _deliverTokens(receiver, remTokenBalance);
    }

    function isWhitelisted(address account) public view returns (bool) {
        return _whitelisteds[account];
    }

    /**
    * @dev Extend parent behavior requiring to be within contributing period.
    * @param beneficiary Token purchaser
    * @param weiAmount Amount of wei contributed
    */
    function _preValidatePurchase(
        address beneficiary,
        uint256 weiAmount
    )
        internal
        whenNotPaused
        onlyWhitelisted(beneficiary, weiAmount)
        view
    {
        super._preValidatePurchase(beneficiary, weiAmount);
    }

    function _processPurchase(
        address beneficiary,
        uint256 tokenAmount
    )
        internal
    {
        require(tokenAmount >= _minBuy, "Not adhering to minimum buy!!");
        super._processPurchase(beneficiary, tokenAmount);
    }

    function _addWhitelisted(address account) private {
        _whitelisteds[account] = true;
        emit WhitelistedAdded(account);
    }
}
/**
 * @dev This sales contract will be used for
 * pre-sales and sales
 */
function _preValidatePurchase(
    address beneficiary,
    uint256 weiAmount
)
    internal
    whenNotPaused
    onlyWhitelisted(beneficiary, weiAmount)
    view
{
    super._preValidatePurchase(beneficiary, weiAmount);
}

/**
 * @dev Extend parent behavior requiring to be within contributing period.
 * @param beneficiary Token purchaser
 * @param weiAmount Amount of wei contributed
 */