contract Dex is Remote {

    event TokensPurchased(address owner, uint256 amountOfTokens, uint256 amountOfWei);
    event TokensSold(address owner, uint256 amountOfTokens, uint256 amountOfWei);
    event TokenPricesSet(uint256 sellPrice, uint256 buyPrice);
    
    address internal _dexAddress;

    uint256 public sellPrice = 200000000000;
    uint256 public buyPrice = 650000000000;
    /// @notice Allow users to buy tokens for `newBuyPrice` eth and sell tokens for `newSellPrice` eth
    /// @param newSellPrice Price the users can sell to the contract
    /// @param newBuyPrice Price users can buy from the contract
    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) public onlyOwner returns (bool success) {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;

        emit TokenPricesSet(sellPrice, buyPrice);
        success = true;
    }

    function topUpEther() external payable {
        // allow payable function to top up the contract
        // without buying tokens.
    }
    
    function _purchaseToken (address sender, uint256 amountOfWei) internal returns (bool success) {
        
        uint256 amountOfTokens = buyTokenExchangeAmount(amountOfWei);
        
        uint256 dexTokenBalance = _remoteToken.balanceOf(_dexAddress);
        require(dexTokenBalance >= amountOfTokens, "The VeriDex does not have enough tokens for this purchase.");

        _remoteToken.transfer(sender, amountOfTokens);

        emit TokensPurchased(sender, amountOfTokens, amountOfWei);
        success = true;
    }

    /** 
    @dev dexRequestTokensFromUser This allows the contract to transferFrom the user to 
    the contract using allowance that has been previously set. 
    // User must have an allowance already. If the user sends tokens to the address, 
    // Then the admin must transfer manually.
    @return string Message
    */
    function dexRequestTokensFromUser () external returns (bool success) {

        // calculate remote allowance given to the contract on the senders address
        // completed via the wallet
        uint256 amountAllowed = _remoteToken.allowance(msg.sender, _dexAddress);

        require(amountAllowed > 0, "No allowance has been set.");        
        
        uint256 amountBalance = _remoteToken.balanceOf(msg.sender);

        require(amountBalance >= amountAllowed, "Your balance must be equal or more than your allowance");
        
        uint256 amountOfWei = sellTokenExchangeAmount(amountAllowed);

        uint256 dexWeiBalance = _dexAddress.balance;

        uint256 dexTokenBalance = _remoteToken.balanceOf(_dexAddress);

        require(dexWeiBalance >= amountOfWei, "Dex balance must be equal or more than your allowance");

        _remoteTransferFrom(msg.sender, _dexAddress, amountAllowed);

        _remoteToken.approve(_dexAddress, dexTokenBalance.add(amountAllowed));  
 
        // Send Ether back to user
        msg.sender.transfer(amountOfWei);

        emit TokensSold(msg.sender, amountAllowed, amountOfWei);
        success = true;
    }
 
    /**
    @dev etherBalance: Returns value of the ether in contract.
    @return tokensOut
     */
    function etherBalance() public view returns (uint256 etherValue) {
        etherValue = _dexAddress.balance;
    }

    /**
    @dev etherBalance: Returns value of the ether in contract.
    @return tokensOut
     */
    function withdrawBalance() public onlyOwner returns (bool success) {
        msg.sender.transfer(_dexAddress.balance);
        success = true;
    }

    /**
    @dev buyTokenExchangeAmount: Returns value of the reward. Does not allocate reward.
    @param numberOfWei The number of ether in wei
    @return tokensOut
     */
    function buyTokenExchangeAmount(uint256 numberOfWei) public view returns (uint256 tokensOut) {
        tokensOut = numberOfWei.mul(10**18).div(buyPrice);
    }

    /**
    @dev sellTokenExchangeAmount: Returns value of the reward. Does not allocate reward.
    @param numberOfTokens The number of tokens
    @return weiOut
     */
    function sellTokenExchangeAmount(uint256 numberOfTokens) public view returns (uint256 weiOut) {
        weiOut = numberOfTokens.mul(sellPrice).div(10**18);
    }
 
}
function buyTokenExchangeAmount(uint256 numberOfWei) public view returns (uint256 tokensOut) {
    tokensOut = numberOfWei.mul(10**18).div(buyPrice);
}

/**
 @dev buyTokenExchangeAmount: Returns value of the reward. Does not allocate reward.
 @param numberOfWei The number of ether in wei
 @return tokensOut
 */