/**
 * Copyright 2011 Google Inc.
 * Copyright 2014 Andreas Schildbach
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bitcoinj.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.concurrent.Semaphore;

/**
 * <p>An implementation of {@link AbstractPeerEventListener} that listens to chain download events and tracks progress
 * as a percentage. The default implementation prints progress to stdout, but you can subclass it and override the
 * progress method to update a GUI instead.</p>
 */
public class DownloadProgressTracker extends AbstractPeerEventListener {
    private static final Logger log = LoggerFactory.getLogger(DownloadProgressTracker.class);
    private int originalBlocksLeft = -1;
    private int lastPercent = 0;
    private Semaphore done = new Semaphore(0);
    private boolean caughtUp = false;

    @Override
    public void onChainDownloadStarted(Peer peer, int blocksLeft) {
        startDownload(blocksLeft);
        // Only mark this the first time, because this method can be called more than once during a chain download
        // if we switch peers during it.
        if (originalBlocksLeft == -1)
            originalBlocksLeft = blocksLeft;
        else
            log.info("Chain download switched to {}", peer);
        if (blocksLeft == 0) {
            doneDownload();
            done.release();
        }
    }

    @Override
    public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) {
        if (caughtUp)
            return;

        if (blocksLeft == 0) {
            caughtUp = true;
            doneDownload();
            done.release();
        }

        if (blocksLeft < 0 || originalBlocksLeft <= 0)
            return;

        double pct = 100.0 - (100.0 * (blocksLeft / (double) originalBlocksLeft));
        if ((int) pct != lastPercent) {
            progress(pct, blocksLeft, new Date(block.getTimeSeconds() * 1000));
            lastPercent = (int) pct;
        }
    }

    /**
     * Called when download progress is made.
     *
     * @param pct  the percentage of chain downloaded, estimated
     * @param date the date of the last block downloaded
     */
    protected void progress(double pct, int blocksSoFar, Date date) {
        log.info(String.format("Chain download %d%% done with %d blocks to go, block date %s", (int) pct, blocksSoFar,
                Utils.dateTimeFormat(date)));
    }

    /**
     * Called when download is initiated.
     *
     * @param blocks the number of blocks to download, estimated
     */
    protected void startDownload(int blocks) {
        if (blocks > 0 && originalBlocksLeft == -1)
            log.info("Downloading block chain of size " + blocks + ". " +
                    (blocks > 1000 ? "This may take a while." : ""));

    }

    /**
     * Called when we are done downloading the block chain.
     */
    protected void doneDownload() {
    }

    /**
     * Wait for the chain to be downloaded.
     */
    public void await() throws InterruptedException {
        done.acquire();
    }
}

@@ -654,16 +654,132 @@
 .core;%0A%0A
+import com.google.common.util.concurrent.ListenableFuture;%0Aimport com.google.common.util.concurrent.SettableFuture;%0A
 import o
@@ -795,16 +795,16 @@
 Logger;%0A
-
 import o
@@ -879,25 +879,34 @@
 current.
-Semaphore
+ExecutionException
 ;%0A%0A/**%0A 
@@ -1449,38 +1449,58 @@
 e Se
-maphore done = new Semaphore(0
+ttableFuture%3CLong%3E future = SettableFuture.create(
 );%0A 
@@ -1611,32 +1611,92 @@
 t blocksLeft) %7B%0A
+        if (blocksLeft %3E 0 && originalBlocksLeft == -1)%0A    
         startDow
@@ -1714,16 +1714,16 @@
 sLeft);%0A
-
         
@@ -2094,37 +2094,55 @@
             
-done.release(
+future.set(peer.getBestHeight()
 );%0A        %7D
@@ -2387,21 +2387,39 @@
     
-done.release(
+future.set(peer.getBestHeight()
 );%0A 
@@ -3375,64 +3375,8 @@
 ) %7B%0A
-        if (blocks %3E 0 && originalBlocksLeft == -1)%0A    
     
@@ -3457,20 +3457,16 @@
         
-    
 (blocks 
@@ -3507,17 +3507,16 @@
 : %22%22));%0A
-%0A
     %7D%0A%0A 
@@ -3759,22 +3759,385 @@
     
-done.acquire()
+try %7B%0A            future.get();%0A        %7D catch (ExecutionException e) %7B%0A            throw new RuntimeException(e);%0A        %7D%0A    %7D%0A%0A    /**%0A     * Returns a listenable future that completes with the height of the best chain (as reported by the peer) once chain%0A     * download seems to be finished.%0A     */%0A    public ListenableFuture%3CLong%3E getFuture() %7B%0A        return future
 ;%0A  

Change how DownloadProgressTracker waits for progress to avoid possible deadlocks.