/**
 * Renderer for dvt.StatusMeterGauge.
 * @class
 */
var DvtStatusMeterGaugeRenderer = new Object();

dvt.Obj.createSubclass(DvtStatusMeterGaugeRenderer, dvt.Obj);

/**
 * Renders the gauge in the specified area.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {number} width The width of the component.
 * @param {number} height The height of the component.
 */
DvtStatusMeterGaugeRenderer.render = function(gauge, container, width, height) {
  if (DvtGaugeDataUtils.hasData(gauge)) {
    // Allocate the outer gap for the component
    var options = gauge.getOptions();
    var outerGap = options['__layout']['outerGap'];
    var bounds = new dvt.Rectangle(outerGap, outerGap, width - 2 * outerGap, height - 2 * outerGap);

    if (options['orientation'] == 'horizontal' || options['orientation'] == 'vertical') {
      // Render metric label outside of plot area. Metric labels within the plot area are handled within
      // the renderShape function.
      if (DvtStatusMeterGaugeRenderer._hasMetricLabelOutsidePlotArea(options))
        DvtStatusMeterGaugeRenderer._renderMetricLabelOutsidePlotArea(gauge, container, bounds);

      DvtStatusMeterGaugeRenderer._renderShape(gauge, container, bounds);
    }
    else if (options['orientation'] == 'circular') {
      DvtStatusMeterGaugeRenderer._renderCircularGauge(gauge, container, bounds);
    }
  }
  else // Render the empty text
    DvtGaugeRenderer.renderEmptyText(gauge, container, new dvt.Rectangle(0, 0, width, height));
};


/**
 * Renders the circular shape into the specified area.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @private
 */
DvtStatusMeterGaugeRenderer._renderCircularGauge = function(gauge, container, bounds) {
  var options = gauge.getOptions();
  var containerBounds = bounds.clone();
  var isRTL = dvt.Agent.isRightToLeft(gauge.getCtx());
  var percentFill = 0;
  var metricLabelBounds = null;
  var value = options['value'];
  var innerRadius = options['innerRadius'];
  var thresholds = options['thresholds'];
  var maxDiameter, maxInnerDiameter = null;
  var startAngleRads = dvt.Math.TWO_PI - dvt.Math.degreesToRads(options['startAngle']);
  var angleExtentRads = dvt.Math.degreesToRads(options['angleExtent']);
  var endAngle = (startAngleRads + angleExtentRads) % (2 * Math.PI);

  // Store center for editable gauges
  gauge.cx = bounds.w / 2;
  gauge.cy = bounds.h / 2;
  // Determine start quadrant and end quadrent
  if (angleExtentRads != 2 * Math.PI) {
    metricLabelBounds = DvtStatusMeterGaugeRenderer._adjustCenterAndBounds(gauge, innerRadius, startAngleRads, angleExtentRads, endAngle, bounds, isRTL);
    maxInnerDiameter = gauge.maxInnerDiameter;
  }
  maxDiameter = maxDiameter ? maxDiameter : Math.min(bounds.w, bounds.h);
  var innerRadiusLength = maxDiameter * .5 * innerRadius;
  var outerRadius = maxDiameter * .5;
  var indicatorSize = options['indicatorSize'];
  if (indicatorSize && indicatorSize > 1) {
    var spaceWidth = ((1 - 1 / indicatorSize) / 2 * (outerRadius - innerRadiusLength));
    innerRadiusLength += spaceWidth;
    outerRadius -= spaceWidth;
  }
  // Add metric label first
  var metricLabelHalign = 'center';
  var metricLabelValign = 'middle';
  if (!metricLabelBounds) {
    maxInnerDiameter = Math.min(bounds.w, bounds.h) * innerRadius;
    // Center label bounds within the space available.
    metricLabelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - maxInnerDiameter * (3 / 7), bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (6 / 7), maxInnerDiameter * (5 / 7));
  }

  var bLabelRendered = false;
  if (DvtGaugeStyleUtils.hasLabel(options)) {
    var labelValign = 'middle';
    var labelSpace = new dvt.Rectangle(metricLabelBounds.x, metricLabelBounds.y, metricLabelBounds.w, metricLabelBounds.h);
    // If both the label and metricLabel are rendered 1/3 of the available space is allocated for the label
    if (options['metricLabel']['rendered'] != 'off') {
      labelSpace.y = labelSpace.y + labelSpace.h * .6;
      labelSpace.h = labelSpace.h * .4;
      labelValign = 'top';
    }
    bLabelRendered = DvtGaugeRenderer.renderLabel(gauge, container, labelSpace, null, labelValign);
    if (bLabelRendered && options['metricLabel']['rendered'] != 'off') {
      metricLabelBounds.h = metricLabelBounds.h * .55;
      metricLabelValign = 'bottom';
    }

  }
  DvtGaugeRenderer.renderMetricLabel(gauge, container, metricLabelBounds, null, metricLabelHalign, metricLabelValign, true);

  var startAngle = startAngleRads;
  var angleExtent = percentFill * angleExtentRads;
  var plotAreaBorderColor = DvtGaugeStyleUtils.getPlotAreaBorderColor(gauge);
  if (thresholds && options['plotArea']['rendered'] != 'off' && options['thresholdDisplay'] == 'all') {
    for (var currentThresholdIndex = 0; currentThresholdIndex < thresholds.length; currentThresholdIndex++) {
      var thresholdColor = DvtGaugeStyleUtils.getThresholdColor(gauge, thresholds[currentThresholdIndex], currentThresholdIndex);
      var max = thresholds[currentThresholdIndex]['max'] < options['max'] ? thresholds[currentThresholdIndex]['max'] : options['max'];
      var min = currentThresholdIndex == 0 ? options['min'] : thresholds[currentThresholdIndex - 1]['max'];
      startAngle = startAngleRads + angleExtentRads * DvtGaugeRenderer.getFillPercentage(options, options['min'], max, min);
      percentFill = DvtGaugeRenderer.getFillPercentage(options, min, max, max);
      angleExtent = percentFill * angleExtentRads;
      var thresholdborderColor = thresholds[currentThresholdIndex]['borderColor'];
      DvtStatusMeterGaugeRenderer._drawCircularArc(gauge, container, bounds, startAngle, angleExtent, innerRadiusLength,
          outerRadius, thresholdColor, true, thresholdborderColor ? thresholdborderColor : plotAreaBorderColor);
    }
  }
  else if (options['plotArea']['rendered'] != 'off' && options['thresholdDisplay'] != 'all') {
    var plotAreaColor = DvtGaugeStyleUtils.getPlotAreaColor(gauge);
    DvtStatusMeterGaugeRenderer._drawCircularArc(gauge, container, bounds, startAngleRads, angleExtentRads, innerRadiusLength, outerRadius, plotAreaColor, true, plotAreaBorderColor);
  }

  innerRadiusLength = maxDiameter * innerRadius * .5;
  outerRadius = maxDiameter * .5;
  if (indicatorSize && indicatorSize < 1) {
    var totalWidth = ((1 - indicatorSize) / 2 * (outerRadius - innerRadiusLength));
    innerRadiusLength += totalWidth;
    outerRadius -= totalWidth;
  }

  percentFill = DvtGaugeRenderer.getFillPercentage(options, options['min'], options['max'], value);
  angleExtent = percentFill * angleExtentRads;
  DvtStatusMeterGaugeRenderer._drawCircularArc(gauge, container, bounds, startAngleRads, angleExtent, innerRadiusLength, outerRadius, DvtGaugeStyleUtils.getColor(gauge), false);

  // Reference lines
  var referenceObjects = options['referenceLines'];
  if (referenceObjects) {
    for (var i = 0; i < referenceObjects.length; i++) {
      var referenceLineColor = referenceObjects[i]['color'] ? referenceObjects[i]['color'] : 'black';
      var referenceLineWidth = referenceObjects[i]['lineWidth'] ? referenceObjects[i]['lineWidth'] : 2;
      var referenceLineStyle = referenceObjects[i]['lineStyle'];
      value = referenceObjects[i]['value'];
      var angle = startAngleRads + DvtGaugeRenderer.getFillPercentage(options, options['min'], options['max'], value) * angleExtentRads;
      DvtStatusMeterGaugeRenderer._drawCircularReferenceLine(gauge, container, bounds, angle, referenceLineColor, referenceLineWidth, referenceLineStyle);
    }
  }
  if (options['center']['renderer'])
    DvtStatusMeterGaugeRenderer._renderCenterContent(gauge, options, bounds, containerBounds, maxInnerDiameter);
};

