import java.math.BigDecimal;

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.methods.response.EthAccounts;
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;

import io.reactivex.functions.Consumer;

public class EthereumWeb3jApplicationAsynch {

	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);
			
			/*
			 * 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, DefaultBlockParameter.valueOf("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 function on completion.
	         */
	        
	        Consumer<TransactionReceipt> action = new Consumer<TransactionReceipt>() {
				@Override
				public void accept(TransactionReceipt transferReceipt) throws Exception {
					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());
				}
	        }
	        Transfer.sendFunds(web3, 
	        		credentials, 
	        		"0xd13215eab5d291f82726191651afb31e383831ea", 
	        		BigDecimal.valueOf(1), 
	        		Convert.Unit.ETHER).sendAsync().thenAccept(action);
	        
	        /*
	         * Check the account balances again
	         */
			System.out.println("Balances:");
			for (String account : ethAccounts.getAccounts()) {
				EthGetBalance balance = web3.ethGetBalance(account, DefaultBlockParameter.valueOf("latest")).send();
				System.out.println(account +" = " + balance.getBalance().toString() + " Wei");
			}
	        
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		/*
		 * Close the RPC connection when we're done
		 */
		web3.shutdown();
	}

}
