/*
 * BIQuerybuilder.java
 *
 * Copyright © 1994-2004, Oracle. All rights reserved. Oracle grants
 * you ("Licensee") a non-exclusive, royalty-free, worldwide license to use,
 * modify and redistribute this software in source and binary code form, provided
 * that (i) this copyright notice and license appear on all copies of the
 * software; (ii) Licensee does not utilize the software in a manner disparaging
 * to Oracle; and (iii) Licensee does not utilize the software in a manner that
 * violates the terms of any Licensee agreement with Oracle.
 */
package samples.javaclient;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeSelectionModel;

import oracle.dss.crosstab.Crosstab;
import oracle.dss.dataSource.common.Query;
import oracle.dss.datautil.QueryAccessDimensionModel;
import oracle.dss.datautil.client.DataUtilException;
import oracle.dss.datautil.gui.StandardDialog;
import oracle.dss.datautil.gui.dimensionList.DimensionListDialog;
import oracle.dss.dataView.ComponentHandle;
import oracle.dss.dataView.Dataview;
import oracle.dss.dataView.PopupManager;
import oracle.dss.metadataManager.common.MetadataManagerException;
import oracle.dss.metadataManager.common.MDDimension;
import oracle.dss.metadataManager.common.MDMeasure;
import oracle.dss.metadataManager.common.MM;
import oracle.dss.queryBuilder.QueryBuilder;
import oracle.dss.queryBuilder.SavedSelectionsPanel;
import oracle.dss.selection.Selection;
import oracle.dss.selection.step.FamilyStep;
import oracle.dss.selection.step.InvalidStepArgException;
import oracle.dss.selection.step.MemberStep;
import oracle.dss.selection.step.SavedSelectionStep;
import oracle.dss.selection.step.Step;
import oracle.dss.util.gui.layout.CrosstabLayout;

/**
 * This class shows how to use the QueryBuilder to modify a Crosstab
 */
public class BIQuerybuilder extends JFrame {
  private Crosstab m_crosstab;
  private JMenuItem m_queryBuilderMenu;
  private JMenuItem m_sampleMenu;
  private Query m_query;
  private QueryBuilder m_queryBuilder;
  private BIFrame m_frame;
  private JPanel m_panel;

  // Dimensions and hierarchies
  private String m_strChannelDimension;
  private String m_strChannelHierarchy;
  private String m_strGeographyDimension;
  private String m_strGeographyHierarchy;
  private String m_strProductDimension;
  private String m_strProductHierarchy;
  private String m_strTimeDimension;
  private String m_strTimeHierarchy;

  // This class will be used to implement right-click context menus.
  private class QueryPopupManager implements PopupManager {
    private JPopupMenu m_menu;