/**
 * Renders the status meter shape into the specified area.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @private
 */
DvtStatusMeterGaugeRenderer._renderShape = function(gauge, container, bounds) {
  var options = gauge.getOptions();
  var isRTL = dvt.Agent.isRightToLeft(gauge.getCtx());
  var isVert = (options['orientation'] == 'vertical');

  // Create an axis info to find the coords of values.
  var axisOptions = {
    'layout' : {
    }
  };
  axisOptions['layout']['gapRatio'] = 0;
  axisOptions['timeAxisType'] = 'disabled';
  axisOptions['position'] = isVert ? 'left' : 'bottom';
  axisOptions['min'] = options['min'];
  axisOptions['max'] = options['max'];
  axisOptions['dataMin'] = options['min'];
  axisOptions['dataMax'] = options['max'];
  axisOptions['tickLabel'] = {
    'rendered' : 'off'
  };
  var axisInfo = dvt.AxisInfo.newInstance(gauge.getCtx(), axisOptions, bounds);

  // Store the axisInfo on the gauge for editing support
  gauge.__axisInfo = axisInfo;

  // First calculate the baseline coordinate.
  var baseline = 0;
  if (options['max'] <= 0)
    baseline = options['max'];
  else if (options['min'] >= 0)
    baseline = options['min'];

  var baselineCoord = axisInfo.getCoordAt(baseline);
  // For statusmeters with plot area on, always draw from min value
  if (options['plotArea']['rendered'] != 'off' && !((options['plotArea']['rendered'] == 'auto') && (options['thresholdDisplay'] == 'onIndicator')))
    baselineCoord = axisInfo.getUnboundedCoordAt(options['min']);

  // Calculate the endCoord.  Adjust to keep within the axis.
  var endCoord = axisInfo.getUnboundedCoordAt(options['value']);
  endCoord = isVert ? Math.max(bounds.y, Math.min(bounds.y + bounds.h, endCoord)) : Math.max(bounds.x, Math.min(bounds.x + bounds.w, endCoord));

  var indicatorSize = options['indicatorSize'];
  var indicatorX1, indicatorX2, indicatorY1, indicatorY2, plotX1, plotX2, plotY1, plotY2 = 0;

  // The variables drawnPlotSize and drawnIndicatorSize are used to specifiy the fraction
  // of the possible width that will be used for plotArea and the indicator
  var drawnPlotSize = indicatorSize < 1 ? 1 : indicatorSize;
  var drawnIndicatorSize = indicatorSize > 1 ? 1 : indicatorSize;
  if (isVert) {
    indicatorX1 = bounds.x + (1 - drawnIndicatorSize) / 2 * bounds.w + .5;
    indicatorX2 = bounds.x + bounds.w * (1 + drawnIndicatorSize) / 2 - .5;
    indicatorY2 = baselineCoord - .5;
    indicatorY1 = endCoord + .5;
    plotX1 = bounds.x + (1 - 1 / drawnPlotSize) / 2 * bounds.w;
    plotX2 = bounds.x + bounds.w * (1 + 1 / drawnPlotSize) / 2;
    plotY1 = bounds.y;
    plotY2 = bounds.y + bounds.h;
  }
  else {
    indicatorX1 = isRTL ? baselineCoord - .5 : baselineCoord + .5;
    indicatorX2 = isRTL ? endCoord + .5 : endCoord - .5;
    indicatorY1 = bounds.y + (1 - drawnIndicatorSize) / 2 * bounds.h + .5;
    indicatorY2 = bounds.y + bounds.h * (1 + drawnIndicatorSize) / 2 - .5;
    plotX1 = bounds.x;
    plotX2 = bounds.x + bounds.w;
    plotY1 = bounds.y + (1 - 1 / drawnPlotSize) / 2 * bounds.h;
    plotY2 = bounds.y + bounds.h * (1 + 1 / drawnPlotSize) / 2;
  }

  var bRender = true;
  if (endCoord == baselineCoord) {
    if (isVert)
      indicatorY1 = indicatorY2;
    else
      indicatorX1 = indicatorX2;
    bRender = false;// don't draw an empty bar
  }

  // Create plotArea
  var borderColor = DvtGaugeStyleUtils.getBorderColor(gauge);
  var plotAreaBorderColor = DvtGaugeStyleUtils.getPlotAreaBorderColor(gauge);
  var thresholds = options['thresholds'];
  var gradientAngle = isVert ? 0 : 270;

  // Case for plot area with all thresholds displayed
  if (thresholds && options['plotArea']['rendered'] != 'off' && options['thresholdDisplay'] == 'all') {
    for (var i = thresholds.length - 1; i >= 0; i--) {
      var currentThresholdIndex = i;
      var plotArea = DvtStatusMeterGaugeRenderer._createShape(gauge, gauge.getCtx(), plotX1, plotX2, plotY1, plotY2);

      var cp = new dvt.ClipPath('pacp' + gauge.getId());

      // For each threshold clip everything above the particular threshold maximum from the plot area shape
      if (i == thresholds.length - 1) {
        if (!isVert && isRTL)
          cp.addRect(axisInfo.getUnboundedCoordAt(options['max']) + 1, 0, bounds.w + 2, bounds.h + 2, 0, 0);
        else
          cp.addRect(0, 0, bounds.w + 2, bounds.h + 2, 0, 0);
      }
      else {
        if (isVert) {
          cp.addRect(0, axisInfo.getUnboundedCoordAt(options['max']), bounds.w + 2, (options['max'] - thresholds[thresholds.length - 2 - currentThresholdIndex]['max']) * 1 / (Math.abs(options['min'] - options['max'])) * bounds.h, 0, 0);
        }
        else {
          if (isRTL) {
            var thresholdMax = thresholds[thresholds.length - 2 - currentThresholdIndex]['max'] == null ? 100 : thresholds[thresholds.length - 2 - currentThresholdIndex]['max'];
            cp.addRect(axisInfo.getUnboundedCoordAt(options['max']), 0, (options['max'] - thresholdMax) * 1 / (Math.abs(options['min'] - options['max'])) * bounds.w, bounds.h + 2, 0, 0);
          }
          else
            cp.addRect(0, 0, (thresholds[currentThresholdIndex]['max'] - options['min']) * 1 / (Math.abs(options['min'] - options['max'])) * bounds.w, bounds.h + 2, 0, 0);
        }
      }
      plotArea.setClipPath(cp);

      // Color threshold with defined color, or use the color ramp if possible
      if (isRTL || isVert)
        currentThresholdIndex = thresholds.length - 1 - i;

      plotArea.setSolidFill(DvtGaugeStyleUtils.getThresholdColor(gauge, thresholds[currentThresholdIndex], currentThresholdIndex));
      var thresholdBorderColor = thresholds[currentThresholdIndex]['borderColor'];
      plotArea.setSolidStroke(thresholdBorderColor ? thresholdBorderColor : plotAreaBorderColor);
      DvtStatusMeterGaugeRenderer._renderPlotAreaVisualEffects(gauge, container, plotArea, DvtGaugeStyleUtils.getThresholdColor(gauge, thresholds[currentThresholdIndex], currentThresholdIndex), gradientAngle);
    }
  }
  else if (!(options['plotArea']['rendered'] == 'off') && !((options['plotArea']['rendered'] == 'auto') && (options['thresholdDisplay'] == 'onIndicator')) && options['thresholdDisplay'] != 'all') {
    var plotArea = isVert ? DvtStatusMeterGaugeRenderer._createShape(gauge, gauge.getCtx(), plotX1, plotX2, axisInfo.getUnboundedCoordAt(options['max']), axisInfo.getUnboundedCoordAt(options['min'])) :
        DvtStatusMeterGaugeRenderer._createShape(gauge, gauge.getCtx(), axisInfo.getUnboundedCoordAt(options['min']), axisInfo.getUnboundedCoordAt(options['max']), plotY1, plotY2);
    var plotAreaColor = DvtGaugeStyleUtils.getPlotAreaColor(gauge);
    plotArea.setSolidFill(plotAreaColor);
    plotArea.setSolidStroke(plotAreaBorderColor);
    DvtStatusMeterGaugeRenderer._renderPlotAreaVisualEffects(gauge, container, plotArea, plotAreaColor, gradientAngle);
  }

  // Create the indicator.
  var shape = new DvtStatusMeterGaugeIndicator(gauge, gauge.getCtx(), indicatorX1, indicatorX2, indicatorY1, indicatorY2);
  gauge.__shapes.push(shape);

  // Apply style properties
  var color = DvtGaugeStyleUtils.getColor(gauge);
  if (!DvtGaugeDefaults.isSkyrosSkin(gauge) && options['visualEffects'] != 'none') {
    var arColors = [dvt.ColorUtils.adjustHSL(color, 0, - .09, .04), dvt.ColorUtils.adjustHSL(color, 0, - .04, - .05)];
    var arAlphas = [1, 1];
    var arStops = [0, 1];
    var gradient = new dvt.LinearGradientFill(gradientAngle, arColors, arAlphas, arStops);
    shape.setFill(gradient);
  }
  else
    shape.setSolidFill(color);

  if (borderColor)
    shape.setSolidStroke(borderColor);

  shape.setClassName(options['svgClassName']);
  shape.setStyle(options['svgStyle']);

  // Add the shape
  if (bRender)
    container.addChild(shape);

  // Render the visual effects
  var overlay = DvtStatusMeterGaugeRenderer._createShape(gauge, gauge.getCtx(), indicatorX1, indicatorX2, indicatorY1, indicatorY2);
  DvtStatusMeterGaugeRenderer._renderVisualEffects(gauge, container, overlay, bRender, gradientAngle);

  // Render reference objects
  var xCoord, yCoord;
  var referenceLine, referenceLineSize;
  var referenceObjects = options['referenceLines'];
  if (referenceObjects) {
    for (var i = 0; i < referenceObjects.length; i++) {
      var refColor = referenceObjects[i]['color'] ? referenceObjects[i]['color'] : 'white';
      var value = referenceObjects[i]['value'];
      var indicatorSize = options['indicatorSize'];
      if (isVert) {
        referenceLineSize = ((1 - indicatorSize) / 2 + indicatorSize) * bounds.w;
        xCoord = bounds.x + ((1 - indicatorSize) / 4) * bounds.w;
        yCoord = axisInfo.getUnboundedCoordAt(value);
        referenceLine = new dvt.Line(gauge.getCtx(), xCoord, yCoord, xCoord + referenceLineSize, yCoord);
      }
      else {
        referenceLineSize = ((1 - indicatorSize) / 2 + indicatorSize) * bounds.h;
        xCoord = axisInfo.getUnboundedCoordAt(value);
        yCoord = bounds.y + ((1 - indicatorSize) / 4) * bounds.h;
        referenceLine = new dvt.Line(gauge.getCtx(), xCoord, yCoord, xCoord, yCoord + referenceLineSize);
      }
      var lineWidth = referenceObjects[i]['lineWidth'] ? referenceObjects[i]['lineWidth'] : 2;
      var stroke = new dvt.SolidStroke(refColor, 1, lineWidth);
      if (referenceObjects[i]['lineStyle'])
        stroke.setStyle(dvt.Stroke.convertTypeString(referenceObjects[i]['lineStyle']));
      referenceLine.setStroke(stroke);
      container.addChild(referenceLine);

      // Shadowing effect
      // Fix for bug 20136668: Shadow causes the reference line to disappear in IE11
      if (!dvt.Agent.isPlatformIE() && options['visualEffects'] != 'none') {
        var shadowRGBA = dvt.ColorUtils.makeRGBA(0, 0, 0, 0.8);
        var shadow = new dvt.Shadow(shadowRGBA, 0.75, 3, 3, 50, 1, 2, false, false, false);
        referenceLine.addDrawEffect(shadow);
      }
    }
  }

  // Render the metric label within the plot area bounds
  var metricLabelPosition = options['metricLabel']['position'];
  if (options['metricLabel']['rendered'] == 'on' && !DvtStatusMeterGaugeRenderer._hasMetricLabelOutsidePlotArea(options) && !DvtGaugeStyleUtils.hasLabel(options)) {
    var indicatorPoints = {x1: indicatorX1, x2: indicatorX2, y1: indicatorY1, y2: indicatorY2};
    var plotAreaPoints = {x1: plotX1, x2: plotX2, y1: plotY1, y2: plotY2};
    DvtStatusMeterGaugeRenderer._renderMetricLabelInsidePlotArea(gauge, container, bounds, color, metricLabelPosition, indicatorPoints, plotAreaPoints);
  }

  // Render the label
  else if (DvtGaugeStyleUtils.hasLabel(options)) {
    DvtStatusMeterGaugeRenderer._renderLabel(gauge, container, bounds, metricLabelPosition);
  }
};


