/*
 * BIQueryTool.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.servlet;

import java.util.Vector;

import oracle.cabo.ui.UINode;

import oracle.dss.dataSource.client.QueryManager;

import oracle.dss.dataSource.common.Query;

import oracle.dss.metadataManager.client.MetadataManager;

import oracle.dss.metadataManager.common.MDDimension;
import oracle.dss.metadataManager.common.MDHierarchy;
import oracle.dss.metadataManager.common.MDLevel;
import oracle.dss.metadataManager.common.MDMeasure;
import oracle.dss.metadataManager.common.MM;

import oracle.dss.selection.OlapQDR;
import oracle.dss.selection.Selection;

import oracle.dss.selection.step.InvalidStepArgException;
import oracle.dss.selection.step.Step;
import oracle.dss.selection.step.MeasValStep;
import oracle.dss.selection.step.TopBottomStep;

import oracle.dss.thin.beans.BIConstants;
import oracle.dss.thin.beans.QueryParameterProvider;
import oracle.dss.thin.beans.ThinBeanUI;
import oracle.dss.thin.beans.ThinException;

import oracle.dss.thin.beans.dataView.ThinDataviewCommon;

import oracle.dss.thin.beans.tools.BaseViewTool;

import oracle.dss.util.DataAccess;
import oracle.dss.util.DataDirector;
import oracle.dss.util.DataSource;
import oracle.dss.util.QDR;
import oracle.dss.util.SliceOutOfRangeException;


/**
 * BIQueryTool is a ViewTool implementation used to modify the View's Query
 * by applying simple selections such as: Top 10 Products based on Sales,
 * or Products where Sales > 10,000.
 */
public class BIQueryTool extends BaseViewTool {
  /**
    * Constructor.
    */
    public BIQueryTool() {
      super();

      // Set the default ThinBean name
      setThinBeanName("queryTool");

      // The display name is used when this tool is displayd on the ViewToolbar
      setDisplayName("Filter");
    }

    /**
     * An application's servlet must call handleEvent, when a <code>ThinBean<code/>
     * event triggered the servlet request.  To determine the correct <code>ThinBean</code>
     * to call handleEvent() on, the application should check the "source" query parameter,
     * which will be the ThinBeanName of the <code>ThinBean</code> that rendered the URL/Form
     * in HTML and which also needs to handle the event.
     *
     * The application can also choose to call handleEvent on all of its
     * ThinBeanUI components and check the return value of handleEvent() to determine
     * which component has handled the event.
     *
     * Each ThinBeanUI component needs to check the source parameter to make sure that
     * it matches their ThinBeanName.  If it does not match, then the event should not
     * be processed.
     *
     * @param provider An instance of a QueryParameterProvider that returns decoded and
     *                 unescaped query parameter values from HTTPServletRequests
     *
     * @return EVENT_NOT_HANDLED, EVENT_HANDLED, or EVENT_RESPONSE_DONE
     *
     * @see oracle.dss.thin.beans.ThinBeanUI#EVENT_NOT_HANDLED
     * @see oracle.dss.thin.beans.ThinBeanUI#EVENT_HANDLED
     * @see oracle.dss.thin.beans.ThinBeanUI#EVENT_RESPONSE_DONE
     *
     */
    public int handleComponentEvent(QueryParameterProvider provider) throws ThinException {
      String source = provider.getQueryParameter(BIConstants.SOURCE);
      String event = provider.getQueryParameter(BIConstants.EVENT);

      // Handle event if the source of the event is this thin bean,
      // or the ViewToolbar this tool belongs to
      if (source != null &&
          (source.equals(getThinBeanName()) || source.equals(getToolbarName()))) {
        if (event.equals(QUERY_EVENT)) {
          return handleQueryEvent(provider);
        }
      }
      return ThinBeanUI.EVENT_NOT_HANDLED;
    }

