/*******************************************************************************
 * Copyright (c) 2013, Art Clarke.  All rights reserved.
 *  
 * This file is part of Humble-Video.
 *
 * Humble-Video is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Humble-Video is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Humble-Video.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/

package io.humble.video.awt;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 * Displays a {@link BufferedImage} in a {@link JFrame}. Helpful for
 * building quick and simple video players.
 * 
 * @author aclarke
 *
 */
public class ImageFrame extends JFrame
{

  /**
   * To avoid a warning... 
   */

  private static final long serialVersionUID = -4752966848100689153L;
  private final ImageComponent mOnscreenPicture;
  
  private static boolean mIsHeadless = GraphicsEnvironment.isHeadless();

  /**
   * Create the frame
   */

  public static ImageFrame make() {
    return mIsHeadless ? null : new ImageFrame();
  }
  private ImageFrame()
  {
    super();
    mOnscreenPicture = new ImageComponent();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    getContentPane().add(mOnscreenPicture);
    this.setVisible(true);
    this.pack();
  }
  
  public void setImage(final Image aImage)
  {
    mOnscreenPicture.setImage(aImage);
  }

  public class ImageComponent extends JComponent
  {
    /**
     * yeah... good idea to add this.
     */
    private static final long serialVersionUID = 5584422798735147930L;
    private Image mImage;
    private Dimension mSize;

    public void setImage(Image image)
    {
      SwingUtilities.invokeLater(new ImageRunnable(image));
    }
    
    public void setImageSize(Dimension newSize)
    {
    }
    
    private class ImageRunnable implements Runnable
    {
      private final Image newImage;

      public ImageRunnable(Image newImage)
      {
        super();
        this.newImage = newImage;
      }
  
      public void run()
      {
        ImageComponent.this.mImage = newImage;
        final Dimension newSize = new Dimension(mImage.getWidth(null), 
          mImage.getHeight(null));
        if (!newSize.equals(mSize))
        {
          ImageComponent.this.mSize = newSize;
          ImageFrame.this.setSize(mImage.getWidth(null), mImage.getHeight(null));
          ImageFrame.this.setVisible(true);
        }
        repaint();
      }
    }
    
    public ImageComponent()
    {
      mSize = new Dimension(0, 0);
      setSize(mSize);
    }

    public synchronized void paint(Graphics g)
    {
      if (mImage != null)
        g.drawImage(mImage, 0, 0, this);
    }
  }
}

@@ -1106,16 +1106,68 @@
 edImage;
+%0Aimport java.lang.reflect.InvocationTargetException;
 %0A%0Aimport
@@ -1375,16 +1375,242 @@
 rs.%0A * %0A
+ * ImageFrame methods can be called from any thread, but all UI updating will get%0A * done on the main Swing UI thread. With the exception of the constructor,%0A * all public methods return without blocking on the UI thread.%0A * %0A
  * @auth
@@ -2112,24 +2112,188 @@
 omponent();%0A
+    final ImageFrame thisClosure = this;%0A    try %7B%0A      SwingUtilities.invokeAndWait(new Runnable() %7B%0A        %0A        @Override%0A        public void run() %7B%0A      
     setDefau
@@ -2332,16 +2332,22 @@
 CLOSE);%0A
+      
     getC
@@ -2386,20 +2386,33 @@
 e);%0A    
-this
+      thisClosure
 .setVisi
@@ -2430,20 +2430,302 @@
     
-this.pack();
+      thisClosure.pack();%0A        %7D%0A      %7D);%0A    %7D catch (InterruptedException e) %7B%0A      throw new RuntimeException(%22The UI thread got interrupted; fail%22);%0A    %7D catch (InvocationTargetException e) %7B%0A      throw new RuntimeException(%22The UI thread could not execute our closure; fail%22);%0A    %7D
 %0A  %7D

make all AWT initiatialization occur on the UI thread.