/**
 * Creates and returns the shape for the statusmeter.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Context} context
 * @param {number} x1
 * @param {number} x2
 * @param {number} y1
 * @param {number} y2
 * @return {dvt.Path}
 * @private
 */
DvtStatusMeterGaugeRenderer._createShape = function(gauge, context, x1, x2, y1, y2) {
  var x = Math.min(x1, x2);
  var y = Math.min(y1, y2);
  var width = Math.abs(x1 - x2);
  var height = Math.abs(y2 - y1);
  var options = gauge.getOptions();
  var multiplier = options['orientation'] == 'vertical' ? width : height;
  var defaultValue = DvtGaugeDefaults.isSkyrosSkin(gauge) ? '25%' : '15%';
  var borderRadiusInput = options['plotArea']['borderRadius'] != 'auto' ? options['plotArea']['borderRadius'] : options['borderRadius'];
  var cmds = dvt.PathUtils.rectangleWithBorderRadius(x, y, width, height, borderRadiusInput, multiplier, defaultValue);
  return new dvt.Path(context, cmds);
};


/**
 * Renders the visual effects for the shape into the specified area.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Shape} shape The shape to use for the visual effects.
 * @param {boolean} bRender True if the shape should be rendered at this time.
 * @param {number} gradientAngle
 * @private
 */
