/*
* Copyright 2003-2010 Tufts University  Licensed under the
 * Educational Community 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.osedu.org/licenses/ECL-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 tufts.vue;

import tufts.Util;

import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.awt.Dimension;

import tufts.vue.MapDropTarget.DropHandler;

import edu.tufts.vue.ontology.ui.TypeList;

/**
 * implements java.awt.datatransfer.Transferable for LWComponent(s)
 *
 * @version $Revision: 1.5 $ / $Date: 2010-02-03 19:17:40 $ / $Author: mike $
 * @author Scott Fraize
 */
public class LWTransfer implements Transferable {

    private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(LWTransfer.class);    
    
    private static boolean isLocalDrop;

    // hack for working around java drop-handler bug that requests all byte representations no matter what
    public static void markAsLocal(boolean t) {
        isLocalDrop = t;
    }
    
    private static final DataFlavor DefaultFlavors[] = {
        LWComponent.DataFlavor,
        DataFlavor.stringFlavor,
        DataFlavor.imageFlavor,
        DropHandler.DataFlavor
        // TypeList.DataFlavor // commented out 2009-06-24 SMF
        //URLFlavor, // try text/uri-list
    };


    private final LWComponent LWC;
    private final boolean isSelection;

    private final DataFlavor[] supportedFlavors;

    public LWTransfer(LWComponent c, boolean selection) {
        LWC = c;
        isSelection = selection;
        if (c.hasClientData(DropHandler.class)) {
            supportedFlavors = DefaultFlavors.clone();
        } else {
            supportedFlavors = new DataFlavor[3];
            System.arraycopy(DefaultFlavors, 0, supportedFlavors, 0, 3);
        }
    }
    
    public LWTransfer(LWComponent c) {
        this(c, false);
    }

    public DataFlavor[] getTransferDataFlavors() {
        return supportedFlavors;
    }
            
    public boolean isDataFlavorSupported(final DataFlavor flavor)
    {
        if (DEBUG.DND) Log.debug("isDataFlavorSupported, flavor=" + flavor);
                
        if (flavor == null)
            return false;

        if (flavor == LWComponent.DataFlavor && LWC instanceof LWMap) {
            // prevent the dropping of the entire map onto itself
            return false;
        }

        if (isLocalDrop && flavor == DataFlavor.imageFlavor) {
            // we never need raw image data when dropping onto a local source
            return false;
        }

        if (flavor == DropHandler.DataFlavor && LWC.hasClientData(DropHandler.class)) {
            return true;
        }
                
        //                 if (flavor == LWComponent.Producer.DataFlavor && LWC.hasClientData(LWComponent.Producer.class)) {
        //                     // note this is a bit convoluted: the producer is currently only passed within an
        //                     // LWComponent.  This needn't be true, but our current usage depends on it.
        //                     // (We want an LWComponent available as the drag image).  We could easily
        //                     // extend LWTransfer to support a producer directly and allowing for a null LWC.
        //                     return true;
        //                 }
                
        if (flavor == Resource.DataFlavor && LWC.getResource() == null)
            return false;

        // TODO BUG: support for TypeList is being incorrectly reported as true here
        // even if there is no old-style meta-data!
                
        for (int i = 0; i < supportedFlavors.length; i++)
            if (flavor.equals(supportedFlavors[i]))
                return true;

        return false;
    }

