var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { InjectVegaSlimmer, MapToComponentField, VegaSlimmer } from 'vega-slimmer/core';
import { BodyPositionCalculationStrategy } from './body-position-calculation-strategy';
import { ScreenPositionCalculationStrategy } from './screen-position-calculation-strategy';
import ResponsiveFormatFacade from '../../formatter/responsive-map-formatter/responsive-format-facade';
/**
 * Calculation and adjustment of positions for appendable elements based on target element positions.
 */
export class PositionCalculationController extends VegaSlimmer {
  constructor() {
    super(...arguments);
    this.bodyPositionStrategy = new BodyPositionCalculationStrategy();
    this.screenPositionStrategy = new ScreenPositionCalculationStrategy();
  }
  /**
   * Clears any pending screen-position scroll animation frame callback.
   * Call this when the appendable is hidden/destroyed.
   */
  clearScreenPositionScrollFrame() {
    this.screenPositionStrategy.clearScrollFrame();
  }
  /**
   * Sets the calculate postion strategy.
   */
  setCalculationStrategy() {
    this.positionStrategy = this.showConfig.isScreenPosition
      ? this.screenPositionStrategy
      : this.bodyPositionStrategy;
  }
  /**
   * Calculates the position and applies it to the appendable element.
   *
   * @param {boolean} [usePreviousPlacement] - Should keep use previous placement and alignment.
   */
  adjustPosition(usePreviousPlacement) {
    if (!this.positionStrategy.shouldAdjustPosition())
      return;
    this.adjustWidth();
    this.adjustHeight();
    this.applyPosition(this.positionStrategy.getPosition(usePreviousPlacement));
  }
  /**
   * Apply the position to the appendable element.
   *
   * @param {ElementPositionBox} positionBox - position box
   */
  applyPosition(positionBox) {
    const appendableElement = this.showConfig.appendableElement;
    // TODO: should control position style in element appender but in component level
    // https://gethired.atlassian.net/browse/VD-7883
    if (this.showConfig.isScreenPosition) {
      appendableElement.style.position = 'fixed';
    }
    else {
      if (appendableElement.classList.contains('date-picker-content-box')) {
        appendableElement.style.position = 'absolute';
      }
    }
    appendableElement.style.left = `${positionBox.left}px`;
    appendableElement.style.top = `${positionBox.top}px`;
    appendableElement.classList.remove('vega-placement-left', 'vega-placement-right', 'vega-placement-top', 'vega-placement-bottom', 'vega-no-suitable-placement');
    appendableElement.classList.remove('vega-alignment-start', 'vega-alignment-center', 'vega-alignment-end', 'vega-no-suitable-alignment');
    if (positionBox.placement) {
      appendableElement.classList.add(`vega-placement-${positionBox.placement}`);
    }
    else {
      appendableElement.classList.add(`vega-no-suitable-placement`);
    }
    if (positionBox.alignment) {
      appendableElement.classList.add(`vega-alignment-${positionBox.alignment}`);
    }
    else {
      appendableElement.classList.add(`vega-no-suitable-alignment`);
    }
  }
  /**
   * It sets the width of the appendable element to the width of the target element if the
   * matchTargetWidth property is true, otherwise it sets the width of the appendable element to the
   * size property
   *
   * @returns {void}
   */
  adjustWidth() {
    if (this.showConfig.matchTargetWidth) {
      const { width: hostWidth } = this.showConfig.target.getBoundingClientRect();
      this.setRelatedWidth('width', hostWidth);
    }
    else {
      this.setRelatedWidth('width', this.showConfig.size);
      this.setRelatedWidth('minWidth', this.showConfig.minWidth);
      this.setRelatedWidth('maxWidth', this.showConfig.maxWidth);
    }
  }
  /**
   * It takes a name and a value, formats the value, and then sets the style of the element
   *
   * @param {'width' | 'maxWidth' | 'minWidth'} name - 'width' | 'maxWidth' | 'minWidth'
   * @param {ResponsiveType<SizeType>} value - ResponsiveType<SizeType>
   * @returns {void}
   */
  setRelatedWidth(name, value) {
    if (!value)
      return;
    const formattedValue = ResponsiveFormatFacade.format(value, 'size');
    if (formattedValue) {
      this.showConfig.appendableElement.style[name] =
        typeof formattedValue === 'number' ? `${formattedValue}px` : formattedValue;
    }
  }
  /**
   * > If the `matchContainerHeight` property is set to `true`, then the maximum height of the dropdown
   * is set to the height of the container minus the height of the target minus the top offset of the
   * target
   *
   * @returns {void}
   */
  adjustHeight() {
    if (this.showConfig.matchContainerHeight && !this.showConfig.isScreenPosition) {
      const { height: targetHeight } = this.showConfig.target.getBoundingClientRect();
      let maxHeight = this.container.scrollHeight - this.showConfig.target.offsetTop - targetHeight;
      if (this.showConfig.maxHeight) {
        maxHeight = Math.min(maxHeight, this.showConfig.maxHeight);
      }
      this.showConfig.appendableElement.style.maxHeight = `${maxHeight}px`;
    }
    else if (this.showConfig.maxHeight) {
      this.showConfig.appendableElement.style.maxHeight = `${this.showConfig.maxHeight}px`;
    }
  }
}
__decorate([
  MapToComponentField()
], PositionCalculationController.prototype, "container", void 0);
__decorate([
  MapToComponentField()
], PositionCalculationController.prototype, "showConfig", void 0);
__decorate([
  InjectVegaSlimmer()
], PositionCalculationController.prototype, "bodyPositionStrategy", void 0);
__decorate([
  InjectVegaSlimmer()
], PositionCalculationController.prototype, "screenPositionStrategy", void 0);
