// TODO: Condense TestUnspents into ONE call for MULTIPLE addresses (separated by | for Unspents according to API docs)

        // TODO: Enable backend to call a running bitcoin node for all this instead of callign third party services



        public static bool TestUnspents(BitcoinChain chain, string address)
        {
            // This function queries the Blockchain API for the unspent coin.
            bool result = false;
            HotBitcoinAddress hotAddress = null;
            JObject           addressInfoResult;
            JObject           unspentJsonResult;

            switch (chain)
            {
            case BitcoinChain.Core:

                addressInfoResult =
                    JObject.Parse(
                        new WebClient().DownloadString(
                            "https://blockchain.info/address/" + address + "?format=json&api_key=" +
                            SystemSettings.BlockchainSwarmopsApiKey));

                if ((int)addressInfoResult["final_balance"] == 0)
                {
                    return(false);    // no funds on address at all at this time
                }

                try
                {
                    unspentJsonResult = JObject.Parse(
                        new WebClient().DownloadString("https://blockchain.info/unspent?active=" + address + "&api_key=" +
                                                       SystemSettings.BlockchainSwarmopsApiKey));
                }
                catch (WebException webException)
                {
                    // A 500 on the above _may_ mean that there's no unspent outpoints. It can also mean a data
                    // retrieval or network error, in which case the exception must absolutely not be interpreted
                    // as valid data of zero unspent outpoints.

                    try
                    {
                        if (webException.Response == null)
                        {
                            throw;     // if there's no response at all, we can't do shit
                        }

                        string errorResponseContent =
                            new StreamReader(webException.Response.GetResponseStream()).ReadToEnd();

                        if (errorResponseContent.Trim().StartsWith("No free outputs to spend"))
                        {
                            // all is okay network-wise, there just aren't any UTXOs so we're getting an error code for that

                            return(false);    // no further processing and there are no fresh transactions
                        }

                        throw;     // otherwise throw upward
                    }
                    catch (WebException)
                    {
                        // Ok, we tried, but there's apparently a network error so we need to abort this whole thing
                        throw;
                    }
                }

                foreach (var unspentJson in unspentJsonResult["unspent_outputs"])
                {
                    BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput()
                    {
                        BitcoinAddress         = address,
                        ConfirmationCount      = (UInt32)unspentJson["confirmations"],
                        Satoshis               = (UInt64)unspentJson["value"],
                        TransactionHash        = (string)unspentJson["tx_hash_big_endian"],
                        TransactionOutputIndex = (UInt32)unspentJson["tx_output_n"]
                    };

                    if (txUnspent.ConfirmationCount < 2)
                    {
                        // Fresh transactions, return true
                        result = true;
                    }

                    // Add unspent to database

                    if (hotAddress == null)
                    {
                        hotAddress = HotBitcoinAddress.FromAddress(chain, address);
                    }

                    SwarmDb.GetDatabaseForWriting()
                    .CreateHotBitcoinAddressUnspentConditional(hotAddress.Identity, txUnspent.TransactionHash,
                                                               (int)txUnspent.TransactionOutputIndex, (Int64)txUnspent.Satoshis,
                                                               (int)txUnspent.ConfirmationCount);
                }

                // Update hotaddress totals

                HotBitcoinAddresses.UpdateAllUnspentTotals();

                return(result);



            case BitcoinChain.Cash:

                // TODO: SELECTION OF BLOCK EXPLORER, ADDRESS STRING FORMAT TO GO WITH IT

                addressInfoResult =
                    JObject.Parse(
                        new WebClient().DownloadString(
                            "https://bitcoincash.blockexplorer.com/api/addr/" + address));

                JArray unspentArray;

                if ((int)addressInfoResult["balanceSat"] == 0 && (int)addressInfoResult["unconfirmedBalanceSat"] == 0)
                {
                    return(false);    // no funds on address at all at this time
                }

                try
                {
                    unspentArray = JArray.Parse(
                        new WebClient().DownloadString("https://bitcoincash.blockexplorer.com/api/addr/" + address + "/utxo"));
                }
                catch (WebException webException)
                {
                    // A 500 on the above _may_ mean that there's no unspent outpoints. It can also mean a data
                    // retrieval or network error, in which case the exception must absolutely not be interpreted
                    // as valid data of zero unspent outpoints.

                    try
                    {
                        if (webException.Response == null)
                        {
                            throw;     // if there's no response at all, we can't do shit
                        }

                        string errorResponseContent =
                            new StreamReader(webException.Response.GetResponseStream()).ReadToEnd();

                        if (errorResponseContent.Trim().StartsWith("No free outputs to spend"))
                        {
                            // all is okay network-wise, there just aren't any UTXOs so we're getting an error code for that

                            return(false);    // no further processing and there are no fresh transactions
                        }

                        throw;     // otherwise throw upward
                    }
                    catch (WebException)
                    {
                        // Ok, we tried, but there's apparently a network error so we need to abort this whole thing
                        throw;
                    }
                }

                foreach (JObject unspentJson in unspentArray.Children())
                {
                    BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput()
                    {
                        BitcoinAddress         = address,
                        ConfirmationCount      = (UInt32)unspentJson["confirmations"],
                        Satoshis               = (UInt64)unspentJson["satoshis"],
                        TransactionHash        = (string)unspentJson["txid"],
                        TransactionOutputIndex = (UInt32)unspentJson["vout"]
                    };

                    if (txUnspent.ConfirmationCount < 2)
                    {
                        // Fresh transactions, return true
                        result = true;
                    }

                    // Add unspent to database

                    if (hotAddress == null)
                    {
                        hotAddress = HotBitcoinAddress.GetAddressOrForkCore(chain, address);
                    }

                    SwarmDb.GetDatabaseForWriting()
                    .CreateHotBitcoinAddressUnspentConditional(hotAddress.Identity, txUnspent.TransactionHash,
                                                               (int)txUnspent.TransactionOutputIndex, (Int64)txUnspent.Satoshis,
                                                               (int)txUnspent.ConfirmationCount);
                }

                // Update hotaddress totals

                HotBitcoinAddresses.UpdateAllUnspentTotals();

                return(result);

            default:
                throw new NotImplementedException("Unimplemented bitcoin chain: " + chain);
            }
        }
        public static Coin[] GetSpendableCoin(BitcoinChain chain, BitcoinSecret secretKey)
        // we're using BitcoinSecret as arg just to reinforce caller must have privkey to spend funds
        {
            List <Coin> coinList = new List <Coin>();

            switch (chain)
            {
            case BitcoinChain.Core:

                // For the Core net, we query the Blockchain API for the unspent coin.

                string addressString     = secretKey.PubKey.GetAddress(Network.Main).ToString();
                var    unspentJsonResult =
                    JObject.Parse(
                        new WebClient().DownloadString("https://blockchain.info/unspent?active=" + addressString +
                                                       "&api_key=" + SystemSettings.BlockchainSwarmopsApiKey));

                foreach (var unspentJson in unspentJsonResult["unspent_outputs"])
                {
                    BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput()
                    {
                        BitcoinAddress         = addressString,
                        ConfirmationCount      = (UInt32)unspentJson["confirmations"],
                        Satoshis               = (UInt64)unspentJson["value"],
                        TransactionHash        = (string)unspentJson["tx_hash_big_endian"],
                        TransactionOutputIndex = (UInt32)unspentJson["tx_output_n"]
                    };

                    coinList.Add(txUnspent);     // invokes implicit conversion to NBitcoin.Coin
                }

                break;


            case BitcoinChain.Cash:

                JArray unspentArray;

                // TODO: SELECT BLOCK EXPLORER AND ITS ACCOMPANYING ADDRESS FORMAT

                unspentArray = JArray.Parse(
                    new WebClient().DownloadString("https://bitcoincash.blockexplorer.com/api/addr/" + secretKey.PubKey.GetAddress(Network.Main) + "/utxo"));

                foreach (JObject unspentJson in unspentArray.Children())
                {
                    BitcoinUnspentTransactionOutput txUnspent = new BitcoinUnspentTransactionOutput()
                    {
                        BitcoinAddress         = secretKey.PubKey.GetAddress(Network.Main).ToString(),
                        ConfirmationCount      = (UInt32)unspentJson["confirmations"],
                        Satoshis               = (UInt64)unspentJson["satoshis"],
                        TransactionHash        = (string)unspentJson["txid"],
                        TransactionOutputIndex = (UInt32)unspentJson["vout"]
                    };

                    coinList.Add(txUnspent);     // invokes implicit conversion to NBitcoin.Coin
                }

                break;

            default:
                throw new NotImplementedException("Unimplemented chain identity: " + chain);
            }

            return(coinList.ToArray());
        }