    public synchronized Object getTransferData(DataFlavor flavor)
        throws UnsupportedFlavorException, java.io.IOException
    {
        //tufts.Util.printStackTrace("GTD " + flavor.getHumanPresentableName());
        // Note: the sun transfer handler (for drops to java) always requests
        // all data for all types during a drop, which is a horrible waste in
        // that we need to create a whole image every time, even it it's just
        // the resource being dropped.  If want to optimize this out, would need
        // to create our own Image class that delays creation of the actual
        // image until something is requested of it.  Drops to the OS, at
        // least in the case of MacOSX are smart and only request data for
        // what is ultimately dropped.

        // @see sun.awt.datatransfer.DataTransferer

        //Log.info("getTransferData " + Util.tags(flavor), new Throwable("HERE"));
        final String type = isLocalDrop ? "LOCAL " : "REMOTE ";

        Log.info("getTransferData " + type + flavor);
                
        if (DEBUG.DND && DEBUG.META) System.err.print("<LWTransfer.getTransferData("
                                                      + flavor.getHumanPresentableName() + ")>");
        //if (DEBUG.DND && DEBUG.META)
        //Log.debug("getTransferData(" + flavor.getHumanPresentableName() + ")", new Throwable("HERE"));
        
        Object data = null;
        
                
        if (TypeList.DataFlavor.equals(flavor)) {
            try {
                data = LWC.getMetadataList().getMetadata().get(0).getObject();
            } catch (Throwable t) {
                Log.warn("getTransferData: " + flavor + "; " + t);
            }
        }
        else if (DataFlavor.stringFlavor.equals(flavor)) {

            if (LWC != null && LWC.getClientData(DataFlavor.stringFlavor) != null) {
                return LWC.getClientData(DataFlavor.stringFlavor);
            }

                    
            String s = null;
            if (LWC instanceof LWMap && ((LWMap)LWC).getFile() != null)
                s = ((LWMap)LWC).getFile().getAbsolutePath();
            if (LWC.hasResource())
                s = LWC.getResource().getSpec();
            if (s == null && LWC.hasLabel())
                s = LWC.getLabel();
            if (s == null && LWC.hasNotes())
                s = LWC.getNotes();
            if (s == null)
                s = LWC.getDisplayLabel();
            //if (s != null) s += "\n";
            data = s;
                    
        }
        else if (DataFlavor.imageFlavor.equals(flavor)) {

            //data = new LazyImage(new LazyImage.Producer() {
            //       public Image getImage() { return LWC.getAsImage(); }
            //});

            // The LazyImage method attempted above is no good for avoiding the creation of large
            // images unless they're needed.  (1) sun.awt.image.SurfaceManager.getManager assumes
            // all images are BufferedImage's with a cast (!)  And (2): The Java VM impl data
            // transfer code appears to request the byte representations of ALL THE SUPPORT FLAVORS
            // whenever a drop happens -- not just the flavor requested.  (Which leads us to the
            // above cast failure, and this whole problem in the first place).  E.g., this image
            // data should ONLY be requested if, say, we drop the image on the desktop or into
            // another app that will import the raw image data.  But for some reason the impls
            // (including 1.5 & 1.6, at least Apple -- need to check Windows) request all possible
            // data, which can be a big issue if you happen to be dragging a very large image, but
            // are not interested in actually ever obtain all it's bytes.  (E.g., very slow to copy
            // all those bytes, oftening hanging or even crashing the internal Apple code).
                    
            if (isLocalDrop) {
                return null;
                //throw new java.io.IOException("raw image data never produced for local drops");
            } else {
                return LWC.getAsImage();
            }
                    
        }
        else if (DropHandler.DataFlavor.equals(flavor)) {
                    
            data = LWC.getClientData(DropHandler.class);
                    
        //} else if (LWComponent.Producer.DataFlavor.equals(flavor)) {

            //data = LWC.getClientData(LWComponent.Producer.class);
                    
        }
        else if (LWComponent.DataFlavor.equals(flavor)) {

                    
            final java.util.Collection duplicates;
            if (isSelection) {
                duplicates = tufts.vue.Actions.duplicatePreservingLinks(LWC.getChildren());
            }
            else if (LWC instanceof LWMap) {
                // don't send the actual map just yet...
                duplicates = tufts.vue.Actions.duplicatePreservingLinks(LWC.getChildren());
            }
            
//             else if (LWC.hasClientData(LWComponent.Producer.class)) {
//                 Util.printStackTrace("deprecated use of node producer");
//                 //duplicates = LWC.getClientData(LWComponent.ListFactory.class).produceNodes(null);
//                 duplicates = LWC.getClientData(LWComponent.Producer.class).produceNodes(null);
//             }
                    
            else if (LWC.hasFlag(LWComponent.Flag.INTERNAL) /*&& LWC.getClientProperty(Field.class) != null*/) {
                // an INTERNAL flagged LWComponent during drag is merely a bucket for other
                // information, and we can return it directly.
                return LWC;
            }
            else {
                duplicates = java.util.Collections.singletonList(LWC.duplicate());
            }
                        
            data = duplicates;
                    
        } else if (Resource.DataFlavor.equals(flavor)) {

            data = LWC.getResource();

            //                 } else if (URLFlavor.equals(flavor) && LWC.getResource() instanceof URLResource) {

            //                     data = ((URLResource)LWC.getResource()).asURL();

        } else {
            throw new UnsupportedFlavorException(flavor);
        }
        
        //if (DEBUG.DND) out("LWTransfer: returning " + Util.tag(data));

        return data;
    }
}
    
