contract ChannelContract is Ownable, SafeMath {
    struct Wallet {
        string name;
        address wallet;
        bool isBlocked;  // Block wallet transfer tokens to.
    }

    Wallet[] wallets;   // list of wallets where allowed to transfer (exchanges wallets, Bancor, etc).
    uint256 public spent;   // Amount of token spend from channel (sent to Market, SmartSwap, etc.)
    uint256 public received; // Amount of token received from Escrow

    string public channelName;
    IERC20Token public tokenContract;
    address payable public escrowContract;

    constructor(IERC20Token _tokenContract,  address payable _escrowContract, string memory _name) public {
        tokenContract = _tokenContract;
        escrowContract = _escrowContract;
        channelName = _name;
    }

    event SetWallet(address indexed channel, uint256 walletId, address wallet, string name);
    event BlockWallet(address indexed channel, uint256 walletId, bool isBlock);
    event TransferTokens(address indexed channel, address indexed to, uint256 value, string walletName);
    event ReceivedETH(address indexed from, uint256 value);

    function totalSupply() public view returns(uint256) {
        return safeSub(received, spent);
    }

    function addWallet(string memory name, address wallet) external onlyOwner {
        require(wallet != address(0),"Zero address");
        uint256 walletId = wallets.length;
        wallets.push(Wallet(name, wallet, false));
        emit SetWallet(address(this), walletId, wallet, name);
    }

    // if wallet is address(0) - wallet removed.
    function updateWallet(uint256 walletId, address wallet) external onlyOwner {
        wallets[walletId].wallet = wallet;
        emit SetWallet(address(this), walletId, wallet, wallets[walletId].name);
    }

    // Block selected wallet transfer to.
    function blockWallet(uint256 walletId, bool isBlock) external onlyOwner {
        wallets[walletId].isBlocked = isBlock;
        emit BlockWallet(address(this), walletId, isBlock);
    }

    function getWalletsNumber() external view returns(uint256) {
        return wallets.length;
    }

    function getWalletInfo(uint256 walletId) external view returns(string memory name, address wallet, bool isBlocked) {
        name = wallets[walletId].name;
        wallet = wallets[walletId].wallet;
        isBlocked = wallets[walletId].isBlocked;
    }

    // Receive tokens from Escrow (gateway)
    function receiveTokens(uint256 value) external onlyOwner {
        received = safeAdd(received, value);
    }

    // transfer tokens to selected wallet (ex. Exchange wallet)
    function transferTokens(uint256 walletId, uint256 value) external onlyOwner {
        require(totalSupply() >= value, "Not enough tokens");
        address to = wallets[walletId].wallet;
        require(to != address(0), "Wallet removed");
        spent = safeAdd(spent, value); 
        tokenContract.transfer(to, value);
        emit TransferTokens(address(this), to, value, wallets[walletId].name);
    }


    // transfer tokens to selected wallet (ex. Exchange wallet)
    function approveTokens(uint256 walletId, uint256 value) external onlyOwner {
        require(totalSupply() >= value, "Not enough tokens");
        address to = wallets[walletId].wallet;
        require(to != address(0), "Wallet removed");
        spent = safeAdd(spent, value); 
        tokenContract.approve(to, value);
        emit TransferTokens(address(this), to, value, wallets[walletId].name);
    }

    /**
     * @dev Trigger arbitrary function on selected wallet address.
     * @param walletId The wallet to call
     * @param params encoded params
     */
    function trigger(uint256 walletId, bytes calldata params) external onlyOwner {
        address to = wallets[walletId].wallet;
        to.call(params);
    }

    // Gateway transfer token to Escrow
    function returnToEscrow(uint256 value) external onlyOwner {
        require(totalSupply() >= value, "Not enough tokens");
        received = safeSub(received, value);
        tokenContract.transfer(escrowContract, value);
    }

    /**
     * @dev Send giveaways (received ETH/ERC20) to Escrow for splitting among participants.
     * When token sold on exchange, withdraw ETH/ERC20 to this contract address.
     * @param soldTokenAmount amount of sold tokens
     * @param receivedToken The ERC20 token address (or 0 for ETH) for which tokens were sold.
     * @param receivedAmount Amount of ETH/ERC20 received for sold tokens.
     */
    function transferGiveaways(uint256 soldTokenAmount, address receivedToken, uint256 receivedAmount) external onlyOwner {
        require(spent >= soldTokenAmount, "Wrong Sold Token Amount");
        if (receivedToken == address(0)) {
            escrowContract.transfer(receivedAmount);
        }
        else {
            IERC20Token(receivedToken).transfer(escrowContract, receivedAmount);
        }
        spent = safeSub(spent, soldTokenAmount);
        received = safeSub(received, soldTokenAmount);
    }

    // accept ETH
    receive() external payable {
        emit ReceivedETH(msg.sender, msg.value);
    }
}
function receiveTokens(uint256 value) external onlyOwner {
    received = safeAdd(received, value);
}

// Receive tokens from Escrow (gateway)