/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2003-2006 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License 1.0 which accompanies this     **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl - initial API and implementation (bpasero@rssowl.org)         **
 **                                                                          **
 **  **********************************************************************  */

package net.sourceforge.rssowl.controller.dnd;

import net.sourceforge.rssowl.model.TabItemData;
import net.sourceforge.rssowl.util.shop.WidgetShop;

import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;

import java.util.ArrayList;
import java.util.List;

/**
 * This class impements basic Drag and Drop support to the CTabFolder. It allows
 * the user to drag a tabitem that displays a newsfeed and drop it in a category
 * of the favorites tree. On success RSSOwl will create a favorite out of the
 * URL and title of the dragged tabitem. It is also possible to move the
 * CTabItem to a different position in the Tabfolder by dropping it over another
 * CTabItem.
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class NewsTabFolderDND {

  /** Support drag and drop operations */
  private static final int operations = DND.DROP_MOVE | DND.DROP_COPY;

  /** Array of transfer types */
  private static final Transfer[] types = new Transfer[] { TextTransfer.getInstance() };

  private DragSource source;
  private DropTarget target;
  FavoritesTreeDND rssOwlFavoritesTreeDND;
  CTabFolder tabFolder;

  /**
   * Instantiate a new NewsTabFolderDND
   * 
   * @param tabFolder The CTabFolder to apply basic drag and drop support
   * @param rssOwlFavoritesTreeDND A reference to the drag and drop class of the
   * favorites tree. This is needed because the drop target will be the tree
   */
  public NewsTabFolderDND(CTabFolder tabFolder, FavoritesTreeDND rssOwlFavoritesTreeDND) {
    this.tabFolder = tabFolder;
    this.rssOwlFavoritesTreeDND = rssOwlFavoritesTreeDND;
    initDragAndDrop();
  }

  /**
   * Dispose the DragSource object
   */
  public void dispose() {
    source.dispose();
    target.dispose();
  }

  /** Create the Drag Source on the tabfolder */
  private void createDragSource() {
    source = new DragSource(tabFolder, operations);
    source.setTransfer(types);
    source.addDragListener(new DragSourceAdapter() {

      /** Reset the DragSourceItem field */
      public void dragFinished(DragSourceEvent event) {
        rssOwlFavoritesTreeDND.setDragSourceItem(null);
      }

      /** Set Feed Title and URL into Data Slot */
      public void dragSetData(DragSourceEvent event) {

        /** The source item is stored in the DND class of the favorites tree */
        TabItemData sourceData = (TabItemData) rssOwlFavoritesTreeDND.getDragSourceItem().getData();

        /** URL and Title as Drag Data */
        String url = "";
        String title = "";

        /** A Newsfeed is being dragged */
        if (sourceData.isFeed()) {
          title = sourceData.getTitle();
          url = sourceData.getUrl();
        }

        /** A Browser is being dragged */
        else if (sourceData.isBrowser()) {
          title = rssOwlFavoritesTreeDND.getDragSourceItem().getText();

          /** Retrieve URL from Browser if available */
          if (sourceData.getRSSOwlBrowserPanel() != null && WidgetShop.isset(sourceData.getRSSOwlBrowserPanel().getBrowser()))
            url = sourceData.getRSSOwlBrowserPanel().getBrowser().getUrl();
        }

        /** Set Data */
        event.data = url + System.getProperty("line.separator") + title;
      }

      /** Reset the DragSourceItem field */
      public void dragStart(DragSourceEvent event) {

        /** Retrieve a valid drag source tabitem */
        CTabItem validDragSourceTabItem = getValidDragSourceTabItem();

        /** TabItem is valid */
        if (validDragSourceTabItem != null) {
          event.doit = true;

          /** Store the item in the DND class of the favorites tree */
          rssOwlFavoritesTreeDND.setDragSourceItem(validDragSourceTabItem);
        }

        /** TabItem is invalid */
        else {
          event.doit = false;
        }
      }
    });
  }

  /** Create the Drop Target on the tabfolder */
  private void createDropTarget() {
    target = new DropTarget(tabFolder, DND.DROP_MOVE);
    target.setTransfer(types);
    target.addDropListener(new DropTargetAdapter() {

      /**
       * Give feedback on drag over event. Only allow to drop source into
       * targets that are categories or favorites.
       */
      public void dragOver(DropTargetEvent event) {
        Item sourceItem = rssOwlFavoritesTreeDND.getDragSourceItem();

        /** TabFolder must be set */
        if (!WidgetShop.isset(tabFolder)) {
          event.detail = DND.DROP_NONE;
          return;
        }

        /** SourceItem must be a CTabItem */
        if (!WidgetShop.isset(sourceItem) || !(sourceItem instanceof CTabItem)) {
          event.detail = DND.DROP_NONE;
          return;
        }

        /** Get the currently hovered CTabItem */
        CTabItem hoveredItem = tabFolder.getItem(tabFolder.toControl(new Point(event.x, event.y)));

        /** Allow op if source is a CTabItem and target is different CTabItem */
        if (WidgetShop.isset(hoveredItem) && hoveredItem != sourceItem)
          event.detail = DND.DROP_MOVE;
        else
          event.detail = DND.DROP_NONE;
      }

      /**
       * Drop the data into the target. This will move the dragged CTabItem to
       * the currently hovered one.
       */
      public void drop(DropTargetEvent event) {
        Item sourceItem = rssOwlFavoritesTreeDND.getDragSourceItem();

        /** TabFolder must be set */
        if (!WidgetShop.isset(tabFolder)) {
          event.detail = DND.DROP_NONE;
          return;
        }

        /** SourceItem must be a CTabItem */
        if (!WidgetShop.isset(sourceItem) || !(sourceItem instanceof CTabItem)) {
          event.detail = DND.DROP_NONE;
          return;
        }

        /** Get the currently hovered CTabItem */
        CTabItem hoveredItem = tabFolder.getItem(tabFolder.toControl(new Point(event.x, event.y)));

        /** Move the dragged item to the hovered item */
        if (WidgetShop.isset(hoveredItem))
          move((CTabItem) sourceItem, hoveredItem);
      }
    });
  }

  /**
   * Init Drag and Drop on the tree widget. Note that the Drop target is created
   * in the DND class of the favorites tree
   */
  private void initDragAndDrop() {
    createDragSource();
    createDropTarget();
  }

  /**
   * Valid source items to drag from the tabfolder are tabs that display a
   * newsfeed
   * 
   * @return CTabItem The dragged tabitem or null if the tab is not valid to be
   * dragged
   */
  CTabItem getValidDragSourceTabItem() {
    CTabItem tabItem = tabFolder.getSelection();

    /** User has not selected a tab */
    if (tabItem == null)
      return null;

    /** Return tabItem to dragg */
    return tabItem;
  }

  /**
   * Move the given CTabItem to the new position.
   * 
   * @param from The CTabItem to move.
   * @param to The new location of the CTabItem to move.
   */
  void move(CTabItem from, CTabItem to) {
    int fromIndex = tabFolder.indexOf(from);
    int toIndex = tabFolder.indexOf(to);

    /** Array of all CTabItems */
    CTabItem items[] = tabFolder.getItems();

    /** Define the new Order to CTabItems */
    List newOrder = new ArrayList();
    for (int i = 0; i < items.length; i++)
      newOrder.add(items[i]);

    newOrder.remove(fromIndex);
    newOrder.add(toIndex, from);

    /** Cache the Data of the new order */
    String text[] = new String[items.length];
    String tooltip[] = new String[items.length];
    Font font[] = new Font[items.length];
    Image image[] = new Image[items.length];
    Control control[] = new Control[items.length];
    Object data[] = new Object[items.length];
    for (int i = 0; i < newOrder.size(); i++) {
      CTabItem newTab = (CTabItem) newOrder.get(i);
      text[i] = newTab.getText();
      tooltip[i] = newTab.getToolTipText();
      font[i] = newTab.getFont();
      image[i] = newTab.getImage();
      control[i] = newTab.getControl();
      data[i] = newTab.getData();
    }

    /** Apply the new Order to the TabFolder */
    for (int i = 0; i < items.length; i++) {
      items[i].setText(text[i]);
      items[i].setToolTipText(tooltip[i]);
      items[i].setFont(font[i]);
      items[i].setImage(image[i]);
      items[i].setControl(control[i]);
      items[i].setData(data[i]);

      /** Update Data if required */
      if (data[i] instanceof TabItemData) {
        TabItemData tabItemData = (TabItemData) data[i];

        /** Let NewsTable know about the new CTabItem */
        if (tabItemData.getNewsTable() != null)
          tabItemData.getNewsTable().setTabItem(items[i]);

        /** Let the BrowserPanel know about the new CTabItem */
        else if (tabItemData.getRSSOwlBrowserPanel() != null) {
          if (WidgetShop.isset(tabItemData.getRSSOwlBrowserPanel().getBrowser()))
            tabItemData.getRSSOwlBrowserPanel().getBrowser().setData(items[i]);
          tabItemData.getRSSOwlBrowserPanel().setTabItem(items[i]);
        }
      }
    }

    /** Keep Selection */
    tabFolder.setSelection(to);
  }
}