package ij.plugin;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.*;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.frame.Editor;
    
/** Copies and pastes images to the clipboard. Java 1.4 or later is 
    required to copy to or paste from the system clipboard. */
public class Clipboard implements PlugIn, Transferable {
    static java.awt.datatransfer.Clipboard clipboard;
    
    public void run(String arg) {
        if (IJ.altKeyDown()) {
            if (arg.equals("copy"))
                arg = "scopy";
            else if (arg.equals("paste"))
                arg = "spaste";
        }
        if (arg.equals("copy"))
            copy(false);
        else if (arg.equals("paste"))
            paste();
        else if (arg.equals("cut"))
            copy(true);
        else if (arg.equals("scopy"))
            copyToSystem();
        else if (arg.equals("showsys"))
            showSystemClipboard();
        else if (arg.equals("show"))
            showInternalClipboard();
    }
    
    void copy(boolean cut) {
        ImagePlus imp = WindowManager.getCurrentImage();
        if (imp!=null)
            imp.copy(cut);
        else
            IJ.noImage();
    }
    
    void paste() {
        if (ImagePlus.getClipboard()==null) {
            if (IJ.isJava14())
                showSystemClipboard();
            else
                IJ.noImage();
        } else {
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp!=null)
                imp.paste();
            else
                showInternalClipboard   ();
        }
    }

    boolean setup() {
        if (!IJ.isJava14()) {
            IJ.error("Clipboard", "Java 1.4 or later required");
            return false;
        }
        if (clipboard==null)
            clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        return true;
    }
    
    void copyToSystem() {
        if (!setup()) return;
        try {
            clipboard.setContents(this, null);
        } catch (Throwable t) {}
    }
    
    void showSystemClipboard() {
        if (!setup()) return;
        IJ.showStatus("Opening system clipboard...");
        try {
            Transferable transferable = clipboard.getContents(null);
            boolean imageSupported = transferable.isDataFlavorSupported(DataFlavor.imageFlavor);
            boolean textSupported = transferable.isDataFlavorSupported(DataFlavor.stringFlavor);
            if (!imageSupported && IJ.isMacOSX() && displayMacImage(transferable))
                return;
            if (imageSupported) {
                Image img = (Image)transferable.getTransferData(DataFlavor.imageFlavor);
                if (img==null) {
                    IJ.error("Unable to convert image on system clipboard");
                    IJ.showStatus("");
                    return;
                }
                int width = img.getWidth(null);
                int height = img.getHeight(null);
                BufferedImage   bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                Graphics g = bi.createGraphics();
                g.drawImage(img, 0, 0, null);
                g.dispose();
                WindowManager.checkForDuplicateName = true;
                new ImagePlus("Clipboard", bi).show();
            } else if (textSupported) {
                String text = (String)transferable.getTransferData(DataFlavor.stringFlavor);
                Editor ed = new Editor();
                ed.setSize(600, 300);
                ed.create("Clipboard", text);
                IJ.showStatus("");
            } else
                IJ.error("Unable to find an image on the system clipboard");
        } catch (Throwable t) {
            IJ.showStatus(""+t);
        }
    }
    
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[] { DataFlavor.imageFlavor };
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return DataFlavor.imageFlavor.equals(flavor);
    }

    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
        if (!isDataFlavorSupported(flavor))
            throw new UnsupportedFlavorException(flavor);
        ImagePlus imp = WindowManager.getCurrentImage();
        if ( imp != null) {
            ImageProcessor ip = imp.getProcessor();
            ip = ip.crop();
            int w = ip.getWidth();
            int h = ip.getHeight();
            IJ.showStatus(w+"x"+h+ " image copied to system clipboard");
            Image img = IJ.getInstance().createImage(w, h);
            Graphics g = img.getGraphics();
            g.drawImage(ip.createImage(), 0, 0, null);
            g.dispose();
            return img;
        } else {
            //IJ.noImage();
            return null;
        }
    }
    
    void showInternalClipboard() {
        ImagePlus clipboard = ImagePlus.getClipboard();
        if (clipboard!=null) {
            ImageProcessor ip = clipboard.getProcessor();
            ImagePlus imp2 = new ImagePlus("Clipboard", ip.duplicate());
            Roi roi = clipboard.getRoi();
            imp2.killRoi();
            if (roi!=null && roi.isArea() && roi.getType()!=Roi.RECTANGLE) {
                roi = (Roi)roi.clone();
                roi.setLocation(0, 0);
                imp2.setRoi(roi);
                WindowManager.setTempCurrentImage(imp2);
                IJ.run("Clear Outside");
                imp2.killRoi();
            }
            WindowManager.checkForDuplicateName = true;          
            imp2.show();
        } else
            IJ.error("The internal clipboard is empty.");
    }
    
    boolean displayMacImage(Transferable t) {
        Image img = getMacImage(t);
        if (img!=null) {
            WindowManager.checkForDuplicateName = true;          
            new ImagePlus("Clipboard", img).show();
        }
        return img!=null;
    }

    // Mac OS X's data transfer handling is horribly broken. We sometimes
    // need to use the "image/x-pict" MIME type and then Quicktime
    // for Java in order to get the image data.
    Image getMacImage(Transferable t) {
        if (!isQTJavaInstalled())
            return null;
        Image img = null;
        DataFlavor[] d = t.getTransferDataFlavors();
        if (d==null || d.length==0)
            return null;
        try {
            Object is = t.getTransferData(d[0]);
            if (is==null || !(is instanceof InputStream))
                return null;
            img = getImageFromPictStream((InputStream)is);
        } catch (Exception e) {}
        return img;
    }
    
    // Converts a PICT to an AWT image using QuickTime for Java.
    // This code was contributed by Gord Peters.
    Image getImageFromPictStream(InputStream is) {
        try {
            ByteArrayOutputStream baos= new ByteArrayOutputStream();
            // We need to strip the header from the data because a PICT file
            // has a 512 byte header and then the data, but in our case we only
            // need the data. --GP
            byte[] header= new byte[512];
            byte[] buf= new byte[4096];
            int retval= 0, size= 0;
            baos.write(header, 0, 512);
            while ( (retval= is.read(buf, 0, 4096)) > 0)
                baos.write(buf, 0, retval);     
            baos.close();
            size = baos.size();
            //IJ.log("size: "+size); IJ.wait(2000);
            if (size<=0)
                return null;
            byte[] imgBytes= baos.toByteArray();
            // Again with the uglyness.  Here we need to use the Quicktime
            // for Java code in order to create an Image object from
            // the PICT data we received on the clipboard.  However, in
            // order to get this to compile on other platforms, we use
            // the Java reflection API.
            //
            // For reference, here is the equivalent code without
            // reflection:
            //
            //
            // if (QTSession.isInitialized() == false) {
            //     QTSession.open();
            // }
            // QTHandle handle= new QTHandle(imgBytes);
            // GraphicsImporter gi=
            //     new GraphicsImporter(QTUtils.toOSType("PICT"));
            // gi.setDataHandle(handle);
            // QDRect qdRect= gi.getNaturalBounds();
            // GraphicsImporterDrawer gid= new GraphicsImporterDrawer(gi);
            // QTImageProducer qip= new QTImageProducer(gid,
            //                          new Dimension(qdRect.getWidth(),
            //                                        qdRect.getHeight()));
            // return(Toolkit.getDefaultToolkit().createImage(qip));
            //
            // --GP
            //IJ.log("quicktime.QTSession");
            Class c = Class.forName("quicktime.QTSession");
            Method m = c.getMethod("isInitialized", null);
            Boolean b= (Boolean)m.invoke(null, null);           
            if (b.booleanValue() == false) {
                m= c.getMethod("open", null);
                m.invoke(null, null);
            }
            c= Class.forName("quicktime.util.QTHandle");
            Constructor con = c.getConstructor(new Class[] {imgBytes.getClass() });
            Object handle= con.newInstance(new Object[] { imgBytes });
            String s= new String("PICT");
            c = Class.forName("quicktime.util.QTUtils");
            m = c.getMethod("toOSType", new Class[] { s.getClass() });
            Integer type= (Integer)m.invoke(null, new Object[] { s });
            c = Class.forName("quicktime.std.image.GraphicsImporter");
            con = c.getConstructor(new Class[] { type.TYPE });
            Object importer= con.newInstance(new Object[] { type });
            m = c.getMethod("setDataHandle",
            new Class[] { Class.forName("quicktime.util." + "QTHandleRef") });
            m.invoke(importer, new Object[] { handle });
            m = c.getMethod("getNaturalBounds", null);
            Object rect= m.invoke(importer, null);
            c = Class.forName("quicktime.app.view.GraphicsImporterDrawer");
            con = c.getConstructor(new Class[] { importer.getClass() });
            Object iDrawer = con.newInstance(new Object[] { importer });
            m = rect.getClass().getMethod("getWidth", null);
            Integer width= (Integer)m.invoke(rect, null);
            m = rect.getClass().getMethod("getHeight", null);
            Integer height= (Integer)m.invoke(rect, null);
            Dimension d= new Dimension(width.intValue(), height.intValue());
            c = Class.forName("quicktime.app.view.QTImageProducer");
            con = c.getConstructor(new Class[] { iDrawer.getClass(), d.getClass() });
            Object producer= con.newInstance(new Object[] { iDrawer, d });
            if (producer instanceof ImageProducer)
                return(Toolkit.getDefaultToolkit().createImage((ImageProducer)producer));
        } catch (Exception e) {IJ.showStatus("QuickTime for java error");}
        return null;
    }

    // Retuns true if QuickTime for Java is installed.
    // This code was contributed by Gord Peters.
    boolean isQTJavaInstalled() {
        boolean isInstalled = false;
        try {
            Class c= Class.forName("quicktime.QTSession");
            isInstalled = true;
        } catch (Exception e) {}
        return isInstalled;
    }

}