/*
 * @(#)MultiShowView.java  1.0  2009-12-20
 * 
 * Copyright (c) 2009 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.multishow;

import ch.randelshofer.multishow.MultiShowApplicationModel;
import ch.randelshofer.util.PropertyModel;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Iterator;
import java.util.LinkedList;
import javax.activation.CommandInfo;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import org.jhotdraw.app.AbstractView;
import org.jhotdraw.app.Disposable;
import org.jhotdraw.app.View;
import org.jhotdraw.gui.JFileURIChooser;
import org.jhotdraw.gui.URIChooser;
import org.jhotdraw.gui.Worker;

/**
 * MultiShowView.
 *
 * @author Werner Randelshofer
 * @version 1.0 2009-12-20 Created.
 */
public class MultiShowView extends AbstractView {

    public final static String COMMAND_OBJECT_PROPERTY = "commandObject";
    public final static String SELECTED_COMMAND_INFO_PROPERTY = "selectedCommandInfo";
    private Object commandObject;
    private CommandInfo[] commandInfos;
    private CommandInfo selectedInfo;
    /**
     * The drop target listener is created lazily.
     */
    private DropTargetListener dropTargetListener;
    /**
     * Holds dialogs that are associated to this project.
     */
    private LinkedList<JDialog> dialogs = new LinkedList<JDialog>();
    /** View properties. */
    private PropertyModel viewProperties;
    /** The title, as it was set by the Application. */
    private String givenTitle;
    /** The full title. */
    private String fullTitle;
    