     /**
     * Handles the QUERY_EVENT, specific to the QueryTool
     *
     * @param provider the structure holding the event parameters
     *
     * @return EVENT_NOT_HANDLED if the new Selection could not be applied
     *         EVENT_HANDLED if applying the new Selection was successfull
     *
     * @see oracle.dss.thin.beans.ThinBeanUI#EVENT_NOT_HANDLED
     * @see oracle.dss.thin.beans.ThinBeanUI#EVENT_HANDLED
     */
    public int handleQueryEvent(QueryParameterProvider provider) throws ThinException {
      // Read all the event parameters
      // Save the last selected display parameters for preserving the tool state
      m_dimension = provider.getQueryParameter(DIMENSION_PARAMETER);
      m_hierarchy = provider.getQueryParameter(HIERARCHY_PARAMETER);
      m_level = provider.getQueryParameter(LEVEL_PARAMETER);
      m_measure = provider.getQueryParameter(MEASURE_PARAMETER);
      m_operator = provider.getQueryParameter(OPERATOR_PARAMETER);
      m_number = provider.getQueryParameter(NUMBER_PARAMETER);

      // If the selected level is not null, create the level Vector
      Vector levels = null;
      if (m_level != null && m_level.length() > 0) {
        levels = new Vector();
        levels.addElement(m_level);
      }

      ThinDataviewCommon view = getView();

      if (view != null) {
        // Get the DataSource from the View
        DataSource dataSource = view.getDataSource();

        // The Query modifications we are about to apply can be
        // done only if the View's DataSource supports the Query API
        if (dataSource != null && dataSource instanceof Query) {
          try {
            Query query = (Query)dataSource;

            // Create the step based on the type specified in the request
            Step step = null;

            // Create a new TopBottomStep (for example, "Select Top 10 Products based on Sales")
            if (m_operator.equals(BIQueryTool.OPERATOR_TOP)) {
              step = new TopBottomStep(m_dimension, m_hierarchy, levels, m_measure, TopBottomStep.TOP, new Integer(m_number), false);
            }
            // Create a new TopBottomStep (for example, "Select Bottom 10 Products based on Sales")
            else if (m_operator.equals(BIQueryTool.OPERATOR_BOTTOM)) {
              step = new TopBottomStep(m_dimension, m_hierarchy, levels, m_measure, TopBottomStep.BOTTOM, new Integer(m_number), false);
            }
            // Create a new MeasValStep (for example, "Select Products where Sales > 1,000,000")
            else if (m_operator.equals(BIQueryTool.OPERATOR_GREATER)) {
              step = new MeasValStep(m_dimension, m_measure, m_hierarchy, levels, MeasValStep.OP_GREATER, new Float(m_number));
            }
            // Create a new MeasValStep (for example, "Select Products where Sales < 1,000,000")
            else if (m_operator.equals(BIQueryTool.OPERATOR_LESS)) {
              step = new MeasValStep(m_dimension, m_measure, m_hierarchy, levels, MeasValStep.OP_LESS, new Float(m_number));
            }

            // Since the applied selection will be on a dimension on the row edge of the View
            // to simplify the UI we are assuming that the Measure is qualified by the first column
            // Therefore, we are getting the QDR for the first slice on the column edge
            OlapQDR     olapQDR     = null;
            QDR         columnQDR   = null;

            // Get the current DataAccess from the View
            DataAccess  dataAccess  = view.getModel().getDataAccess();

            if (dataAccess != null) {
              columnQDR = dataAccess.getSliceQDR(DataDirector.COLUMN_EDGE, 0, DataAccess.QDR_WITHOUT_PAGE);
            }

            // Find out the Measure dimension name from the MetadataManager
            String measureDimensionName = null;

            // Get the MetadataManager from the QueryManager
            QueryManager queryManager = (QueryManager)query.getQueryManager();
            MetadataManager metadataManager = queryManager.getMetadataManager();

            if (metadataManager != null) {
              MDDimension measureDimension = metadataManager.getMeasureDimension(null);
              if (measureDimension != null) {
                measureDimensionName = measureDimension.getUniqueID();
              }
            }

            // Create the OlapQDR from the column QDR
            // OlapQDRs are used by Selection steps
            if (columnQDR != null && measureDimensionName != null) {
              olapQDR = new OlapQDR(measureDimensionName, columnQDR.getDimMemberPairs());
              step.setQDR(olapQDR);
            }

            // Create a new Selection for the specified dimension
            Selection selection = new Selection(m_dimension);
            // Set the selected hierarchy on the Selection
            selection.setHierarchy(m_hierarchy);
            // Add the new Step to the Selection
            selection.addStep(step);

            // Apply the new selection to the current Query
            query.applySelection(selection);

            // If the Selection was successfully applied, return ThinBeanUI.EVENT_HANDLED
            return ThinBeanUI.EVENT_HANDLED;
          }
          // Cath any exceptions that may occur
          catch (Exception e) {
            throw new ThinException("Could not handle the Query event", e);
          }
        }
      }
        return ThinBeanUI.EVENT_NOT_HANDLED;
     }

    /**
     * Sets the dimension on the <code>QueryTool</code>.
     * @param dimension String ID
     */
    public void setDimension(String dimension) {
      m_dimension = dimension;
    }

    /**
     * Retrieves the dimension for this <code>QueryTool</code>.
     * @return dimension String ID
     */
    public String getDimension() {
      return m_dimension;
    }

    /**
     * Sets the hierarchy on the <code>QueryTool</code>.
     * @param hierarchy String ID
     */
    public void setHierarchy(String hierarchy) {
      m_hierarchy = hierarchy;
    }

    /**
     * Retrieves the hierarchy for this <code>QueryTool</code>.
     * @return hierarchy String ID
     */
    public String getHierarchy() {
      return m_hierarchy;
    }

