text: import Element from '../core/core.element';
  import {isObject, _limitValue} from '../helpers';
  import {addRoundedRectPath} from '../helpers/helpers.canvas';
  import {toTRBL, toTRBLCorners} from '../helpers/helpers.options';
  
  /**
   * Helper function to get the bounds of the bar regardless of the orientation
   * @param {BarElement} bar the bar
   * @param {boolean} [useFinalPosition]
   * @return {object} bounds of the bar
   * @private
   */
  function getBarBounds(bar, useFinalPosition) {
    const {x, y, base, width, height} = bar.getProps(['x', 'y', 'base', 'width', 'height'], useFinalPosition);
  
    let left, right, top, bottom, half;
  
    if (bar.horizontal) {
      half = height / 2;
      left = Math.min(x, base);
      right = Math.max(x, base);
      top = y - half;
      bottom = y + half;
    } else {
      half = width / 2;
      left = x - half;
      right = x + half;
      top = Math.min(y, base);
      bottom = Math.max(y, base);
    }
  
    return {left, top, right, bottom};
  }
  
  function skipOrLimit(skip, value, min, max) {
    return skip ? 0 : _limitValue(value, min, max);
  }
  
  function parseBorderWidth(bar, maxW, maxH) {
    const value = bar.options.borderWidth;
    const skip = bar.borderSkipped;
    const o = toTRBL(value);
  
    return {
      t: skipOrLimit(skip.top, o.top, 0, maxH),
      r: skipOrLimit(skip.right, o.right, 0, maxW),
      b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),
      l: skipOrLimit(skip.left, o.left, 0, maxW)
    };
  }
  
  function parseBorderRadius(bar, maxW, maxH) {
    const {enableBorderRadius} = bar.getProps(['enableBorderRadius']);
    const value = bar.options.borderRadius;
    const o = toTRBLCorners(value);
    const maxR = Math.min(maxW, maxH);
    const skip = bar.borderSkipped;
  
    // If the value is an object, assume the user knows what they are doing
    // and apply as directed.
    const enableBorder = enableBorderRadius || isObject(value);
  
    return {
      topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),
      topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),
      bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),
      bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)
    };
  }
  
  function boundingRects(bar) {
    const bounds = getBarBounds(bar);
    const width = bounds.right - bounds.left;
    const height = bounds.bottom - bounds.top;
    const border = parseBorderWidth(bar, width / 2, height / 2);
    const radius = parseBorderRadius(bar, width / 2, height / 2);
  
    return {
      outer: {
        x: bounds.left,
        y: bounds.top,
        w: width,
        h: height,
        radius
      },
      inner: {
        x: bounds.left + border.l,
        y: bounds.top + border.t,
        w: width - border.l - border.r,
        h: height - border.t - border.b,
        radius: {
          topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),
          topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),
          bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),
          bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)),
        }
      }
    };
  }
  
  function inRange(bar, x, y, useFinalPosition) {
    const skipX = x === null;
    const skipY = y === null;
    const skipBoth = skipX && skipY;
    const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);
  
    return bounds
  		&& (skipX || x >= bounds.left && x <= bounds.right)
  		&& (skipY || y >= bounds.top && y <= bounds.bottom);
  }
  
  function hasRadius(radius) {
    return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
  }
  
  /**
   * Add a path of a rectangle to the current sub-path
   * @param {CanvasRenderingContext2D} ctx Context
   * @param {*} rect Bounding rect
   */
  function addNormalRectPath(ctx, rect) {
    ctx.rect(rect.x, rect.y, rect.w, rect.h);
  }
  
  function inflateRect(rect, amount, refRect = {}) {
    const x = rect.x !== refRect.x ? -amount : 0;
    const y = rect.y !== refRect.y ? -amount : 0;
    const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;
    const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;
    return {
      x: rect.x + x,
      y: rect.y + y,
      w: rect.w + w,
      h: rect.h + h,
      radius: rect.radius
    };
  }
  
  export default class BarElement extends Element {
  
    constructor(cfg) {
      super();
  
      this.options = undefined;
      this.horizontal = undefined;
      this.base = undefined;
      this.width = undefined;
      this.height = undefined;
  
      if (cfg) {
        Object.assign(this, cfg);
      }
    }
  
    draw(ctx) {
      const options = this.options;
      const {inner, outer} = boundingRects(this);
      const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;
      const inflateAmount = 0.33;
  
      ctx.save();
  
      if (outer.w !== inner.w || outer.h !== inner.h) {
        ctx.beginPath();
        addRectPath(ctx, inflateRect(outer, inflateAmount, inner));
        ctx.clip();
        addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));
        ctx.fillStyle = options.borderColor;
        ctx.fill('evenodd');
      }
  
      ctx.beginPath();
      addRectPath(ctx, inflateRect(inner, inflateAmount, outer));
      ctx.fillStyle = options.backgroundColor;
      ctx.fill();
  
      ctx.restore();
    }
  
    inRange(mouseX, mouseY, useFinalPosition) {
      return inRange(this, mouseX, mouseY, useFinalPosition);
    }
  
    inXRange(mouseX, useFinalPosition) {
      return inRange(this, mouseX, null, useFinalPosition);
    }
  
    inYRange(mouseY, useFinalPosition) {
      return inRange(this, null, mouseY, useFinalPosition);
    }
  
    getCenterPoint(useFinalPosition) {
      const {x, y, base, horizontal} = this.getProps(['x', 'y', 'base', 'horizontal'], useFinalPosition);
      return {
        x: horizontal ? (x + base) / 2 : x,
        y: horizontal ? y : (y + base) / 2
      };
    }
  
    getRange(axis) {
      return axis === 'x' ? this.width / 2 : this.height / 2;
    }
  }
  
  BarElement.id = 'bar';
  
  /**
   * @type {any}
   */
  BarElement.defaults = {
    borderSkipped: 'start',
    borderWidth: 0,
    borderRadius: 0,
    enableBorderRadius: true,
    pointStyle: undefined,
    keepHover: false
  };
  
  /**
   * @type {any}
   */
  BarElement.defaultRoutes = {
    backgroundColor: 'backgroundColor',
    borderColor: 'borderColor'
  };
  