DvtStatusMeterGaugeRenderer._renderVisualEffects = function(gauge, container, shape, bRender, gradientAngle) {
  var options = gauge.getOptions();

  if (options['visualEffects'] == 'none')
    return;

  // Gradient
  if (DvtGaugeDefaults.isSkyrosSkin(gauge)) {
    var arColors = ['#FFFFFF', '#FFFFFF', '#FFFFFF'];
    var arAlphas = [0.5, 0.3125, 0];
    var arStops = [0, 0.3, 1.0];
    var gradient = new dvt.LinearGradientFill(gradientAngle, arColors, arAlphas, arStops);
    shape.setFill(gradient);
    gauge.__shapes.push(shape);

    // Overlay Shape
    shape.setMouseEnabled(false);
    if (bRender)
      container.addChild(shape);
  }
};


/**
 * Renders the visual effects for the plot area.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Shape} shape The plot area.
 * @param {string} color
 * @param {number} gradientAngle
 * @private
 */
DvtStatusMeterGaugeRenderer._renderPlotAreaVisualEffects = function(gauge, container, shape, color, gradientAngle) {
  var options = gauge.getOptions();
  shape.setMouseEnabled(false);
  container.addChild(shape);

  if (options['visualEffects'] == 'none')
    return;

  var arColors, gradient;

  // Gradient
  if (DvtGaugeDefaults.isSkyrosSkin(gauge)) {
    arColors = [dvt.ColorUtils.getDarker(color, .1), color, dvt.ColorUtils.getBrighter(color, .7)];
    gradient = new dvt.LinearGradientFill(gradientAngle, arColors, [1, 1, 1], [0, 0.04, 0.73]);
  }
  else {
    arColors = [dvt.ColorUtils.adjustHSL(color, 0, - .04, - .05), dvt.ColorUtils.adjustHSL(color, 0, - .09, .04)];
    gradient = new dvt.LinearGradientFill(gradientAngle, arColors, [1, 1], [0, 1]);
  }
  shape.setFill(gradient);
  shape.setClassName(options['plotArea']['svgClassName']);
  shape.setStyle(options['plotArea']['svgStyle']);
};


/**
 * Renders the metricLabel into the specified area for vertical/horizontal status meter gauges with the metricLabel outside the plotarea.
 * Updates the bounds after rendering to reserve space for the labels.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @private
 */
DvtStatusMeterGaugeRenderer._renderMetricLabelOutsidePlotArea = function(gauge, container, bounds) {
  var options = gauge.getOptions();
  var isRTL = dvt.Agent.isRightToLeft(gauge.getCtx());
  var isVert = options['orientation'] == 'vertical';
  var metricLabel = new dvt.OutputText(gauge.getCtx(), '');
  var metricLabelString = DvtGaugeRenderer.getFormattedMetricLabel(options['value'], gauge);
  var metricLabelGap = options['__layout']['labelGap'];
  var metricLabelStyle = options['metricLabel']['style'];

  // Backward compatibility
  var metricLabelColor = metricLabelStyle.getStyle('color');
  metricLabelColor = metricLabelColor ? metricLabelColor : '#333333';

  var maxMetricLabelDims = null;
  metricLabel.setCSSStyle(metricLabelStyle);
  metricLabel.setSolidFill(metricLabelColor);

  if (isVert && options['metricLabel']['rendered'] == 'on') {
    var bound = options['max'] > 0 ? options['max'] : options['min'];
    var maxValue = DvtGaugeRenderer.getFormattedMetricLabel(bound, gauge);
    var maxMetricLabel = new dvt.OutputText(gauge.getCtx(), maxValue);
    maxMetricLabel.setCSSStyle(metricLabelStyle);
    var computedMetricLabelBounds = new dvt.Rectangle(bounds.x, bounds.y + .8 * bounds.h, bounds.w, .2 * (bounds.h));
    var size = metricLabelStyle.getStyle('font-size');
    size = size ? parseInt(size) : maxMetricLabel.getOptimalFontSize(computedMetricLabelBounds);
    maxMetricLabelDims = maxMetricLabel.measureDimensions();
    bounds.h -= maxMetricLabelDims.h;
    alignCoord = bounds.y - maxMetricLabelDims.h;
    var metricLabelSpace = maxMetricLabelDims.w;
    metricLabel.setFontSize(size);
    metricLabel.setTextString(metricLabelString);
    metricLabel.setX(bounds.x + bounds.w / 2);
    metricLabel.setY(bounds.y + bounds.h);
    bounds.h -= metricLabelGap;
    metricLabel.alignCenter();
    dvt.TextUtils.fitText(metricLabel, bounds.w, bounds.h, container);
  }
  // Allocate space for the horizontal metricLabel
  else if (!isVert && options['metricLabel']['rendered'] == 'on') {
    // Check if the metric label's height will fit
    var size = metricLabelStyle.getStyle('font-size');
    var minMetricLabel = DvtGaugeRenderer.getFormattedMetricLabel(options['min'], gauge);
    var maxMetricLabel = DvtGaugeRenderer.getFormattedMetricLabel(options['max'], gauge);
    if (size === undefined && bounds.h < 18) {
      size = DvtGaugeRenderer.calcLabelFontSize([metricLabelString, minMetricLabel, maxMetricLabel], metricLabel, bounds);
    }
    size = size ? parseInt(size) : 13;
    metricLabel.setFontSize(size);

    var alignCoord;// The horizontal alignment point for the metricLabel
    // Allocate space to the right for positive values and any values with plotArea
    if (options['max'] > 0 || options['plotArea']['rendered'] != 'off' || !((options['plotArea']['rendered'] == 'auto') && (options['thresholdDisplay'] == 'onIndicator'))) {
      var bound = options['max'] > 0 ? options['max'] : options['min'];
      var maxValue = DvtGaugeRenderer.getFormattedMetricLabel(bound, gauge);
      var maxMetricLabel = new dvt.OutputText(gauge.getCtx(), maxValue);
      maxMetricLabel.setCSSStyle(metricLabelStyle);
      maxMetricLabel.setFontSize(size);
      maxMetricLabelDims = maxMetricLabel.measureDimensions();
      maxMetricLabelDims.w = Math.min(maxMetricLabelDims.w, bounds.w);
      // Align the metricLabel
      alignCoord = isRTL ? bounds.x + maxMetricLabelDims.w : bounds.x + bounds.w;
      metricLabelSpace = maxMetricLabelDims.w;

      if (isRTL) {
        // Allocate to the left
        bounds.x += (maxMetricLabelDims.w + metricLabelGap);
        bounds.w -= (maxMetricLabelDims.w + metricLabelGap);
      }
      else {// Allocate to the right
        bounds.w -= (maxMetricLabelDims.w + metricLabelGap);
      }
    }

    // Allocate space to the left for negative values
    if (options['min'] < 0 && options['plotArea']['rendered'] != 'on' && !(options['plotArea']['rendered'] == 'auto' && (options['thresholdDisplay'] == 'onIndicator'))) {
      var minValue = DvtGaugeRenderer.getFormattedMetricLabel(options['min'], gauge);
      var minMetricLabel = new dvt.OutputText(gauge.getCtx(), minValue);
      minMetricLabel.setCSSStyle(metricLabelStyle);
      minMetricLabel.setFontSize(size);
      var minMetricLabelDims = minMetricLabel.measureDimensions();

      // Align the metricLabel
      if (options['value'] < 0 || options['max'] <= 0) {
        alignCoord = isRTL ? bounds.x + bounds.w : bounds.x + minMetricLabelDims.w;
        metricLabelSpace = minMetricLabelDims.w;
      }

      // Update the allocated space
      if (isRTL) // Allocate to the right
        bounds.w -= (minMetricLabelDims.w + metricLabelGap);
      else {
        // Allocate to the left
        bounds.x += (minMetricLabelDims.w + metricLabelGap);
        bounds.w -= (minMetricLabelDims.w + metricLabelGap);
      }
    }
    // Create and position the text
    metricLabel.setTextString(metricLabelString);
    metricLabel.setX(alignCoord);
    dvt.TextUtils.centerTextVertically(metricLabel, bounds.y + bounds.h / 2);
    metricLabel.alignRight();

    // Truncate to fit
    dvt.TextUtils.fitText(metricLabel, metricLabelSpace, bounds.h, container);
  }
};