    /**
     * Sets the level on the <code>QueryTool</code>.
     * @param level String ID
     */
    public void setLevel(String level) {
      m_level = level;
    }

    /**
     * Retrieves the level for this <code>QueryTool</code>.
     * @return level String ID
     */
    public String getLevel() {
      return m_level;
    }

    /**
     * Sets the measure on the <code>QueryTool</code>.
     * @param measure String ID
     */
    public void setMeasure(String measure) {
      m_measure = measure;
    }

    /**
     * Retrieves the measure for this <code>QueryTool</code>.
     * @return measure String ID
     */
    public String getMeasure() {
      return m_measure;
    }

    /**
     * Sets the operator on the <code>QueryTool</code>.
     * @param operator String ID
     */
    public void setOperator(String operator) {
      m_operator = operator;
    }

    /**
     * Retrieves the operator for this <code>QueryTool</code>.
     * @return operator String ID
     */
    public String getOperator() {
      return m_operator;
    }

    /**
     * Sets the number on the <code>QueryTool</code>.
     * @param number String ID
     */
    public void setNumber(String number) {
      m_number = number;
    }

    /**
     * Retrieves the number for this <code>QueryTool</code>.
     * @return number String ID
     */
    public String getNumber() {
      return m_number;
    }

     /**
     * A convenience method.
     * Retrieves the array of MDMeasure objects representing the
     * Measures of the View's Query
     *
     * @return an array of MDMeasure objects
     */
    protected MDMeasure[] getQueryMeasures() throws Exception {

      // If DataSource supports Query API, proceed
      if (getView() != null && getView().getDataSource() != null
            && getView().getDataSource() instanceof Query) {

        Query query = (Query)getView().getDataSource();

        // Get the MetadataManager from the QueryManager
        QueryManager queryManager = (QueryManager)query.getQueryManager();
        MetadataManager metadataManager = queryManager.getMetadataManager();

        if (metadataManager != null) {
          // Get the Query measures
          String[] measures = query.getMeasures();
          MDMeasure[] mdMeasures = new MDMeasure[measures.length];

          for (int i=0; i< measures.length; i++) {
            mdMeasures[i] = metadataManager.getMeasure(MM.UNIQUE_ID, measures[i]);
          }

          return mdMeasures;
        }
      }
      return null;
    }

    /**
     * A convenience method.
     * Retrieves the array of MDLevel objects representing the
     * Levels of the specified hierarchy.
     *
     * @param hierarchy the unique name of the hierarchy
     *
     * @return an array of MDLevel objects
     */
    public MDLevel[] getHierarchyLevels(String hierarchy) throws Exception {

      // If DataSource supports Query API, proceed
      if (getView() != null && getView().getDataSource() != null
            && getView().getDataSource() instanceof Query) {

        Query query = (Query)getView().getDataSource();

        // Get the MetadataManager from the QueryManager
        QueryManager queryManager = (QueryManager)query.getQueryManager();
        MetadataManager metadataManager = queryManager.getMetadataManager();

        if (metadataManager != null) {
          // Get the current hierarchy from the MetadataManager
          MDHierarchy hierarchyObj = metadataManager.getHierarchy(MM.UNIQUE_ID, hierarchy);
          if (hierarchyObj != null) {
            // Get the levels in the current hierarchy
            return (hierarchyObj.getLevels());
          }
        }
      }
      return null;
    }

     /**
     * Retrieves the UINode used to render this ViewTool
     *
     * @return UINode used to render this view tool
     *
     */
    public UINode getContentUINode() {

      if (m_UINode == null) {
        m_UINode = new BIQueryToolBean("", this);
      }
      return m_UINode;
    }

    //////////////
    // Constants
    //////////////

    // Constants
    public static final String QUERY_TOOL_NAME  = "QueryTool";

    // Event types
    public static final String QUERY_EVENT = "query";

    // Event parameters
    public static final String DIMENSION_PARAMETER = "D";
    public static final String HIERARCHY_PARAMETER = "H";
    public static final String LEVEL_PARAMETER     = "L";
    public static final String MEASURE_PARAMETER   = "M";
    public static final String OPERATOR_PARAMETER  = "O";
    public static final String NUMBER_PARAMETER    = "N";

    // Operator constants
    public static final String OPERATOR_TOP        = "T";
    public static final String OPERATOR_BOTTOM     = "B";
    public static final String OPERATOR_GREATER    = "G";
    public static final String OPERATOR_LESS       = "L";

    //////////////
    // Members
    //////////////

    protected String            m_dimension         = null;
    protected String            m_hierarchy         = null;
    protected String            m_level             = null;
    protected String            m_measure           = null;
    protected String            m_operator          = null;
    protected String            m_number            = null;
    protected UINode            m_UINode            = null;
}
