    001// License: GPL. For details, see LICENSE file.
    002package org.openstreetmap.josm.plugins.streetside.actions;
    003
    004import java.awt.image.BufferedImage;
    005
    006import javax.swing.SwingUtilities;
    007
    008import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
    009import org.openstreetmap.josm.plugins.streetside.StreetsideData;
    010import org.openstreetmap.josm.plugins.streetside.StreetsideDataListener;
    011import org.openstreetmap.josm.plugins.streetside.StreetsideImage;
    012import org.openstreetmap.josm.plugins.streetside.StreetsideLayer;
    013import org.openstreetmap.josm.plugins.streetside.cache.CacheUtils;
    014import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog;
    015import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;
    016
    017
    018/**
    019 * Thread containing the walk process.
    020 *
    021 * @author nokutu
    022 */
    023public class WalkThread extends Thread implements StreetsideDataListener {
    024  private final int interval;
    025  private final StreetsideData data;
    026  private boolean end;
    027  private final boolean waitForFullQuality;
    028  private final boolean followSelected;
    029  private final boolean goForward;
    030  private BufferedImage lastImage;
    031  private volatile boolean paused;
    032
    033  /**
    034   * Main constructor.
    035   *
    036   * @param interval How often the images switch.
    037   * @param waitForPicture If it must wait for the full resolution picture or just the
    038   * thumbnail.
    039   * @param followSelected Zoom to each image that is selected.
    040   * @param goForward true to go forward; false to go backwards.
    041   */
    042  public WalkThread(int interval, boolean waitForPicture,
    043                    boolean followSelected, boolean goForward) {
    044    this.interval = interval;
    045    waitForFullQuality = waitForPicture;
    046    this.followSelected = followSelected;
    047    this.goForward = goForward;
    048    data = StreetsideLayer.getInstance().getData();
    049    data.addListener(this);
    050  }
    051
    052  @Override
    053  public void run() {
    054    try {
    055      while (!end && data.getSelectedImage().next() != null) {
    056        StreetsideAbstractImage image = data.getSelectedImage();
    057        if (image != null && image.next() instanceof StreetsideImage) {
    058          // Predownload next 10 thumbnails.
    059          preDownloadImages((StreetsideImage) image.next(), 10, CacheUtils.PICTURE.THUMBNAIL);
    060          if(StreetsideProperties.PREDOWNLOAD_CUBEMAPS.get()) {
    061            preDownloadCubemaps((StreetsideImage) image.next(), 10);
    062          }
    063          if (waitForFullQuality) {
    064            // Start downloading 3 next full images.
    065            StreetsideAbstractImage currentImage = image.next();
    066                  preDownloadImages((StreetsideImage) currentImage, 3, CacheUtils.PICTURE.FULL_IMAGE);
    067                  /*if (StreetsideProperties.PREDOWNLOAD_CUBEMAPS.get().booleanValue()) {
    068                  preDownloadCubemaps((StreetsideImage) currentImage, 3);
    069            }*/
    070          }
    071        }
    072        try {
    073          // Waits for full quality picture.
    074          final BufferedImage displayImage = StreetsideMainDialog.getInstance().getStreetsideImageDisplay().getImage();
    075          if (waitForFullQuality && image instanceof StreetsideImage) {
    076            while (displayImage == lastImage || displayImage == null || displayImage.getWidth() < 2048) {
    077              Thread.sleep(100);
    078            }
    079          } else { // Waits for thumbnail.
    080            while (displayImage == lastImage || displayImage == null || displayImage.getWidth() < 320) {
    081              Thread.sleep(100);
    082            }
    083          }
    084          while (paused) {
    085            Thread.sleep(100);
    086          }
    087          wait(interval);
    088          while (paused) {
    089            Thread.sleep(100);
    090          }
    091          lastImage = StreetsideMainDialog.getInstance().getStreetsideImageDisplay().getImage();
    092          if (goForward) {
    093            data.selectNext(followSelected);
    094          } else {
    095            data.selectPrevious(followSelected);
    096          }
    097        } catch (InterruptedException e) {
    098          return;
    099        }
    100      }
    101    } catch (NullPointerException e) {
    102      // TODO: Avoid NPEs instead of waiting until they are thrown and then catching them
    103      return;
    104    }
    105    end();
    106  }
    107
    108  private void preDownloadCubemaps(StreetsideImage startImage, int n) {
    109          if (n >= 1 && startImage != null) {
    110
    111                  for (int i = 0; i < 6; i++) {
    112                                for (int j = 0; j < 4; j++) {
    113                                        for (int k = 0; k < 4; k++) {
    114
    115                                                CacheUtils.downloadPicture(startImage, CacheUtils.PICTURE.CUBEMAP);
    116                                                if (startImage.next() instanceof StreetsideImage && n >= 2) {
    117                                                        preDownloadCubemaps((StreetsideImage) startImage.next(), n - 1);
    118                                                }
    119                                        }
    120                                }
    121                  }
    122          }
    123  }
    124
    125/**
    126   * Downloads n images into the cache beginning from the supplied start-image (including the start-image itself).
    127   *
    128   * @param startImage the image to start with (this and the next n-1 images in the same sequence are downloaded)
    129   * @param n the number of images to download
    130   * @param type the quality of the image (full or thumbnail)
    131   */
    132  private static void preDownloadImages(StreetsideImage startImage, int n, CacheUtils.PICTURE type) {
    133    if (n >= 1 && startImage != null) {
    134      CacheUtils.downloadPicture(startImage, type);
    135      if (startImage.next() instanceof StreetsideImage && n >= 2) {
    136        preDownloadImages((StreetsideImage) startImage.next(), n - 1, type);
    137      }
    138    }
    139  }
    140
    141  @Override
    142  public void imagesAdded() {
    143    // Nothing
    144  }
    145
    146  @Override
    147  public void selectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) {
    148    if (newImage != oldImage.next()) {
    149      end();
    150      interrupt();
    151    }
    152  }
    153
    154  /**
    155   * Continues with the execution if paused.
    156   */
    157  public void play() {
    158    paused = false;
    159  }
    160
    161  /**
    162   * Pauses the execution.
    163   */
    164  public void pause() {
    165    paused = true;
    166  }
    167
    168  /**
    169   * Stops the execution.
    170   */
    171  public void stopWalk() {
    172    if (SwingUtilities.isEventDispatchThread()) {
    173      end();
    174      interrupt();
    175    } else {
    176      SwingUtilities.invokeLater(this::stopWalk);
    177    }
    178  }
    179
    180  /**
    181   * Called when the walk stops by itself of forcefully.
    182   */
    183  public void end() {
    184    if (SwingUtilities.isEventDispatchThread()) {
    185      end = true;
    186      data.removeListener(this);
    187      StreetsideMainDialog.getInstance().setMode(StreetsideMainDialog.MODE.NORMAL);
    188    } else {
    189      SwingUtilities.invokeLater(this::end);
    190    }
    191  }
    192}