// Copyright (c) 2008, 2023, Oracle and/or its affiliates.
/*---------------------------------------------------------------------*/
/*  DvtHandleManager     singleton object managing handle instances    */
/*---------------------------------------------------------------------*/
/**
 *  @extends DvtObj
 *  @class DvtHandleManager is a singleton object for managing {@link DvtHandle}
 *         instances
 *  <code>
 *  @constructor
 */
var DvtHandleManager = function (chart) {
  this.Init(chart);
};

DvtObj.createSubclass(DvtHandleManager, DvtObj, "DvtHandleManager");

//
// Constants
//
DvtHandleManager.C_SCROLL = "s";
DvtHandleManager.C_RESIZE = "r";

DvtHandleManager.SCROLL_START = "__START__";
DvtHandleManager.SCROLL_SPAN  = "__SPAN__";
DvtHandleManager.SCROLL_TOTAL = "__TOTAL__";
DvtHandleManager.TOK_TYPE     = "__T__";    // type s=scroll/move, r=resize

/**
  *  Object initializer.
  *  @protected
  */
DvtHandleManager.prototype.Init = function (chart) {
    this._Chart = chart;
    this._handleList = new Array();

    this._hPA = null;
    this._hX1L = null;
    this._hY1PA = null;
    this._hY2PA = null;
    this._hOL = null;
    this._hY1L = null;
    this._hY2L = null;

    this._x1rs = null;
    this._y1rs = null;
    this._y2rs = null;
    this._o1rs = null;

    this._fullPlotPoints = null;
    this._fullPlotWidth  = 0;
    this._fullPlotHeight = 0;

    this._fullPlotPointsY2 = null;
    this._fullPlotWidthY2  = 0;
    this._fullPlotHeightY2 = 0;

    // We need to know when mouse up occurs even outside the scrollbar arrow 
    chart.addEvtListener(DvtMouseEvent.MOUSEUP, this.onGlobalMouseUp, false, this);

}



/**
 * Adds a new handle to the list of managed handles
 */
DvtHandleManager.prototype.addHandle = function(handle) {

    this._handleList.push(handle);

    switch(handle.getId()) {
        case DvtGraphHandle.ID_PA:
            this._hPA = handle;
            break;
        case DvtGraphHandle.ID_X1L:
            this._hX1L = handle;
            break;
        case DvtGraphHandle.ID_Y1PA:
            this._hY1PA = handle;
            break;
        case DvtGraphHandle.ID_Y2PA:
            this._hY2PA = handle;
            break;
        case DvtGraphHandle.ID_OL:
            this._hOL = handle;
            break;
        case DvtGraphHandle.ID_Y1L:
            this._hY1L = handle;
            break;
        case DvtGraphHandle.ID_Y2L:
            this._hY2L = handle;
            break;
    }

    if (handle.getId() == DvtGraphHandle.ID_PA
            && handle.getBoundingPoints() != null)
    {
        this._fullPlotPoints = handle.getBoundingPoints();
        this._fullPlotWidth  = this._fullPlotPoints[2] - this._fullPlotPoints[0];
        this._fullPlotHeight = this._fullPlotPoints[3] - this._fullPlotPoints[1];
    }
    else if (handle.getId() == DvtGraphHandle.ID_Y2PA
            && handle.getBoundingPoints() != null)
    {
        this._fullPlotPointsY2 = handle.getBoundingPoints();
        this._fullPlotWidthY2  = this._fullPlotPointsY2[2] - this._fullPlotPointsY2[0];
        this._fullPlotHeightY2 = this._fullPlotPointsY2[3] - this._fullPlotPointsY2[1];
    }

}


/**
 * Adds a reference to a DvtScrollbar to this handle manager. Required
 * for programmatic scroll support.
 */
