/*
 * Copyright (C) 2017 Google Inc.
 *
 * 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 com.google.gapid.models;

import static com.google.gapid.util.Paths.thumbnail;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gapid.image.FetchedImage;
import com.google.gapid.proto.service.path.Path;
import com.google.gapid.server.Client;
import com.google.gapid.util.Events;
import com.google.gapid.util.Events.ListenerCollection;

import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.internal.DPIUtil;

import java.util.logging.Logger;

/**
 * Manages the loading of thumbnail previews.
 */
public class Thumbnails {
  protected static final Logger LOG = Logger.getLogger(ApiState.class.getName());

  public static final int THUMB_SIZE = 192;
  private static final int THUMB_PIXELS = DPIUtil.autoScaleUp(THUMB_SIZE);

  private final Client client;
  private final Devices devices;
  private final Capture capture;
  private final ListenerCollection<Listener> listeners = Events.listeners(Listener.class);

  public Thumbnails(Client client, Devices devices, Capture capture) {
    this.client = client;
    this.devices = devices;
    this.capture = capture;

    devices.addListener(new Devices.Listener() {
      @Override
      public void onReplayDeviceChanged() {
        update();
      }
    });
  }

  protected void update() {
    if (isReady()) {
      listeners.fire().onThumbnailsChanged();
    }
  }

  public boolean isReady() {
    return devices.hasReplayDevice() && capture.isLoaded();
  }

  public ListenableFuture<ImageData> getThumbnail(Path.Command command, int size) {
    return Futures.transform(FetchedImage.loadThumbnail(client, thumbnail(command, THUMB_PIXELS)),
        image -> processImage(image, size));
  }

  public ListenableFuture<ImageData> getThumbnail(Path.CommandTreeNode node, int size) {
    return Futures.transform(FetchedImage.loadThumbnail(client, thumbnail(node, THUMB_PIXELS)),
        image -> processImage(image, size));
  }

  private static ImageData processImage(ImageData image, int size) {
    size = DPIUtil.autoScaleUp(size);
    if (image.width > image.height && image.width > size) {
      return image.scaledTo(size, (image.height * size) / image.width);
    } else if (image.height > size) {
      return image.scaledTo((image.width * size) / image.height, size);
    } else {
      return image;
    }
  }

  public void addListener(Listener listener) {
    listeners.addListener(listener);
  }

  public void removeListener(Listener listener) {
    listeners.removeListener(listener);
  }

  public static interface Listener extends Events.Listener {
    /**
     * Event indicating that render settings have changed an thumbnails need to be updated.
     */
    public default void onThumbnailsChanged() { /* empty */ }
  }
}

@@ -1342,16 +1342,79 @@
  = 192;%0A
+  private static final int MIN_SIZE = DPIUtil.autoScaleUp(18);%0A
   privat
@@ -2768,16 +2768,17 @@
 .width %3E
+=
  image.h
@@ -2782,20 +2782,30 @@
 e.height
- && 
+) %7B%0A      if (
 image.wi
@@ -2810,32 +2810,34 @@
 width %3E size) %7B%0A
+  
       return ima
@@ -2892,24 +2892,26 @@
 width);%0A    
+ 
+ 
 %7D else if (i
@@ -2904,24 +2904,163 @@
   %7D else if 
+(image.width %3C MIN_SIZE) %7B%0A        return image.scaledTo(MIN_SIZE, (image.height * MIN_SIZE) / image.width);%0A      %7D%0A    %7D else %7B%0A      if 
 (image.heigh
@@ -3067,24 +3067,26 @@
 t %3E size) %7B%0A
+  
       return
@@ -3153,42 +3153,161 @@
     
+  
 %7D else 
-%7B%0A      return image
+if (image.height %3C MIN_SIZE) %7B%0A        return image.scaledTo((image.width * MIN_SIZE) / image.height, MIN_SIZE)
 ;%0A    
+ 
-%7D
+ %7D%0A    %7D%0A    return image;
 %0A  %7D

Scale small thumbnails and previews up.