import java.math.BigDecimal;
import java.util.concurrent.Flow.Subscription;
import java.util.function.Consumer;

import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthAccounts;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.core.methods.response.Web3ClientVersion;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;

public class EthereumWeb3jFilter {

	public static void main(String[] args) {

		/*
		 * Open a connection to the geth node using rpc
		 */
		Web3j web3 = Web3j.build(new HttpService("http://localhost:8545"));
		
		try {
			/*
			 * Use RPC send() to get the client version of geth
			 */
			Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().send();
			String clientVersion = web3ClientVersion.getWeb3ClientVersion();
			System.out.println("Client is version: "+clientVersion);
			
	        Consumer<EthTransaction> txAction = new Consumer<EthTransaction>() {
				@Override
				public void accept(EthTransaction tx) {
					System.out.println("The transaction has finally been mined!");
			        /*
			         * Examine the results of the transaction (the transaction receipt)
			         */
			        System.out.println("Transaction complete");
			        System.out.println("  Tx Hash: " + transferReceipt.getTransactionHash());
			        System.out.println("  Block Hash: " + transferReceipt.getBlockHash());
			        System.out.println("  Block Number: " + transferReceipt.getBlockNumber());
			        System.out.println("  Tx Index: " + transferReceipt.getTransactionIndex());
			        System.out.println("  Status: " + transferReceipt.getStatus());
			        System.out.println("  Gas Used: " + transferReceipt.getGasUsed());
			        System.out.println("  From: " + transferReceipt.getFrom());
			        System.out.println("  To: " + transferReceipt.getTo());
				}
	        };
	        
	        Consumer<EthBlock> blockAction = new Consumer<EthBlock>() {
				@Override
				public void accept(EthBlock block) {
			        System.out.println("Block Info");
			        System.out.println("  Hash: " + block.getBlock().getHash());
			        System.out.println("  Parent: " + block.getBlock().getParentHash());
			        System.out.println("  Author: " + block.getBlock().getAuthor());
			        System.out.println("  Miner: " + block.getBlock().getMiner());
			        System.out.println("  Extra: " + block.getBlock().getExtraData());
			        for (String uncle : block.getBlock().getUncles()) {
			        	System.out.println("  Uncle: " + uncle);
			        }
			        System.out.println("  Nonce: " + block.getBlock().getNonce());
				}
	        };
			
			
			
			
			Subscription subscription = web3.blockFlowable(false).subscribe(block -> {
				
				public void accept(Block block) {
			        System.out.println("New Block Appeared on Chain");
			        System.out.println("  Hash: " + block.getBlock().getHash());
			        System.out.println("  Parent: " + block.getBlock().getParentHash());
			        System.out.println("  Author: " + block.getBlock().getAuthor());
			        System.out.println("  Miner: " + block.getBlock().getMiner());
			        System.out.println("  Extra: " + block.getBlock().getExtraData());
			        for (String uncle : block.getBlock().getUncles()) {
			        	System.out.println("  Uncle: " + uncle);
			        }
			        System.out.println("  Nonce: " + block.getBlock().getNonce());
			        
			});
			/*
			 * Use RPC send() to get the accounts on this node
			 */
			System.out.println("Accounts:");
			EthAccounts ethAccounts = web3.ethAccounts().send();
			for (String account : ethAccounts.getAccounts()) {
				System.out.println(account);
			}

			/*
			 * Get the balances for each account (in Wei)
			 */
			System.out.println("Balances:");
			for (String account : ethAccounts.getAccounts()) {
				EthGetBalance balance = web3.ethGetBalance(account, DefaultBlockParameterName.LATEST).send();
				System.out.println(account +" = " + balance.getBalance().toString() + " Wei");
			}

			/*
			 * Open a keystore file using the provided password to load transaction signing credentials (public and private key)
			 */
	        Credentials credentials = WalletUtils.loadCredentials("password", "/home/ubuntu/.ethereum/keystore/UTC--2019-04-25T17-44-11.125457608Z--7d01838cde5ab81b171b64e4216c5f3dcbe45c66");
	        System.out.println("My credentials: ");
	        System.out.println("Address = " + credentials.getAddress());
	        System.out.println("Private key = " + credentials.getEcKeyPair().getPrivateKey().toString(16));
	        System.out.println("Public key = " + credentials.getEcKeyPair().getPublicKey().toString(16));

	        /*
	         * Use the credentials to send Ether funds to another address
	         * 
	         * This will take a while because send() waits until the transactionReceipt
	         * is available. This naturally happens only after the transaction is
	         * mined into a block. 
	         * 
	         * We could use sendAsync() to get a Future object and then later call
	         * get() on that or attach a callback for completion.
	         */
	        TransactionReceipt transferReceipt = Transfer.sendFunds(web3, 
	        		credentials, 
	        		"0xd13215eab5d291f82726191651afb31e383831ea", 
	        		BigDecimal.valueOf(1), 
	        		Convert.Unit.ETHER).send();
	        
	        /*
	         * Examine the results of the transaction (the transaction receipt)
	         */
	        System.out.println("Transaction complete");
	        System.out.println("  Tx Hash: " + transferReceipt.getTransactionHash());
	        System.out.println("  Block Hash: " + transferReceipt.getBlockHash());
	        System.out.println("  Block Number: " + transferReceipt.getBlockNumber());
	        System.out.println("  Tx Index: " + transferReceipt.getTransactionIndex());
	        System.out.println("  Status: " + transferReceipt.getStatus());
	        System.out.println("  Gas Used: " + transferReceipt.getGasUsed());
	        System.out.println("  From: " + transferReceipt.getFrom());
	        System.out.println("  To: " + transferReceipt.getTo());
	        
	        /*
	         * Check the account balances again
	         */
			System.out.println("Balances:");
			for (String account : ethAccounts.getAccounts()) {
				EthGetBalance balance = web3.ethGetBalance(account, DefaultBlockParameterName.LATEST).send();
				System.out.println(account +" = " + balance.getBalance().toString() + " Wei");
			}
	        
			EthBlock block = web3.ethGetBlockByNumber(DefaultBlockParameter.valueOf(transferReceipt.getBlockNumber()), true).send();

	        System.out.println("Block Info");
	        System.out.println("  Hash: " + block.getBlock().getHash());
	        System.out.println("  Parent: " + block.getBlock().getParentHash());
	        System.out.println("  Author: " + block.getBlock().getAuthor());
	        System.out.println("  Miner: " + block.getBlock().getMiner());
	        System.out.println("  Extra: " + block.getBlock().getExtraData());
	        for (String uncle : block.getBlock().getUncles()) {
	        	System.out.println("  Uncle: " + uncle);
	        }
	        System.out.println("  Nonce: " + block.getBlock().getNonce());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		/*
		 * Close the RPC connection when we're done
		 */
		web3.shutdown();
	}

}
