package org.autoplot;

import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.das2.graph.DasCanvasComponent;
import org.das2.graph.DasPlot;
import org.das2.util.LoggerManager;
import org.autoplot.bookmarks.Bookmark;
import org.autoplot.dom.Plot;
import org.autoplot.dom.PlotElement;
import org.autoplot.layout.LayoutConstants;
import org.autoplot.datasource.DataSetSelector;

/**
 * DropTarget allows URIs to be dropped onto plots of the page.
 * @author jbf
 */
public class UriDropTargetListener implements DropTargetListener {
    
    private static final Logger logger= LoggerManager.getLogger("autoplot.gui.droptarget");
    
    DataSetSelector dss;
    ApplicationModel model;
    List<String> supportedFlavors;

    public UriDropTargetListener(DataSetSelector dss, ApplicationModel model) {
        this.model = model;
        this.dss = dss;
        supportedFlavors= new ArrayList<>();
        supportedFlavors.add("text/uri-list;class=java.lang.String");
        supportedFlavors.add("application/x-java-url;class=java.net.URL");
        supportedFlavors.add("application/x-java-file-list;class=java.util.List");
        //TODO: make sure this list is consistent with getURI code, and eventually make into loop.
    }

    /**
     * get the URI when a reference is dropped on to Autoplot.  This is quite
     * platform-specific, how the drop appears, and a number of different 
     * transfer types are queried to see if a URI can be found.
     * 
     * @param dtde the drop target.
     * @return the URI or null.
     */
    private String getURI( DropTargetDropEvent dtde ) {

        boolean haveAcceptedDrop= false;
        Bookmark item = null;
        List<Bookmark> items = null;
        if ( logger.isLoggable(Level.FINE) ) {
            for ( DataFlavor df: dtde.getCurrentDataFlavors() ) {
                logger.log(Level.FINE, "drop data flavor: {0} {1}", new Object[]{df.getMimeType(), df.getHumanPresentableName()});
            }
        }
        if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            try {
                dtde.acceptDrop(DnDConstants.ACTION_COPY);
                haveAcceptedDrop= true;
                logger.fine("looking, try using DataFlavor.stringFlavor to get uri");
                String data = ((String) dtde.getTransferable().getTransferData(DataFlavor.stringFlavor)).trim();
                logger.log(Level.FINER, "got string data: {0}", data);
                if (data.length() > 19 && data.startsWith("<bookmark-list")) {
                    items = Bookmark.parseBookmarks(data);
                } else if (data.length() > 14 && data.startsWith("<bookmark")) {
                    item = Bookmark.parseBookmark(data);
                } else {
                    item = new Bookmark.Item(data);
                }
            } catch ( Exception ex) {
                logger.log(Level.FINE, "unable to get text/uri-list: {0}", ex.getMessage());
            }
        }

        if ( item==null ) {
            try {
                DataFlavor df = new DataFlavor("text/uri-list;class=java.lang.String");
                logger.fine("looking, try text/uri-list;class=java.lang.String");
                if ( dtde.isDataFlavorSupported(df) ) {
                    if ( !haveAcceptedDrop ) {
                        dtde.acceptDrop(DnDConstants.ACTION_COPY);
                        haveAcceptedDrop= true;
                    }
                    String data = String.valueOf( dtde.getTransferable().getTransferData(df) );
                    logger.log(Level.FINER, "got string data: {0}", data);
                    if ( data!=null ) {
                        item= new Bookmark.Item( data );
                    }
                } else {
                    logger.finer("not supported");
                }
            } catch ( Exception ex) {
                logger.log(Level.FINE, "unable to get text/uri-list: {0}", ex.getMessage());
            }
        }

        if ( item==null ) { // how to do the drop on a Mac???     
            try {
                DataFlavor df = new DataFlavor("application/x-java-url;class=java.net.URL");
                logger.fine("looking, try application/x-java-url;class=java.net.URL");
                if ( dtde.isDataFlavorSupported( df ) ) {
                    logger.fine("data flavor application/x-java-url;class=java.net.URL supported");
                    if ( !haveAcceptedDrop ) {
                        dtde.acceptDrop(DnDConstants.ACTION_COPY);
                        haveAcceptedDrop= true;
                    }
                    String data = String.valueOf( dtde.getTransferable().getTransferData(df) );
                    logger.log(Level.FINER, "got string data: {0}", data);
                    if (data.startsWith("file://localhost/")) {
                        data= data.substring(16); // mac at least does this...
                    }
                    item= new Bookmark.Item( data );
                } else {
                    logger.finer("not supported");
                }
            } catch ( Exception ex ) {
                logger.log(Level.FINE, "unable to get application/x-java-url: {0}", ex.getMessage());
            }
        }
        if ( item==null ) {
            try {
                DataFlavor df = new DataFlavor("application/x-java-file-list;class=java.util.List");
                logger.fine("looking, try application/x-java-url;class=java.net.List");
                if ( dtde.isDataFlavorSupported( df ) ) {
                    logger.fine("data flavor application/x-java-url;class=java.net.List supported");
                    if ( !haveAcceptedDrop ) {
                        dtde.acceptDrop(DnDConstants.ACTION_COPY);
                        haveAcceptedDrop= true;
                    }
                    List list= (List)dtde.getTransferable().getTransferData(df);
                    logger.log(Level.FINER, "got list data with number of elements: {0}", list.size() );
                    if ( list.size()==1 ) {
                        String data = list.get(0).toString();
                        if (data.startsWith("file://localhost/")) {
                            data= data.substring(16); // mac at least does this...
                        }
                        item= new Bookmark.Item( data );
                    }
                } else {
                    logger.finer("not supported");
                }
            } catch (Exception ex) {
                logger.log(Level.FINE, "unable to get application/x-java-file-list: {0}", ex.getMessage());
            }
        }