    private class Handler implements PropertyChangeListener {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (ch.randelshofer.view.View.REQUEST_INFO_UPDATE_PROPERTY.equals(evt.getPropertyName())) {
                firePropertyChange(evt.getPropertyName(), false,true);
            }
        }
        
    }
    private Handler handler=new Handler();

    /** Creates new form MultiShowView */
    public MultiShowView() {
    }

    @Override
    public void init() {
        super.init();
        initComponents();
        new DropTarget(this, getDropTargetListener());
        MultiShowApplicationModel msam = ((MultiShowApplicationModel) getApplication().getModel());
        viewProperties = (PropertyModel) msam.getViewProperties().clone();
        viewProperties.setForwardTarget(msam.getViewProperties());
        viewProperties.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equals("title")) {
                    updateFullTitle();
                }
            }
        });
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setLayout(new java.awt.BorderLayout());
    }// </editor-fold>//GEN-END:initComponents

    @Override
    public void clear() {
        //
    }

    @Override
    public void write(URI uri, URIChooser c) throws IOException {
        //
    }

    @Override
    public void read(URI uri, URIChooser openChooser) throws IOException {
        //
        FileFilter filter = (openChooser == null) ? null : ((JFileURIChooser) openChooser).getFileFilter();
        CommandInfo info = getRegistry().getCommandInfo(filter);
        viewAs(uri, info);
    }

    /**
     * Displays the project using the view indicated by
     * the CommandInfo object.
     * By convention this method is never called on the AWT Event-Dispatcher Thread.
     */
    protected void viewAs(URI uri, final CommandInfo info) {
        DataHandler dataHandler;
        FileDataSource dataSource = new FileDataSource(new File(uri));

        dataHandler = new DataHandler(dataSource);
        String contentType = dataHandler.getContentType();
        commandInfos = dataHandler.getAllCommands();
        final CommandInfo newValue = (info == null) ? commandInfos[0] : info;
        try {
            final Object cmdObj = newValue.getCommandObject(dataHandler, getClass().getClassLoader());
            //                    result = info.getCommandObject(projectDataHandler, getClass().getClassLoader());
            SwingUtilities.invokeAndWait(new Runnable() {

                @Override
                public void run() {
                    CommandInfo oldInfo = selectedInfo;
                    selectedInfo = newValue;
                    setCommandObject(cmdObj);

                    if (cmdObj instanceof ch.randelshofer.view.View) {
                        ch.randelshofer.view.View view = (ch.randelshofer.view.View) cmdObj;
                        view.setViewProperties(viewProperties);
                        view.setApplicationProperties(((MultiShowApplicationModel) getApplication().getModel()).getApplicationProperties());
                        view.init();
                        revalidate();

                        Window window = SwingUtilities.getWindowAncestor(MultiShowView.this);
                        if (view.hasScrollPane() == false) {
                            window.pack();
                        } else {
                            Dimension pref = window.getPreferredSize();
                            Dimension curr = window.getSize();
                            window.setSize(Math.max(curr.width, pref.width), Math.max(curr.height, pref.height));
                            revalidate();
                        }
                        ((Component) view).requestFocus();
                    } else {
                        viewProperties.setProperty("title", newValue.getCommandName());
                        removeAll();
                        if (cmdObj instanceof Component) {
                            add((Component) cmdObj);
                            ((Component) cmdObj).requestFocus();
                        } else {
                            add(new JLabel(cmdObj == null ? "null" : cmdObj.toString()));
                        }
                        revalidate();
                    }
                    firePropertyChange(SELECTED_COMMAND_INFO_PROPERTY, oldInfo, selectedInfo);
                }
            });
        } catch (final Throwable ex) {
            ex.printStackTrace();
            try {
                SwingUtilities.invokeAndWait(new Runnable() {

                    @Override
                    public void run() {
                        viewProperties.setProperty("title", newValue.getCommandName());
                        CommandInfo oldInfo = selectedInfo;
                        selectedInfo = newValue;
                        removeAll();
                        if (ex instanceof InvocationTargetException) {
                       add(new JLabel(ex.getCause().toString()));
                         } else {
                        add(new JLabel(ex.toString()));
                        }
                        revalidate();
                        firePropertyChange(SELECTED_COMMAND_INFO_PROPERTY, oldInfo, selectedInfo);
                        updateFullTitle();
                    }
                });
            } catch (InterruptedException ex1) {
               ex1.printStackTrace();
            } catch (InvocationTargetException ex1) {
               ex1.printStackTrace();
            }
        }
    }

    private ViewRegistry getRegistry() {
        return ((MultiShowApplicationModel) getApplication().getModel()).getRegistry();
    }

    public void setCommandObject(Object newValue) {
        Object oldValue = commandObject;
        removeAll();
        if (oldValue instanceof Component) {
            ((Component)oldValue).removePropertyChangeListener(handler);
        }
        if (oldValue instanceof Disposable) {
            ((Disposable) oldValue).dispose();
        }
        commandObject = newValue;
        if (newValue instanceof Component) {
            add((Component) newValue);
            ((Component)newValue).addPropertyChangeListener(handler);
        }
        if (newValue instanceof ch.randelshofer.view.View) {
            ((ch.randelshofer.view.View) newValue).addDropTargetListener(getDropTargetListener());
        }
        firePropertyChange(COMMAND_OBJECT_PROPERTY, oldValue, newValue);
    }

    public Object getCommandObject() {
        return commandObject;
    }

    public CommandInfo[] getCommandInfos() {
        return commandInfos == null ? new CommandInfo[0] : commandInfos;
    }

    public CommandInfo getSelectedInfo() {
        return selectedInfo;
    }

    public void addDialog(JDialog dialog) {
        dialogs.add(dialog);
    }

    public void disposeDialogs() {
        while (!dialogs.isEmpty()) {
            ((JDialog) dialogs.removeLast()).dispose();
        }
    }

    @Override
    public void dispose() {
        disposeDialogs();
        removeAll();
        if (commandObject instanceof Disposable) {
            ((Disposable) commandObject).dispose();
        }
        commandObject = null;
        super.dispose();
    }

    @Override
    public void setTitle(String newValue) {
        String oldValue = getTitle();
        givenTitle = newValue;
        updateFullTitle();
    }

    @Override
    public String getTitle() {
        return fullTitle;
    }

    protected void updateFullTitle() {
        String oldValue = fullTitle;
        StringBuffer buf = new StringBuffer();

        if (givenTitle != null) {
            buf.append(givenTitle);
        }
        if (uri != null) {
            if (viewProperties.get("title") != null) {
                buf.append(viewProperties.get("title"));
            }
        }
        fullTitle = buf.toString();
        firePropertyChange(TITLE_PROPERTY, oldValue, fullTitle);
    }

    protected DropTargetListener getDropTargetListener() {
        if (dropTargetListener == null) {
            dropTargetListener = new DropTargetListener() {

                /**
                 * Called when a drag operation has
                 * encountered the <code>DropTarget</code>.
                 * <P>
                 * @param dtde the <code>DropTargetDragEvent</code>
                 */
                @Override
                public void dragEnter(DropTargetDragEvent event) {
                    if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                        event.acceptDrag(DnDConstants.ACTION_COPY);
                    } else {
                        event.rejectDrag();
                    }
                }

                /**
                 * The drag operation has departed
                 * the <code>DropTarget</code> without dropping.
                 * <P>
                 * @param dte the <code>DropTargetEvent</code>
                 */
                @Override
                public void dragExit(DropTargetEvent event) {
                    // No action necessary
                }

                /**
                 * Called when a drag operation is ongoing
                 * on the <code>DropTarget</code>.
                 * <P>
                 * @param dtde the <code>DropTargetDragEvent</code>
                 */
                @Override
                public void dragOver(DropTargetDragEvent event) {
                    if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                        event.acceptDrag(DnDConstants.ACTION_COPY);
                    } else {
                        event.rejectDrag();
                    }
                }

                /**
                 * The drag operation has terminated
                 * with a drop on this <code>DropTarget</code>.
                 * <P>
                 * @param dtde the <code>DropTargetDropEvent</code>
                 */
                @Override
                public void drop(DropTargetDropEvent event) {
                    if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                        event.acceptDrop(DnDConstants.ACTION_COPY);
                        try {
                            final java.util.List files = (java.util.List) event.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
                            // Use invokeLater to work around crashes that
                            // sporadically occur when dropping a QuickTime movie.
                            SwingUtilities.invokeLater(new Runnable() {

                                @Override
                                public void run() {
                                    Iterator iter = files.iterator();
                                    boolean isFirst = true;
                                    while (iter.hasNext()) {
                                        View p2;
                                        if (isFirst) {
                                            isFirst = false;
                                            p2 = MultiShowView.this;
                                        } else {
                                            p2 = getApplication().createView();
                                            getApplication().add(p2);
                                            getApplication().show(p2);
                                        }
                                        final File droppedFile = (File) iter.next();
                                        p2.execute(new Worker() {

                                            @Override
                                            protected Object construct() throws Exception {
                                                read(droppedFile.toURI(), null);
                                                return null;
                                            }

                                            @Override
                                            protected void done(Object value) {
                                                setURI(droppedFile.toURI());
                                                getApplication().addRecentURI(droppedFile.toURI());
                                            }

                                            @Override
                                            protected void failed(Throwable error) {
                                                JOptionPane.showConfirmDialog(
                                                        MultiShowView.this,
                                                        "<html><p><b>Could not load \"" + droppedFile.getName() + "\".</p><br>" + error.getMessage(),
                                                        "MultiShow: Drop Failed",
                                                        JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE);
                                            }
                                        });
                                    }
                                }
                            });
                        } catch (IOException e) {
                            JOptionPane.showConfirmDialog(
                                    MultiShowView.this,
                                    "Could not access the dropped data.",
                                    "MultiShow: Drop Failed",
                                    JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE);
                        } catch (UnsupportedFlavorException e) {
                            e.printStackTrace(); // should never happen since we
                            // accept drop only when it can
                            // provide the required data flavor.
                        }
                    } else {
                        event.rejectDrop();
                    }
                }

                /**
                 * Called if the user has modified
                 * the current drop gesture.
                 * <P>
                 * @param dtde the <code>DropTargetDragEvent</code>
                 */
                @Override
                public void dropActionChanged(DropTargetDragEvent event) {
                    // No action necessary: We are just a view.
                }
            };
        }
        return dropTargetListener;
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
}
