contract BaseContract is Initializable, ERC20PresetMinterPauserUpgradeSafe, OwnableUpgradeSafe, FreezableToken {

    // Variables

    uint256 private _decimalsMultiplier;

    uint256 private _buyPrice;

    uint256 private _buyPriceDecimals;

    uint256 private _buyTokensLimit;

    uint256 private _boughtTokensByCurrentPrice;

    uint256 private _membersCount;

    // Roles

    //    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    //Events

    event Buy(address target, uint256 eth, uint256 tokens);

    event Mint(address indexed to, uint256 amount);


    // Modifiers

    modifier onlyMinter() {
        require(hasRole(MINTER_ROLE, msg.sender), "BaseContract: Only approved card minter");
        _;
    }

    modifier onlyOwnerOrAdmin() {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || owner() == msg.sender, "BaseContract: Only owner or admin user");
        _;
    }

    modifier onlyApproved(){
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || owner() == msg.sender || hasRole(MINTER_ROLE, msg.sender), "BaseContract: Only approved user");
        _;
    }

    modifier hasMintPermission() {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(MINTER_ROLE, msg.sender), "BaseContract: sender has not permissions");
        _;
    }

    // Getters

    function getDecimalsMultiplier() public view returns (uint256){
        return _decimalsMultiplier;
    }

    function getBuyPrice() public view returns (uint256){
        return _buyPrice;
    }

    function getBuyPriceDecimals() public view returns (uint256){
        return _buyPriceDecimals;
    }

    function getMembersCount() public view returns (uint256){
        return _membersCount;
    }

    function getBoughtTokensByCurrentPrice() public view returns (uint256){
        return _boughtTokensByCurrentPrice;
    }

    // Setters

    function setBuyPrice(uint256 __buyPrice) public onlyOwnerOrAdmin {
        _buyPrice = __buyPrice;
        _boughtTokensByCurrentPrice = 0;
    }

    function setByuPriceDecimals(uint256 __buyPriceDecimals) public onlyOwnerOrAdmin {
        _buyPriceDecimals = __buyPriceDecimals;
    }

    function setBuyTokensLimit(uint256 __buyTokensLimit) public onlyOwnerOrAdmin {
        _buyTokensLimit = __buyTokensLimit;
    }

    function setDecimals(uint8 __decimals) public onlyOwnerOrAdmin {
        _setupDecimals(__decimals);
    }

    // Functions

    /**
    * Contract initializer
    */
    function init(string memory __name, string memory __symbol, uint __decimals, uint __totalSupply, address __owner, address __admin) internal initializer {
        __Ownable_init();
        __ERC20PresetMinterPauser_init(__name, __symbol);

        _setupDecimals(uint8(__decimals));

        _decimalsMultiplier = 10 ** __decimals;

        transferOwnership(__owner);
        _setupRole(DEFAULT_ADMIN_ROLE, __owner);
        _setupRole(MINTER_ROLE, __owner);
        _setupRole(DEFAULT_ADMIN_ROLE, __admin);
        _setupRole(MINTER_ROLE, __admin);

        _membersCount = 0;
        setBuyPrice(0);
        setByuPriceDecimals(0);

        _mint(__owner, __totalSupply.mul(_decimalsMultiplier));
    }

    function calculateBuyTokens(uint256 _value) public view returns (uint256) {
        uint256 _buyDecimalMultiplier = 10 ** _buyPriceDecimals;
        return (_value * _decimalsMultiplier) / (_buyPrice * _buyDecimalMultiplier);
    }

    /**
    * @dev Buy tokens
    */
    function buy(address _sender, uint256 _value) internal {
        require(_value > 0, 'BaseContract: Value must be bigger than zero');
        require(_buyPrice > 0, 'BaseContract: Cannot buy tokens');
        require(_boughtTokensByCurrentPrice < _buyTokensLimit, 'BaseContract: Cannot buy tokens more than current limit');
        uint256 amount = this.calculateBuyTokens(_value);
        if (_boughtTokensByCurrentPrice + amount > _buyTokensLimit) {
            amount = _buyTokensLimit - _boughtTokensByCurrentPrice;
        }
        _membersCount = _membersCount.add(1);
        _transfer(owner(), _sender, amount);
        _boughtTokensByCurrentPrice = _boughtTokensByCurrentPrice.add(amount);
        msg.sender.transfer(_value);
        emit Buy(_sender, _value, amount);
    }

    /**
    *  @dev Mint tokens by owner
    */
    function addTokens(uint256 _amount) public hasMintPermission {
        _mint(msg.sender, _amount);
        emit Mint(msg.sender, _amount);
    }

    /** @dev Override base _mint ERC20 function
    */
    function _mint(address account, uint256 amount) internal override {
        require(amount > 0, "BaseContract: value must be bigger than zero");

        super._mint(account, amount);
        super._approve(account, account, amount);
        emit Transfer(address(0), account, amount);
        emit Mint(account, amount);
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20PresetMinterPauserUpgradeSafe) {
        require(isFrozen(from), "BaseContract: account from is frozen");
        require(isFrozen(to), "BaseContract: account to is frozen");
        super._beforeTokenTransfer(from, to, amount);
    }

    function freezeAccount(address account, bool freeze) public onlyOwnerOrAdmin {
        _freeze(account, freeze);
    }
}
/**
 * BaseContract
 */
function addTokens(uint256 _amount) public hasMintPermission {
    _mint(msg.sender, _amount);
    emit Mint(msg.sender, _amount);
}

/**
 *  @dev Mint tokens by owner
 */