DvtHandleManager.prototype.addScrollbar = function(sb) {
    var thumb = sb.getScrollbarThumb();
    DvtGraphSysUtils.makeDetObject(thumb, sb._Dlo._Chart);
    
    switch(sb._uiId) {    // TODO: change to a method call!
        case DvtScrollbar.ID_X1RS:
            this._x1rs = sb;
            break;
        case DvtScrollbar.ID_Y1RS:
            this._y1rs = sb;
            break;
        case DvtScrollbar.ID_Y2RS:
            this._y2rs = sb;
            break;
        case DvtScrollbar.ID_O1RS:
            this._o1rs = sb;
            break;
    }

    // register scrollbar listeners (TODO: should be done on Chart instead?)
    sb.addEvtListener(DvtScrollbarEvent.SB_MOVE, this.onScrollbarMove, false, this);
    sb.addEvtListener(DvtScrollbarEvent.SB_MOVE_COMPLETE, this.onScrollbarMoveComplete, false, this);
    sb.addEvtListener(DvtScrollbarEvent.SB_RESIZE_COMPLETE, this.onScrollbarResizeComplete, false, this);
    
}


/**
 * Returns true if this handle manager has any associated scrollbars.
 */
DvtHandleManager.prototype.hasScrollbars = function() {
    if (this._o1rs || this._x1rs || this._y1rs || this._y2rs)
        return true;
    else
        return false;
}

/**
 * mouse up event handler
 */
DvtHandleManager.prototype.onGlobalMouseUp = function(event) {
    if (this._o1rs)
        this._o1rs.processGlobalMouseUp();
    if (this._x1rs)
        this._x1rs.processGlobalMouseUp();
    if (this._y1rs)
        this._y1rs.processGlobalMouseUp();
    if (this._y2rs)
        this._y2rs.processGlobalMouseUp();
}

/**
 * scrollbarMove event handler
 */
DvtHandleManager.prototype.onScrollbarMove = function(event) {
    
    var movedDelta = event.getMovedDelta();

    if (event.type != DvtScrollbarEvent.SB_MOVE || movedDelta == 0)
        return;

    if (this._Chart.isMagnifyLensEnabled()) {
        var magnifyLens =  this._Chart.getCtx().getCustomTooltipManager().getMagnifyLens();
        magnifyLens.setDirty(true);
    }

    var scaledDelta = event.getScaledDelta();
    var sb = event.getScrollbar();

    var width = this._fullPlotWidth;
    var height = this._fullPlotHeight;

    if (sb.getUIId() == DvtScrollbar.ID_Y2RS) {
        width = this._fullPlotWidthY2;
        height = this._fullPlotHeightY2;
    }

    var offset;
    
    if (sb.isHorizontal())
        offset = scaledDelta * width;
    else
        offset = scaledDelta * height;
    
    //bug 13552685    
    if (offset > 0)
        offset = Math.ceil(offset);
    else
        offset = Math.floor(offset);

    switch (event.getScrollbarId()) {
        case DvtScrollbar.ID_X1RS:  // scroll PA and X1L handles
            this._scrollXAxis(offset, sb);
            break;
        case DvtScrollbar.ID_O1RS:
            this._scrollOAxis(offset, sb);
            break;
        case DvtScrollbar.ID_Y1RS:
            this._scrollYAxis(offset, sb);
            break;
        case DvtScrollbar.ID_Y2RS:
            this._scrollY2Axis(offset, sb);
            break;
    }

    // TODO: updateBackgroundEraser()  -- needed ?
}


/**
 * scrollbarMoveComplete event handler
 */
DvtHandleManager.prototype.onScrollbarMoveComplete = function(event) {
    this._emitMTEvent(event);
}

/**
 * scrollbarResizeComplete event handler
 */
DvtHandleManager.prototype.onScrollbarResizeComplete = function(event) {
    this._emitMTEvent(event);
}

/**
 * Emits an event to the middle-tier
 */