/**
 * Renders the metricLabel into the specified area for vertical/horizontal status meter gauges with the metricLabel inside the plotarea.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @param {string} color The color of the background
 * @param {string} metricLabelPosition The position of the metricLabel
 * @param {object} indicator An object containing the indicator x1, y1, x2, y2.
 * @param {object} plotArea An object containing the plotArea x1, y1, x2, y2.
 * @param {boolean} repeatedTry Specifies if this is the second try to fit the metricLabel into available space
 * @private
 */
DvtStatusMeterGaugeRenderer._renderMetricLabelInsidePlotArea = function(gauge, container, bounds, color, metricLabelPosition, indicator, plotArea, repeatedTry) {
  var options = gauge.getOptions();
  var isRTL = dvt.Agent.isRightToLeft(gauge.getCtx());
  var isVert = options['orientation'] == 'vertical';
  var plotAreaRendered = options['plotArea']['rendered'] == 'on';
  var hAlignment = 'center';
  var vAlignment = 'middle';

  var metricLabelSpace = new dvt.Rectangle(Math.min(indicator.x1, indicator.x2), Math.min(indicator.y1, indicator.y2),
      Math.abs(indicator.x2 - indicator.x1), Math.abs(indicator.y2 - indicator.y1));
  var metricLabelColor = options['metricLabel']['style'].getStyle('color');
  if (metricLabelPosition == 'center') {
    metricLabelColor = metricLabelColor ? metricLabelColor : dvt.ColorUtils.getContrastingTextColor(color);
    if (isVert) {
      metricLabelSpace.h -= metricLabelSpace.w;
      metricLabelSpace.y += (metricLabelSpace.w / 2);
    }
    else {
      metricLabelSpace.w -= metricLabelSpace.h;
      metricLabelSpace.x += (metricLabelSpace.h / 2);
    }
  }
  else if (metricLabelPosition == 'insideIndicatorEdge') {
    metricLabelColor = metricLabelColor ? metricLabelColor : dvt.ColorUtils.getContrastingTextColor(color);
    if (isVert) {
      metricLabelSpace.h -= metricLabelSpace.w;
      metricLabelSpace.y += (metricLabelSpace.w / 2);
      if (!plotAreaRendered && indicator.y1 > indicator.y2) {
        vAlignment = 'bottom';
      }
      else {
        vAlignment = 'top';
      }
    }
    else {
      metricLabelSpace.w -= metricLabelSpace.h;
      metricLabelSpace.x += (metricLabelSpace.h / 2);
      if (isRTL) {
        if (!plotAreaRendered && indicator.x1 < indicator.x2) {
          hAlignment = 'right';
        }
        else {
          hAlignment = 'left';
        }
      }
      else {
        if (!plotAreaRendered && indicator.x1 > indicator.x2) {
          hAlignment = 'left';
        }
        else {
          hAlignment = 'right';
        }
      }
    }
  }
  else if (metricLabelPosition == 'outsideIndicatorEdge') {
    if (isVert) {
      metricLabelSpace.h = Math.abs(plotArea.y1 - indicator.y1) - metricLabelSpace.w;
      metricLabelSpace.y = plotArea.y1 + (metricLabelSpace.w / 2);
      vAlignment = 'bottom';
      if (!plotAreaRendered && indicator.y1 > indicator.y2) {
        metricLabelSpace.h = Math.abs(plotArea.y1 - indicator.y1) - metricLabelSpace.w;
        metricLabelSpace.y = indicator.y1 + (metricLabelSpace.w / 2);
        vAlignment = 'top';
      }
    }
    else {
      if (isRTL) {
        if (!plotAreaRendered && indicator.x1 < indicator.x2) {
          metricLabelSpace.w = Math.abs(plotArea.x2 - indicator.x2) - metricLabelSpace.h;
          metricLabelSpace.x = indicator.x2 + (metricLabelSpace.h / 2);
          hAlignment = 'left';

        }
        else {
          metricLabelSpace.w = Math.abs(plotArea.x1 - indicator.x2) - metricLabelSpace.h;
          metricLabelSpace.x = plotArea.x1 + (metricLabelSpace.h / 2);
          hAlignment = 'right';
        }
      }
      else {
        if (!plotAreaRendered && indicator.x1 > indicator.x2) {
          metricLabelSpace.w = Math.abs(plotArea.x1 - indicator.x2) - metricLabelSpace.h;
          metricLabelSpace.x = plotArea.x1 + (metricLabelSpace.h / 2);
          hAlignment = 'right';
        }
        else {
          metricLabelSpace.w = Math.abs(plotArea.x2 - indicator.x2) - metricLabelSpace.h;
          metricLabelSpace.x = indicator.x2 + (metricLabelSpace.h / 2);
          hAlignment = 'left';
        }
      }
    }
  }
  var metricLabelRendered = DvtGaugeRenderer.renderMetricLabel(gauge, container, metricLabelSpace, metricLabelColor, hAlignment, vAlignment);
  // If the metricLabel didn't fit, try to fit it into another section
  if (!metricLabelRendered && !repeatedTry) {
    if (metricLabelPosition == 'outsideIndicatorEdge') {
      DvtStatusMeterGaugeRenderer._renderMetricLabelInsidePlotArea(gauge, container, bounds, color, 'insideIndicatorEdge', indicator, plotArea, true);
    }
    else if (metricLabelPosition == 'insideIndicatorEdge' || metricLabelPosition == 'center') {
      DvtStatusMeterGaugeRenderer._renderMetricLabelInsidePlotArea(gauge, container, bounds, color, 'outsideIndicatorEdge', indicator, plotArea, true);
    }
  }
};

/**
 * Renders the label for vertical/horizontal status meter gauges.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @param {string} metricLabelPosition The position of the metricLabel
 * @private
 */