    /**
     * Constructor
     */
    public QueryPopupManager() {
      m_menu = new JPopupMenu();

      // Add menu items
      JMenuItem geogItem = new JMenuItem("Insert Geography Saved Selection");
      m_menu.add(geogItem);
      geogItem.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent menuEvent) {
          insertGeographySavedSelection();
        }
      });
      JMenuItem prodItem = new JMenuItem("Insert Product Members");
      m_menu.add(prodItem);
      prodItem.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent menuEvent) {
          insertProductMembers();
        }
      });
    }

    /**
     * Called on a manager to inform it the data source
     * has changed.
     */
    public void dataSourceChanged() {
      // Not used in this sample, but required to implement the
      // PopupManager interface
    }

    /**
     * Return a complete <code>JPopupMenu</code>.  The implementor
     * of this method is responsible for processing its own calls
     * from this popup menu.
     *
     * @param  dataview a <code>Dataview</code> value that represents
     *         the Dataview associated with this popup.
     * @param  componentHandle a <code>ComponentHandle</code> value that
     *         represents the component handle associated with this
     *         popup.
     *
     * @return an entire <code>JPopupMenu</code> structure, or
     * <code>null</code>, in which case <code>getPopupStrings</code>
     * will be called.
     */
    public JPopupMenu getPopupMenu(Dataview dataview,
      ComponentHandle componentHandle) {
      return m_menu;
    }
  }

  // This class allows the user to select a Saved Selection
  private class SavedSelectionsDialog extends StandardDialog {
    private SavedSelectionsPanel m_fp;

    /**
     * Constructor
     *
     * @param parent The parent Frame
     * @param queryBuilder The QueryBuilder object
     * @param strDimension The dimension for which saved selections should be shown
     */
    public SavedSelectionsDialog(Frame parent, QueryBuilder queryBuilder, String strDimension) {
      super(parent, "Select a Saved Selection", true, null);

      // Only enable OK and CANCEL buttons
      setButtonMask(BUTTON_OK | BUTTON_CANCEL);

      // Create and add a single-selection SavedSelectionsPanel
      m_fp = new SavedSelectionsPanel(queryBuilder, strDimension);
      m_fp.getTree().getSelectionModel().setSelectionMode(
        TreeSelectionModel.SINGLE_TREE_SELECTION);
      m_fp.getTree().addTreeSelectionListener(new TreeSelectionListener() {
        public void valueChanged(TreeSelectionEvent e) {
          updateEnabled();
        }
      });
      setContent(m_fp);

      // Disable the OK button when nothing is selected
      setOKButtonEnabled(false);
    }

    /**
     * Get the Saved Selections Panel
     *
     * @returns The Saved Selections Panel
     */
    public SavedSelectionsPanel getSavedSelectionsPanel() {
      return m_fp;
    }

    /**
     * Determines whether the OK button should be enabled.
     * Returns true if a Saved Selection is currently selected.
     */
    private boolean shouldEnableOKButton() {
      Object[] items = m_fp.getSelectedItems();
      return ((items != null) && (items.length != 0));
    }

    /**
     * Updates the state of the OK button.
     */
    private void updateEnabled() {
      setOKButtonEnabled(shouldEnableOKButton());
    }
  }

  /**
   * Constructor for BIQuerybuilder.
   *
   * @param title The title of the JFrame.
   */
  public BIQuerybuilder(BIFrame parentFrame, String title) {
    super(title);
    m_frame = parentFrame;
    init();
  }

  /**
   * This method creates a new Crosstab, sets the DataSource on the crosstab
   * and adds the Crosstab to the frame.
   */
  protected void createCrosstab() {
    // Create a new Crosstab
    m_crosstab = new Crosstab();
    m_crosstab.setPagingControlVisible(true);
    m_crosstab.setPopupManager(new QueryPopupManager());

    // Create Query
    m_query = m_frame.getDefaultQuery();

    // Get IDs for all dimensions and hierarchies
    String[] strMeasures = m_query.getMeasures();
    if (strMeasures == null || strMeasures.length == 0) {
      return;
    }
    String strSales = strMeasures[0];
    try {
      MDMeasure sales = (MDMeasure) m_frame.getMetadataManager().getMDObject(
        MM.UNIQUE_ID, strSales, MM.MEASURE);
      MDDimension[] mdDimensions = sales.getDimensions();
      for (int i = 0; i < mdDimensions.length; i++) {
        String strName = mdDimensions[i].getName();

        if (strName.equals("SHAWCHANNELS")) {
          m_strChannelDimension = mdDimensions[i].getUniqueID();
          m_strChannelHierarchy =
            mdDimensions[i].getDefaultHierarchy().getUniqueID();
        }
        else if (strName.equals("SHAWGEOGRAPHIES")) {
          m_strGeographyDimension = mdDimensions[i].getUniqueID();
          m_strGeographyHierarchy =
            mdDimensions[i].getDefaultHierarchy().getUniqueID();
        }
        else if (strName.equals("SHAWPRODUCTS")) {
          m_strProductDimension = mdDimensions[i].getUniqueID();
          m_strProductHierarchy =
            mdDimensions[i].getDefaultHierarchy().getUniqueID();
        }
        else if (strName.equals("SHAWT_TIME")) {
          m_strTimeDimension = mdDimensions[i].getUniqueID();
          m_strTimeHierarchy =
            mdDimensions[i].getDefaultHierarchy().getUniqueID();
        }
      }
    }
    catch (MetadataManagerException mme) {
      m_frame.showExceptionDialog(this, mme);
    }

    // Limit the Time universe to 2000
    Selection selection = m_query.findSelection(m_strTimeDimension);
    Vector base = new Vector();
    base.addElement("1804");  // the id for 2000
    FamilyStep fs = new FamilyStep(m_strTimeDimension, m_strTimeHierarchy,
      FamilyStep.OP_DESCENDANTS, base, true);
    selection.removeAllSteps();
    selection.addStep(fs);
    try {
      m_query.setUniverseSelection(m_strTimeDimension, selection);
    }
    catch (Exception e) {
      m_frame.showExceptionDialog(this, e);
    }

    // Attach the crosstab to the query
    m_crosstab.setDataSource(m_query);

    // Add crosstab to the Frame
    m_panel.add(m_crosstab, BorderLayout.CENTER);
    m_panel.invalidate();
    m_panel.validate();
    m_panel.repaint();
  }

  /**
   *  This method first removes crosstab from the frame and 
   *  disables appropriate buttons 
   */
  protected void cleanUp() {
    
    if(m_crosstab != null) {
      m_crosstab.setDataSource(null);
      m_panel.remove(m_crosstab);
      m_crosstab = null;
    }

    m_queryBuilderMenu = null;
    dispose();
    }

  /**
   * Initialize the user interface.
   */
  protected void init() {

    JMenuBar m_menuBar = new JMenuBar();
        
    // create new menu items
    m_sampleMenu = new JMenu("Using querybuilder");
    m_queryBuilderMenu = new JMenuItem("Modify Query");

    m_sampleMenu.add(m_queryBuilderMenu);
    m_menuBar.add(m_sampleMenu);
    
    m_queryBuilderMenu.setMnemonic('Q');

    // Add Listener to m_queryBuilderMenu
    m_queryBuilderMenu.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        runQueryBuilder();
      }
    });

    m_panel = new JPanel();
    m_panel.setLayout(new BorderLayout());
    
    // put all of these into the frame
    setJMenuBar(m_menuBar);
    getContentPane().removeAll();
    getContentPane().add(m_panel, BorderLayout.CENTER);
  
    // add a listener to listen for the window closing event
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        cleanUp();
      }
    });
    
    createCrosstab();

  }

  /**
   * This method invokes the QueryBuilder and allows the user to modify
   * the Query.
   */
  protected void runQueryBuilder() {
    if (m_queryBuilder == null) {
      initQueryBuilder();
    }
    try {
      m_queryBuilder.run();
    }
    catch (DataUtilException e) {
      m_frame.showExceptionDialog(this, e);
    }
  }

  /**
   * This method creates and customizes the QueryBuilder object
   */
  private void initQueryBuilder() {
    m_queryBuilder = new QueryBuilder(this);
    m_queryBuilder.setMode(QueryBuilder.TABBED);

    // Choose the main panels
    m_queryBuilder.setVisiblePanels(QueryBuilder.ITEMS
      | QueryBuilder.DIMENSIONS | QueryBuilder.LAYOUT);

    // Choose the tabs on the DimensionPanel
    m_queryBuilder.setVisibleDimensionPanels(QueryBuilder.MEMBERS
      | QueryBuilder.STEPS | QueryBuilder.PREVIEW);

    // Choose the dimensions that can be edited on the DimensionPanel
    String[] strDimensions = new String[2];
    strDimensions[0] = m_strChannelDimension;
    strDimensions[1] = m_strTimeDimension;
    m_queryBuilder.setVisibleDimensions(strDimensions);

    // Allow apply
    m_queryBuilder.setApplyAllowed(true);

    // Allow the user to switch between dimensions on the DimensionPanel
    m_queryBuilder.setDimensionVisible(true);

    // Disable changing the hierarchy from the DimensionPanel
    m_queryBuilder.setHierarchyVisible(false);

    // Make the QueryBuilder visible
    m_queryBuilder.setVisible(true);

    // Assign the QueryBuilder's query, metadataManager, and layout
    m_queryBuilder.setQuery(m_query);
    m_queryBuilder.setMetadataManager(m_frame.getMetadataManager());
    CrosstabLayout ctLayout = new CrosstabLayout();
    ctLayout.setPagingControlAttributes(m_crosstab);
    ctLayout.setLayoutContext(m_query);
    m_queryBuilder.setViewLayoutPanel(ctLayout);
  }

  /**
   * This method replaces the Geography selection with a Saved Selection step
   * chosen by the user.
   */
  private void insertGeographySavedSelection() {
    if (m_queryBuilder == null) {
      initQueryBuilder();
    }

    // Create a new SavedSelectionsDialog
    SavedSelectionsDialog fd = new SavedSelectionsDialog(this, m_queryBuilder,
      m_strGeographyDimension);

    if (fd.display(this) == StandardDialog.OK) {
      // Replace the geography selection with the SavedSelectionStep
      SavedSelectionStep step = (SavedSelectionStep)(fd.getSavedSelectionsPanel()
        .getSelectedItems())[0];
      try {
        step.setAction(Step.SELECT);
      }
      catch (InvalidStepArgException e) {
        m_frame.showExceptionDialog(this, e);
      }
      Selection selection = m_query.findSelection(m_strGeographyDimension);
      selection.removeAllSteps();
      selection.addStep(step);
      try {
        m_query.applySelection(selection);
      }
      catch (Exception e) {
        m_frame.showExceptionDialog(this, e);
      }
    }
    fd.dispose();
  }

  /**
   * This method replaces the Product selection with members
   * chosen by the user.
   */
  private void insertProductMembers() {
    if (m_queryBuilder == null) {
      initQueryBuilder();
    }

    // Create a new DimensionListDialog
    QueryAccessDimensionModel qadm = new QueryAccessDimensionModel(
      m_query.createQueryAccess(), null, null, m_strProductDimension,
      m_strProductHierarchy, m_frame.getMetadataManager());
    DimensionListDialog dlg = new DimensionListDialog(this, qadm, this, true);

    if (dlg.display() == StandardDialog.OK) {
      // Replace the product selection with the MemberStep
      MemberStep step = dlg.getSelectedMembers();
      try {
        step.setAction(Step.SELECT);
      }
      catch (InvalidStepArgException e) {
        m_frame.showExceptionDialog(this, e);
      }
      Selection selection = m_query.findSelection(m_strProductDimension);
      selection.removeAllSteps();
      selection.addStep(step);
      try {
        m_query.applySelection(selection);
      }
      catch (Exception e) {
        m_frame.showExceptionDialog(this, e);
      }
    }
    dlg.dispose();
  }
}