        String uri=null;
        if ( item != null ) {
            if ( item instanceof Bookmark.Item ) {
                uri= ((Bookmark.Item)item).getUri();
            } else {
                model.showMessage( "only one URI can be dropped", "only one URI", JOptionPane.WARNING_MESSAGE);
            }
        } else if ( items!=null ) {
            model.showMessage( "only one URI can be dropped", "only one URI", JOptionPane.WARNING_MESSAGE);
        } else {
            model.showMessage("couldn't find URI in drop target", "no URI", JOptionPane.WARNING_MESSAGE);
        }
        return uri;

    }

    @Override
    public void dragEnter(DropTargetDragEvent dtde) {

        Object s= dtde.getSource();

        DasCanvasComponent cc=null;
        if ( s instanceof DropTarget ) {
            Component c= ((DropTarget)s).getComponent();
            if ( c instanceof DasCanvasComponent ) cc= (DasCanvasComponent)c;
        }
        if ( cc instanceof DasPlot ) {
            if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                dtde.acceptDrag(DnDConstants.ACTION_COPY);
            } else {
                for ( String ss: supportedFlavors ) {
                    try {
                        DataFlavor df= new DataFlavor(ss);
                        dtde.isDataFlavorSupported(df);
                    } catch (ClassNotFoundException ex) {
                        logger.log(Level.FINE, null, ex);
                    }
                }
            }
        }

    }

    @Override
    public void dragOver(DropTargetDragEvent dtde) {
    }

    @Override
    public void dropActionChanged(DropTargetDragEvent dtde) {
    }

    @Override
    public void dragExit(DropTargetEvent dte) {
    }

    @Override
    public void drop(DropTargetDropEvent dtde) {
        
        String uri= getURI( dtde );

        if (uri != null) {

            DasCanvasComponent cc=null;

            Object s= dtde.getSource();

            if ( s instanceof DropTarget ) {
                Component c= ((DropTarget)s).getComponent();
                if ( c instanceof DasCanvasComponent ) cc= (DasCanvasComponent)c;
            }

            Plot plot;
            if ( cc instanceof DasPlot ) {
                plot= model.dom.getController().getPlotFor(cc);
                if ( plot==null ) plot= null;
            } else {
                plot= null;
            }

            if ( plot==null ) {
                DasCanvasComponent dcc=  model.getCanvas().getCanvasComponentAt( dtde.getLocation().x, dtde.getLocation().y-50  );
                if ( dcc!=null ) {
                    plot= model.dom.getController().getPlotFor(dcc);
                    plot= model.dom.getController().addPlot( plot, LayoutConstants.BELOW );
                } else {
                    dcc=  model.getCanvas().getCanvasComponentAt( dtde.getLocation().x, dtde.getLocation().y+50  );
                    if ( dcc!=null ) {
                        plot= model.dom.getController().getPlotFor(dcc);
                        plot= model.dom.getController().addPlot( plot, LayoutConstants.ABOVE );
                    }
                }

                if ( plot==null ) {
                    model.showMessage("URIs may only be dropped on plots, just above, or just below", "not plot target", JOptionPane.WARNING_MESSAGE);
                } else {
                    PlotElement pe= model.dom.getController().addPlotElement( plot, null );
                    model.dom.getController().setPlotElement(pe);
                    dss.setValue(uri);
                    dss.maybePlot(true);
                }
                
            } else {

                final List<PlotElement> pe = model.dom.getController().getPlotElementsFor(plot);
                if (pe.isEmpty()) {
                    model.showMessage("no plot elements here", "no plot elements", JOptionPane.WARNING_MESSAGE);
                    return;
                }

                model.dom.getController().setPlotElement(pe.get(0)); // set the focus

                dss.setValue(uri);
                dss.maybePlot(true);
            }

        }
    }

}