DvtStatusMeterGaugeRenderer._renderLabel = function(gauge, container, bounds, metricLabelPosition) {
  var isRTL = dvt.Agent.isRightToLeft(gauge.getCtx());
  var options = gauge.getOptions();
  var isVert = options['orientation'] == 'vertical';
  var labelSpace = new dvt.Rectangle(bounds.x, bounds.y, isVert ? bounds.w : bounds.w - bounds.h, isVert ? bounds.h - bounds.w : bounds.h);
  var labelString = options['label']['text'];
  if (!DvtStatusMeterGaugeRenderer._hasMetricLabelOutsidePlotArea(options) && options['metricLabel']['rendered'] == 'on') {
    var metricLabelString = DvtGaugeRenderer.getFormattedMetricLabel(options['value'], gauge);
    labelString = dvt.Bundle.getTranslatedString(dvt.Bundle.UTIL_PREFIX, 'COLON_SEP_LIST', [labelString, metricLabelString]);
  }
  var labelStyle = options['label']['style'];
  var fontStyle = labelStyle.clone();
  var size = labelStyle.getStyle('font-size');
  if (!size) {
    // Calculate font size
    var tempLabel = new dvt.OutputText(gauge.getCtx(), labelString, 0, 0);
    tempLabel.setCSSStyle(labelStyle);
    tempLabel.setTextString(labelString);
    // Make size calculations based on the available height not width to have consistent size on same sized gauges
    // for horizontal gauges. For vertical gauges make calculations based on width
    if (isVert)
      size = tempLabel.getOptimalFontSize(new dvt.Rectangle(labelSpace.x, labelSpace.y, labelSpace.w, Number.MAX_VALUE));
    else
      size = tempLabel.getOptimalFontSize(new dvt.Rectangle(labelSpace.x, labelSpace.y, Number.MAX_VALUE, labelSpace.h));
  }
  var label = new dvt.MultilineText(gauge.getCtx(), labelString);
  fontStyle.setFontSize('font-size', size.toString());
  label.setCSSStyle(fontStyle);
  dvt.TextUtils.fitText(label, labelSpace.w, labelSpace.h, gauge);
  if (options['label']['position'] == 'center' || (options['label']['position'] == 'auto' && isVert)) {
    dvt.TextUtils.centerTextVertically(label, bounds.y + bounds.h / 2);
    label.setX(bounds.x + bounds.w / 2);
    label.alignCenter();
  }
  else {
    dvt.TextUtils.centerTextVertically(label, bounds.y + bounds.h / 2);
    if (!isVert && isRTL) {
      label.setX(bounds.x + bounds.w - labelSpace.h / 2);
      label.alignRight();
    }
    else if (!isVert && !isRTL)
    {
      label.setX(bounds.x + labelSpace.h / 2);
      label.alignLeft();
    }
    else if (isVert) {
      label.setY(bounds.y + bounds.h - label.getDimensions().h - labelSpace.w / 2);
      label.setX(bounds.x + bounds.w / 2);
      label.alignCenter();
    }
  }
  container.addChild(label);
};
/**
 * Returns the location of the point on the arc with the specified radius
 * at the specified angle.
 * @param {dvt.Rectangle} bounds
 * @param {number} radius
 * @param {number} angle
 * @return {object} An object with x and y properties.
 * @private
 */
DvtStatusMeterGaugeRenderer._calcPointOnArc = function(bounds, radius, angle) {
  var x = Math.cos(angle) * radius + bounds.w / 2 + bounds.x;
  var y = Math.sin(angle) * radius + bounds.h / 2 + bounds.y;
  return {x: x, y: y};
};


/**
 * Draw specified segment for circular status meter
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @param {Number} startAngle Start angle.
 * @param {Number} angleExtent Angle from start to end.
 * @param {Number} innerRadius Radius to inner border of arc.
 * @param {Number} outerRadius Radius to outer border of arc..
 * @param {color} color Color of the arc.
 * @param {Boolean} isPlotArea True if arc being drawn is the plot area.
 * @param {color} plotAreaBorderColor Color of the plot area border color
 * @private
 */
DvtStatusMeterGaugeRenderer._drawCircularArc = function(gauge, container, bounds, startAngle, angleExtent, innerRadius, outerRadius, color, isPlotArea, plotAreaBorderColor) {
  var context = gauge.getCtx();
  var isRTL = dvt.Agent.isRightToLeft(gauge.getCtx());
  if (isRTL) {
    startAngle = Math.PI - startAngle - angleExtent;
    startAngle = startAngle > 0 ? startAngle : startAngle + 2 * Math.PI;
  }

  var shape;
  if (isPlotArea) {
    shape = new dvt.Path(context, DvtStatusMeterGaugeRenderer.createCircularPathCmd(bounds, startAngle, angleExtent, innerRadius, outerRadius));
  }
  else {
    shape = new DvtStatusMeterGaugeCircularIndicator(context, bounds, startAngle, angleExtent, innerRadius, outerRadius);
    gauge.__shapes.push(shape);
  }
  shape.setSolidFill(color);
  var borderColor = DvtGaugeStyleUtils.getBorderColor(gauge);
  if (borderColor && !isPlotArea) {
    shape.setSolidStroke(borderColor);
  }
  else if (isPlotArea && plotAreaBorderColor) {
    shape.setSolidStroke(plotAreaBorderColor);
  }
  var options = gauge.getOptions();
  shape.setClassName(isPlotArea ? options['plotArea']['svgClassName'] : options['svgClassName']);
  shape.setStyle(isPlotArea ? options['plotArea']['svgStyle'] : options['svgStyle']);
  container.addChild(shape);
};


/**
 * Draw reference line
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {dvt.Container} container The container to render into.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @param {Number} angle Angle at which line is drawn.
 * @param {string} color Color of the arc.
 * @param {number} lineWidth
 * @param {string} lineStyle
 * @private
 */
DvtStatusMeterGaugeRenderer._drawCircularReferenceLine = function(gauge, container, bounds, angle, color, lineWidth, lineStyle) {
  var context = gauge.getCtx();
  var maxDiameter = Math.min(bounds.w, bounds.h);
  var innerRadius = maxDiameter * .275;
  var outerRadius = maxDiameter * .5;
  if (dvt.Agent.isRightToLeft(gauge.getCtx())) {
    angle = Math.PI - angle;
    angle = angle > 0 ? angle : angle + 2 * Math.PI;
  }
  var p1 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, innerRadius, angle);
  var p2 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, outerRadius, angle);
  var shape = new dvt.Line(context, p1.x, p1.y, p2.x, p2.y);

  var stroke = new dvt.SolidStroke(color, 1, lineWidth);
  if (lineStyle) {
    stroke.setStyle(dvt.Stroke.convertTypeString(lineStyle));
  }
  shape.setStroke(stroke);
  container.addChild(shape);
};

/**
 * Calculates and returns the path command for the circular shape.
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @param {Number} startAngle
 * @param {Number} angleExtent
 * @param {Number} innerRadius
 * @param {Number} outerRadius
 * @return {string} The path command for the shape.
 */
DvtStatusMeterGaugeRenderer.createCircularPathCmd = function(bounds, startAngle, angleExtent, innerRadius, outerRadius) {
  var cmd;
  var p1, p2, p3, p4;

  if (angleExtent < dvt.Math.TWO_PI)
  {
    // Calc the 4 points.  We will draw:
    // 1. Arc from p1 to p2
    // 2. Line/Move from p2 to p3
    // 3. Arc from p3 to p4
    // 4. Line from p4 to p1
    p1 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, outerRadius, startAngle);
    p2 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, outerRadius, startAngle + angleExtent);
    p3 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, innerRadius, startAngle + angleExtent);
    p4 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, innerRadius, startAngle);

    // Create the command and feed it into the path
    cmd = dvt.PathUtils.moveTo(p1.x, p1.y) +
          dvt.PathUtils.arcTo(outerRadius, outerRadius, angleExtent, 1, p2.x, p2.y) +
          dvt.PathUtils.lineTo(p3.x, p3.y) +
          dvt.PathUtils.arcTo(innerRadius, innerRadius, angleExtent, 0, p4.x, p4.y) +
          dvt.PathUtils.closePath();
  }
  else
  {
    // To work around a chrome/safari bug, we draw two segments around each of the outer and inner arcs
    p1 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, outerRadius, startAngle);
    p2 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, outerRadius, startAngle + angleExtent / 2);
    p3 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, innerRadius, startAngle);
    p4 = DvtStatusMeterGaugeRenderer._calcPointOnArc(bounds, innerRadius, startAngle + angleExtent / 2);

    // Create the command and return it
    cmd = dvt.PathUtils.moveTo(p1.x, p1.y) +
          dvt.PathUtils.arcTo(outerRadius, outerRadius, angleExtent / 2, 1, p2.x, p2.y) +
          dvt.PathUtils.arcTo(outerRadius, outerRadius, angleExtent / 2, 1, p1.x, p1.y);

    // Add the inner segment for a hollow center
    if (innerRadius > 0)
      cmd += dvt.PathUtils.moveTo(p4.x, p4.y) +
             dvt.PathUtils.arcTo(innerRadius, innerRadius, angleExtent / 2, 0, p3.x, p3.y) +
             dvt.PathUtils.arcTo(innerRadius, innerRadius, angleExtent / 2, 0, p4.x, p4.y);

    cmd += dvt.PathUtils.closePath();
  }
  return cmd;
};