DvtHandleManager.prototype._emitMTEvent = function(event) {

    var sb = event.getScrollbar();
    var sJS;
    var cType;

    if (event.type == DvtScrollbarEvent.SB_MOVE_COMPLETE) {
        cType = DvtHandleManager.C_SCROLL;
        sJS = sb.getScrollEventJs();
    }
    else if (event.type == DvtScrollbarEvent.SB_RESIZE_COMPLETE) {
        cType = DvtHandleManager.C_RESIZE;
        sJS = sb.getZoomEventJs();
    }

    if (!sJS) {
        return;
    }

    var thumbDisp = event.getStartCoord();
    var thumbSize = event.getEndCoord() - thumbDisp;
    var scrollbarSize = sb.getScrollAreaLength();

    // flip start thumb position for vertical scrollbars
    if (!sb.isHorizontal()) {
        thumbDisp = scrollbarSize - thumbSize - thumbDisp;
    }

    // TODO: should use StrUtils token replace and EventTokens constants when available

    var sJS1 = sJS.replace(DvtHandleManager.SCROLL_START, thumbDisp);
    var sJS2 = sJS1.replace(DvtHandleManager.SCROLL_SPAN, thumbSize);
    var sJS3 = sJS2.replace(DvtHandleManager.SCROLL_TOTAL, scrollbarSize);
    var finalJS = sJS3.replace(DvtHandleManager.TOK_TYPE, cType);

    // execute the JS code
    //BUG 34184877 - MAKE DVT COMPATIBLE WITH CSP STANDARDS
    var jsArray = finalJS.match(/'(.*?)'/g);
    var src = jsArray[0].replace(/\'/g, "");
    var eventData = jsArray[2].replace(/\'/g, "");
    var bi_gdAData = jsArray[3].replace(/\'/g, "");
    var bi_gdsStData = jsArray[4].replace(/\'/g, "");
    var bi_gdsSpData = jsArray[5].replace(/\'/g, "");
    var bi_gdsCData = jsArray[6].replace(/\'/g, "");
    var bi_gdsTData = jsArray[7].replace(/\'/g, "");
     
    AdfPage.PAGE.findComponent(src).getPeer().sendToServerPPR2({source:src,event: eventData ,bi_gdsA: bi_gdAData ,bi_gdsSt: bi_gdsStData ,bi_gdsSp: bi_gdsSpData ,bi_gdsC: bi_gdsCData ,bi_gdsT: bi_gdsTData });

}

/**
 * Scrolls the plotArea on the O-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollOAxis = function(offset, sb) {

    if (this._hPA == null || this._hPA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hPA.getBoundingPoints();
    var boundedOffset;
    var frameRect = this._Chart.getFrameRect();

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], frameRect.x, frameRect.x + frameRect.w);

        this._hPA.moveX(-boundedOffset);
        if (this._hOL != null)
            this._hOL.moveX(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveX(-boundedOffset);
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], frameRect.y, frameRect.y + frameRect.h);

        this._hPA.moveY(-boundedOffset);
        if (this._hOL != null)
            this._hOL.moveY(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveY(-boundedOffset);
    }
}

/**
 * Scrolls the plotArea on the x-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollXAxis = function(offset, sb) {

    if (this._hPA == null || this._hPA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hPA.getBoundingPoints();
    var boundedOffset;
    var frameRect = this._Chart.getFrameRect();

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], frameRect.x, frameRect.x + frameRect.w);

        this._hPA.moveX(-boundedOffset);
        if (this._hX1L != null)
            this._hX1L.moveX(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveX(-boundedOffset);

        // TODO: also update the min axis coord ???
        // chart.m_xPxMin -= boundedOffset
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], frameRect.y, frameRect.y + frameRect.h);

        this._hPA.moveY(-boundedOffset);
        if (this._hX1L != null)
            this._hX1L.moveY(-boundedOffset);
        if (this._hY2PA != null)
            this._hY2PA.moveY(-boundedOffset);

        // TODO: also update the min axis coord ???
    }
}

/**
 * Scrolls the plotArea on the y-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollYAxis = function(offset, sb) {

    if (this._hPA == null || this._hPA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hPA.getBoundingPoints();
    var clipRect = this._hPA.getClipRect();
    var boundedOffset;

    if (clipRect == null)
        return;

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], clipRect.x, clipRect.x + clipRect.w);

        this._hPA.moveX(-boundedOffset);
        if (this._hY1L != null)
            this._hY1L.moveX(-boundedOffset);

        // TODO: also update the min axis coord ???
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], clipRect.y, clipRect.y + clipRect.h);

        this._hPA.moveY(-boundedOffset);
        if (this._hY1L != null)
            this._hY1L.moveY(-boundedOffset);

        // TODO: also update the min axis coord ???
        // chart.m_yPxMin -= boundedOffset
    }

}

/**
 * Scrolls the plotArea on the y2-axis by the specified number of pixels.
 *
 * @param offset  the number of pixels to scroll the plot area
 * @param sb      the scrollbar for this axis
 */
DvtHandleManager.prototype._scrollY2Axis = function(offset, sb) {

    if (this._hY2PA == null || this._hY2PA.getBoundingPoints() == null)
        return;     // bounds must be specified for scrolling to work

    var bounds = this._hY2PA.getBoundingPoints();
    var clipRect = this._hY2PA.getClipRect();
    var boundedOffset;

    if (clipRect == null)
        return;

    if (sb.isHorizontal()) {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[0], bounds[2], clipRect.x, clipRect.x + clipRect.w);

        this._hY2PA.moveX(-boundedOffset);
        if (this._hY2L != null)
            this._hY2L.moveX(-boundedOffset);
    }
    else {
        // figure out the offset bounded by the plot area
        boundedOffset = this._getBoundedOffset(offset, bounds[1], bounds[3], clipRect.y, clipRect.y + clipRect.h);

        this._hY2PA.moveY(-boundedOffset);
        if (this._hY2L != null)
            this._hY2L.moveY(-boundedOffset);
    }
}

/**
 * Returns the offset bounded by the frameStart and frameEnd coords given
 */
DvtHandleManager.prototype._getBoundedOffset = function(offset, handleStart, handleEnd, frameStart, frameEnd) {
    var boundedOffset = offset;
    if (handleStart - offset >= frameStart)
        boundedOffset = handleStart - frameStart;
    else if (frameEnd > handleEnd - offset)
        boundedOffset = handleEnd - frameEnd;

    return boundedOffset;
}


    
/**
* Scrolls the plot area by the given number of pixels on each axis.
* @param offsetPctO the offset to scroll in pixels 
* @param offsetPctX the offset to scroll in pixels
* @param offsetPctY the offset to scroll in pixels
* @param offsetPctY2 the offset to scroll in pixels
* @param clickPoint if triggered by a mouse event, this is the absolute coord for the click
* @param dispatchEvent true if a move event should be fired
* @param complete true if the scrolling operation is complete
*/ 
DvtHandleManager.prototype.scrollPlotAreaOffset = function(offsetO, offsetX, offsetY, offsetY2, clickPoint, dispatchEvent, complete) 
    {
      // Delta in percentage of axis
      var deltaO = 0;  
      var deltaX = 0;
      var deltaY = 0;
      var deltaY2 = 0;
      // Scroll on the O-Axis
      if(offsetO != 0 && this._o1rs != null) {
        if(this._o1rs.isHorizontal())
          deltaO = 100*offsetO/this._fullPlotWidth;
        else
          deltaO = 100*offsetO/this._fullPlotHeight;
      }
      
      // Scroll on the X-Axis
      if(offsetX != 0 && this._x1rs != null) {
        if(this._x1rs.isHorizontal())
          deltaX = 100*offsetX/this._fullPlotWidth;
        else
          deltaX = 100*offsetX/this._fullPlotHeight;
      }
      
      // Scroll on the Y-Axis
      if(offsetY != 0 && this._y1rs != null) {
      	var clipRect = this._hPA.getClipRect();
      	if (clickPoint != null && clipRect != null) {
      		// For split dual y, target correct handle only 
      		if (clickPoint.x < clipRect.x || clickPoint.x > (clipRect.x+clipRect.width) ||
      		    clickPoint.y < clipRect.y || clickPoint.y > (clipRect.y+clipRect.height))
      		    offsetY = 0;
      	}
      	
        if(this._y1rs.isHorizontal())
          deltaY = 100*offsetY/this._fullPlotWidth;
        else
          deltaY = 100*offsetY/this._fullPlotHeight;
      }
      
      // Scroll on the Y2-Axis
      if(offsetY2 != 0 && this._y2rs != null) {
      	var clipRectY2 = this._hY2PA.getClipRect();
      	if (clickPoint != null && clipRectY2 != null) {
      		// For split dual y, target correct handle only
      		if (clickPoint.x < clipRectY2.x || clickPoint.x > (clipRectY2.x+clipRectY2.width) ||
      		    clickPoint.y < clipRectY2.y || clickPoint.y > (clipRectY2.y+clipRectY2.height))
      		    offsetY2 = 0;
      	}
      	
        if(this._y2rs.isHorizontal())
          deltaY2 = 100*offsetY2/this._fullPlotWidthY2;
        else
          deltaY2 = 100*offsetY2/this._fullPlotHeightY2;
      }
      
      this.scrollPlotAreaPercentage(deltaO, deltaX, deltaY, deltaY2, dispatchEvent, complete);
    }

    /**
    * Scrolls the plot area by the given percentage on each axis.
    * @param offsetPctO the offset to scroll in percentage of the axis
    * @param offsetPctX the offset to scroll in percentage of the axis
    * @param offsetPctY the offset to scroll in percentage of the axis
    * @param offsetPctY2 the offset to scroll in percentage of the axis
    */ 
DvtHandleManager.prototype.scrollPlotAreaPercentage = function(offsetPctO, offsetPctX, offsetPctY, offsetPctY2, dispatchEvent, complete) 
    {
      var delta = null; // delta in coords
      // Scroll on the O-Axis
      if(offsetPctO != 0 && this._o1rs != null) {
        if(this._o1rs.isHorizontal())
          delta = (offsetPctO/100)*this._fullPlotWidth;
        else
          delta = (offsetPctO/100)*this._fullPlotHeight;
          
        // scroll the plot area
        this._scrollOAxis(delta, this._o1rs); 
        
        // Update the Range Slider
        this._o1rs.move(offsetPctO, dispatchEvent, complete);
      }
      
      // Scroll on the X-Axis
      if(offsetPctX != 0 && this._x1rs != null) {
        if(this._x1rs.isHorizontal())
          delta = (offsetPctX/100)*this._fullPlotWidth;
        else
          delta = (offsetPctX/100)*this._fullPlotHeight;
          
        // scroll the plot area
        this._scrollXAxis(delta, this._x1rs); 
        
        // Update the Range Slider
        this._x1rs.move(offsetPctX, dispatchEvent, complete);
      }
      
      // Scroll on the Y-Axis
      if(offsetPctY != 0 && this._y1rs != null) {
        if(this._y1rs.isHorizontal())
          delta = (offsetPctY/100)*this._fullPlotWidth;
        else
          delta = (offsetPctY/100)*this._fullPlotHeight;
          
        // scroll the plot area
        this._scrollYAxis(delta, this._y1rs); 
        
        // Update the Range Slider
        this._y1rs.move(offsetPctY, dispatchEvent, complete);
      }
      
      // Scroll on the Y2-Axis
      if(offsetPctY2 != 0 && this._y2rs != null) {
        if(this._y2rs.isHorizontal())
          delta = (offsetPctY2/100)*this._fullPlotWidthY2;
        else
          delta = (offsetPctY2/100)*this._fullPlotHeightY2;
          
        // scroll the plot area
        this._scrollY2Axis(delta, this._y2rs); 
        
        // Update the Range Slider
        this._y2rs.move(offsetPctY2, dispatchEvent, complete);
      }
      //updateBackgroundEraser();
    }
    
/**
 * Dispatches move events to JS. 
 * @param dispatchEvent true if a move event should be fired
 * @param complete true if the scrolling operation is complete
 */ 
DvtHandleManager.prototype.dispatchJSEvents = function() {
    if(this._o1rs != null)
        this._o1rs.move(0, true, true);
    if(this._x1rs != null)
        this._x1rs.move(0, true, true);
    if(this._y1rs != null)
        this._y1rs.move(0, true, true);
    if(this._y2rs != null)
        this._y2rs.move(0, true, true);
} 

/*-----------------------------------------------------------------------*/
/*     getHandle()       Returns a Handle object with the specified ID   */
/*                       Returns null if not found.                      */
/*-----------------------------------------------------------------------*/
DvtHandleManager.prototype.getHandle = function(handleId) {
    switch(handleId) {
        case DvtGraphHandle.ID_PA:
            return this._hPA;
        case DvtGraphHandle.ID_X1L:
            return this._hX1L;
        case DvtGraphHandle.ID_Y1PA:
            return this._hY1PA;
        case DvtGraphHandle.ID_Y2PA:
            return this._hY2PA;
        case DvtGraphHandle.ID_OL:
            return this._hOL;
        case DvtGraphHandle.ID_Y1L:
            return this._hY1L;
        case DvtGraphHandle.ID_Y2L:
            return this._hY2L;
        default:
            return null;
    }
    return null;
}