/**
 * Calculates which Quadrant the angle specified belongs to.
 * @param {Number} angle
 * @param {Boolean} bStart Indicates if the angle is a start angle.
 * @return {Number} The quadrant.
 */
DvtStatusMeterGaugeRenderer.getAngleQuadrant = function(angle, bStart) {
  var quadrant = 1;
  if (bStart) {
    if (angle >= dvt.Math.HALF_PI && angle < Math.PI)
      quadrant = 2;
    else if (angle >= Math.PI && angle < Math.PI * 1.5)
      quadrant = 3;
    else if (angle >= Math.PI * 1.5 && angle < dvt.Math.TWO_PI)
      quadrant = 4;
  }
  else {
    if (angle > dvt.Math.HALF_PI && angle <= Math.PI)
      quadrant = 2;
    else if (angle > Math.PI && angle <= Math.PI * 1.5)
      quadrant = 3;
    else if (angle > Math.PI * 1.5 && angle < dvt.Math.TWO_PI || angle == 0)
      quadrant = 4;
  }
  return quadrant;
};


/**
 * Determines if the metric label is rendered outside the plot area
 * @param {object} options The options object
 * @return {Boolean} Returns true if the plotArea is rendered outside the plot area
 * @private
 */
DvtStatusMeterGaugeRenderer._hasMetricLabelOutsidePlotArea = function(options) {
  var metricLabelPosition = options['metricLabel']['position'];
  return metricLabelPosition == 'auto' || metricLabelPosition == 'outsidePlotArea' || ((metricLabelPosition == 'withLabel' || metricLabelPosition == 'withTitle') && !DvtGaugeStyleUtils.hasLabel(options));
};

/**
 * Adjust the bounds of the rendered circular gauge shape when the angleExtent is less than 360. Returns the metricLabel bounds.
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {Number} innerRadius
 * @param {Number} startAngleRads
 * @param {Number} angleExtentRads
 * @param {Number} endAngle
 * @param {dvt.Rectangle} bounds The available bounds for rendering.
 * @param {Boolean} isRTL Indicates if the page is right to left.
 * @return {dvt.Rectangle} The bounds of the label.
 * @private
 */
DvtStatusMeterGaugeRenderer._adjustCenterAndBounds = function(gauge, innerRadius, startAngleRads, angleExtentRads, endAngle, bounds, isRTL) {
  var labelBounds = null;
  var startQuadrant = DvtStatusMeterGaugeRenderer.getAngleQuadrant(startAngleRads, true);
  var endQuadrant = DvtStatusMeterGaugeRenderer.getAngleQuadrant(endAngle, false);
  var width = bounds.w;
  var height = bounds.h;
  var cx = width / 2;
  var cy = height / 2;
  var maxDiameter, maxInnerDiameter;

  // Arc is within one quadrant
  if (startQuadrant == endQuadrant && angleExtentRads <= dvt.Math.HALF_PI) {
    maxDiameter = Math.min(bounds.w, bounds.h) * 2;
    bounds.w = bounds.w + maxDiameter / 2;
    bounds.h = bounds.h + maxDiameter / 2;
    maxInnerDiameter = maxDiameter * innerRadius;

    // Start and end in first quadrant
    if ((!isRTL && startQuadrant == 1) || (isRTL && startQuadrant == 2)) {
      bounds.x -= maxDiameter / 2;
      bounds.y -= maxDiameter / 2;
      cx = width / 2 - maxDiameter / 4 + 1;
      cy = height / 2 - maxDiameter / 4 + 1;
      labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - 1, bounds.y + bounds.h / 2 - 1, maxInnerDiameter * (3 / 7) - 2, maxInnerDiameter * (2.5 / 7) - 2);
    }

  // Start and end in second quadrant
    else if ((!isRTL && startQuadrant == 2) || (isRTL && startQuadrant == 1)) {
      bounds.y -= maxDiameter / 2;
      cx = width / 2 + maxDiameter / 4 - 1;
      cy = height / 2 - maxDiameter / 4 + 1;
      labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - maxInnerDiameter * (3 / 7) + 1, bounds.y + bounds.h / 2 - 1, maxInnerDiameter * (3 / 7) - 2, maxInnerDiameter * (2.5 / 7) - 2);
    }

  // Start and end in third quadrant
    else if ((!isRTL && startQuadrant == 3) || (isRTL && startQuadrant == 4)) {
      cx = width / 2 + maxDiameter / 4 - 1;
      cy = height / 2 + maxDiameter / 4 - 1;
      labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - maxInnerDiameter * (3 / 7) + 1, bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7) + 1, maxInnerDiameter * (3 / 7) - 2, maxInnerDiameter * (2.5 / 7) - 2);
    }

  // Start and end in fourth quadrant
    else if ((!isRTL && startQuadrant == 4) || (isRTL && startQuadrant == 3)) {
      bounds.x -= maxDiameter / 2;
      cx = width / 2 - maxDiameter / 4 + 1;
      cy = height / 2 + maxDiameter / 4 - 1;
      labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - 1, bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7) + 1, maxInnerDiameter * (3 / 7) - 2, maxInnerDiameter * (2.5 / 7) - 2);
    }
  }

  // Arc spans 2 quadrants
  else if ((startQuadrant % 4 + 1) == endQuadrant && angleExtentRads <= Math.PI) {
    if (startQuadrant == 1 || startQuadrant == 3) {
      maxDiameter = Math.min(bounds.w, bounds.h * 2);
      maxInnerDiameter = maxDiameter * innerRadius;
      if (bounds.w > bounds.h) {
        labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - maxInnerDiameter * (3 / 7), bounds.y + bounds.h - (bounds.h - maxDiameter / 2) / 2 - maxInnerDiameter * (2.75 / 7), maxInnerDiameter * (6 / 7), maxInnerDiameter * (2.5 / 7));
        if (startQuadrant == 1) {
          labelBounds.y = bounds.y + (bounds.h - maxDiameter / 2) / 2 + maxInnerDiameter * (.5 / 7);
          bounds.y -= (maxDiameter / 2 - ((bounds.h - maxDiameter / 2) / 2));
          cy = height / 2 - maxDiameter / 4 + 1;
        }
        else {
          bounds.y += ((bounds.h - maxDiameter / 2) / 2);
          cy = height / 2 + maxDiameter / 4 - 1;
        }
        bounds.h = maxDiameter;
      }
    }
    else {
      maxDiameter = Math.min(bounds.w * 2, bounds.h);
      maxInnerDiameter = maxDiameter * innerRadius;
      if (bounds.w < bounds.h) {
        labelBounds = new dvt.Rectangle(bounds.x + bounds.w - (bounds.w - maxDiameter / 2) / 2 - maxInnerDiameter * (3.25 / 7), bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (3 / 7), maxInnerDiameter * (5 / 7));
        if ((!isRTL && startQuadrant == 4) || (isRTL && startQuadrant == 2)) {
          labelBounds.x = bounds.x + (bounds.w - maxDiameter / 2) / 2 + maxInnerDiameter * (.25 / 7);
          bounds.x -= (maxDiameter / 2 - ((bounds.w - maxDiameter / 2) / 2));
          cx = width / 2 - maxDiameter / 4 + 1;
        }
        else {
          bounds.x += ((bounds.w - maxDiameter / 2) / 2);
          cx = width / 2 + maxDiameter / 4 - 1;
        }
        bounds.w = maxDiameter;
      }
    }
  }

  // Arc spans 3 quadrants
  else if ((endQuadrant % 4 + 1) == startQuadrant && angleExtentRads > Math.PI) {
    if (startQuadrant == 1 && bounds.h > bounds.w) {
      maxDiameter = Math.min(2 * ((bounds.w) / (Math.cos(startAngleRads) + 1)), 2 * ((bounds.w) / (Math.sin(endAngle - Math.PI * 1.5) + 1)), bounds.h);
      maxInnerDiameter = maxDiameter * innerRadius;
      var labelCenterOffset = maxInnerDiameter * (3 / 7) * ((2 * bounds.w) / maxDiameter - 1);
      if (!isRTL) {
        labelBounds = new dvt.Rectangle(bounds.x + maxDiameter / 2 - maxInnerDiameter * (3 / 7), bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (3 / 7) * (1 + ((2 * bounds.w) / maxDiameter - 1)), maxInnerDiameter * (5 / 7));
        cx = maxDiameter / 2;
      }
      else {
        labelBounds = new dvt.Rectangle(bounds.x + bounds.w - maxDiameter / 2 - labelCenterOffset, bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (3 / 7) + labelCenterOffset, maxInnerDiameter * (5 / 7));
        bounds.x -= (maxDiameter - bounds.w);
        cx = -maxDiameter / 2 + width;
      }
      bounds.w = maxDiameter;
    }
    else if (startQuadrant == 2 && bounds.h < bounds.w) {
      maxDiameter = Math.min(2 * ((bounds.h) / (Math.cos(startAngleRads - dvt.Math.HALF_PI) + 1)), 2 * ((bounds.h) / (Math.sin(endAngle) + 1)), bounds.w);
      maxInnerDiameter = maxDiameter * innerRadius;
      labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - maxInnerDiameter * (3 / 7), bounds.y + maxDiameter / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (6 / 7), maxInnerDiameter * (2.5 / 7) * (1 + ((2 * bounds.h) / maxDiameter - 1)));
      bounds.h = maxDiameter;
      cy = maxDiameter / 2;
    }
    else if (startQuadrant == 3 && bounds.h > bounds.w) {
      maxDiameter = Math.min(2 * ((bounds.w) / (Math.cos(startAngleRads - Math.PI) + 1)), 2 * ((bounds.w) / (Math.sin(endAngle - dvt.Math.HALF_PI) + 1)), bounds.h);
      maxInnerDiameter = maxDiameter * innerRadius;
      var labelCenterOffset = maxInnerDiameter * (3 / 7) * ((2 * bounds.w) / maxDiameter - 1);
      if (!isRTL) {
        labelBounds = new dvt.Rectangle(bounds.x + bounds.w - maxDiameter / 2 - labelCenterOffset, bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (3 / 7) + labelCenterOffset, maxInnerDiameter * (5 / 7));
        bounds.x -= (maxDiameter - bounds.w);
        cx = -maxDiameter / 2 + width;
      }
      else {
        labelBounds = new dvt.Rectangle(bounds.x + maxDiameter / 2 - maxInnerDiameter * (3 / 7), bounds.y + bounds.h / 2 - maxInnerDiameter * (2.5 / 7), maxInnerDiameter * (3 / 7) * (1 + ((2 * bounds.w) / maxDiameter - 1)), maxInnerDiameter * (5 / 7));
        cx = maxDiameter / 2;
      }
      bounds.w = maxDiameter;
    }
    else if (startQuadrant == 4 && bounds.h < bounds.w) {
      maxDiameter = Math.min(2 * ((bounds.h) / (Math.cos(startAngleRads - Math.PI * 1.5) + 1)), 2 * ((bounds.h) / (Math.sin(dvt.Math.TWO_PI - endAngle) + 1)), bounds.w);
      maxInnerDiameter = maxDiameter * innerRadius;
      var labelCenterOffset = maxInnerDiameter * (2.5 / 7) * ((2 * bounds.h) / maxDiameter - 1);
      labelBounds = new dvt.Rectangle(bounds.x + bounds.w / 2 - maxInnerDiameter * (3 / 7) , bounds.y + bounds.h - maxDiameter / 2 - labelCenterOffset, maxInnerDiameter * (6 / 7), maxInnerDiameter * (2.5 / 7) + labelCenterOffset);
      bounds.y -= (maxDiameter - bounds.h);
      bounds.h = maxDiameter;
      cy = - maxDiameter / 2 + height;
    }
  }
  gauge.cx = cx;
  gauge.cy = cy;
  gauge.maxInnerDiameter = maxInnerDiameter;
  return labelBounds;
};

/**
 * Renders the custom center content of circular status meter gauge
 * @param {dvt.StatusMeterGauge} gauge The gauge being rendered.
 * @param {object} options The options object.
 * @param {dvt.Rectangle} bounds The bounds of the 360 degree gauge.
 * @param {dvt.Rectangle} containerBounds The available bounds for rendering.
 * @param {number} innerDiameter The inner diameter of the gauge.
 * @private
 */
DvtStatusMeterGaugeRenderer._renderCenterContent = function(gauge, options, bounds, containerBounds, innerDiameter) {
  var outerBounds = new dvt.Rectangle(bounds.x + (bounds.w - innerDiameter) * .5, bounds.y + (bounds.h - innerDiameter) * .5, innerDiameter, innerDiameter);
  outerBounds = outerBounds.getIntersection(containerBounds);
  var innerBounds = new dvt.Rectangle(bounds.x + (bounds.w - innerDiameter / Math.sqrt(2)) * .5, bounds.y + (bounds.h - innerDiameter / Math.sqrt(2)) * .5, innerDiameter / Math.sqrt(2), innerDiameter / Math.sqrt(2));
  innerBounds = innerBounds.getIntersection(containerBounds);
  var centerRenderer = options['center']['renderer'];
  if (centerRenderer) {
    var dataContext = {
      'outerBounds': {'x': outerBounds.x, 'y': outerBounds.y, 'width': outerBounds.w, 'height': outerBounds.h},
      'innerBounds': {'x': innerBounds.x, 'y': innerBounds.y, 'width': innerBounds.w, 'height': innerBounds.h},
      'metricLabel': DvtGaugeRenderer.getFormattedMetricLabel(options['value'], gauge),
      'component': options['_widgetConstructor']
    };
    var context = gauge.getCtx();
    var parentDiv = context.getContainer();

    // Remove existing overlay if there is one
    var existingOverlay = gauge.centerDiv;
    if (existingOverlay)
      parentDiv.removeChild(existingOverlay);

    var customContent = centerRenderer(dataContext);
    if (!customContent)
      return;
    var newOverlay = context.createOverlayDiv();
    newOverlay.appendChild(customContent);
    gauge.centerDiv = newOverlay;
    parentDiv.appendChild(newOverlay);

    // Invoke the overlay attached callback if one is available.
    var callback = context.getOverlayAttachedCallback();
    if (callback)
      callback(newOverlay);
  }
};
