// @flow

export type Position = 'top' | 'right' | 'bottom' | 'left';

export type Placement =
  | 'auto-start'
  | 'auto'
  | 'auto-end'
  | 'top-start'
  | 'top'
  | 'top-end'
  | 'right-start'
  | 'right'
  | 'right-end'
  | 'bottom-end'
  | 'bottom'
  | 'bottom-start'
  | 'left-end'
  | 'left'
  | 'left-start';

export type Offset = {
  top: number,
  left: number,
  width: number,
  height: number,
  position: Position,
};

export type Boundary = 'scrollParent' | 'viewport' | 'window';

export type Behavior = 'flip' | 'clockwise' | 'counterclockwise';

export type Data = {
  instance: Popper,
  placement: Placement,
  originalPlacement: Placement,
  flipped: boolean,
  hide: boolean,
  arrowElement: Element,
  styles: CSSStyleDeclaration,
  arrowStyles: CSSStyleDeclaration,
  boundaries: Object,
  offsets: {
    popper: Offset,
    reference: Offset,
    arrow: {
      top: number,
      left: number,
    },
  },
};

export type ModifierFn = (data: Data, options: Object) => Data;

export type Padding = {
  top?: number,
  bottom?: number,
  left?: number,
  right?: number,
};

export type BaseModifier = {
  order?: number,
  enabled?: boolean,
  fn?: ModifierFn,
};

export type Modifiers = {
  shift?: BaseModifier,
  offset?: BaseModifier & {
    offset?: number | string,
  },
  preventOverflow?: BaseModifier & {
    priority?: Position[],
    padding?: number | Padding,
    boundariesElement?: Boundary | Element,
    escapeWithReference?: boolean,
  },
  keepTogether?: BaseModifier,
  arrow?: BaseModifier & {
    element?: string | Element | null,
  },
  flip?: BaseModifier & {
    behavior?: Behavior | Position[],
    padding?: number | Padding,
    boundariesElement?: Boundary | Element,
    flipVariations?: boolean,
    flipVariationsByContent?: boolean,
  },
  inner?: BaseModifier,
  hide?: BaseModifier,
  applyStyle?: BaseModifier & {
    onLoad?: Function,
    gpuAcceleration?: boolean,
  },
  computeStyle?: BaseModifier & {
    gpuAcceleration?: boolean,
    x?: 'bottom' | 'top',
    y?: 'left' | 'right',
  },

  [name: string]: (BaseModifier & { [string]: * }) | null,
};

export type Options = {
  placement?: Placement,
  positionFixed?: boolean,
  eventsEnabled?: boolean,
  modifiers?: Modifiers,
  removeOnDestroy?: boolean,

  onCreate?: (data: Data) => void,

  onUpdate?: (data: Data) => void,
};

export type ReferenceObject = {
  +clientHeight: number,
  +clientWidth: number,
  +referenceNode?: Node,

  getBoundingClientRect():
    | ClientRect
    | {
        width: number,
        height: number,
        top: number,
        right: number,
        bottom: number,
        left: number,
      },
};

export type Instance = {
  destroy: () => void,
  scheduleUpdate: () => void,
  update: () => void,
  enableEventListeners: () => void,
  disableEventListeners: () => void,
};

declare class Popper {
  static placements: Placement;

  popper: Element;
  reference: Element | ReferenceObject;

  constructor(
    reference: Element | ReferenceObject,
    popper: Element,
    options?: Options
  ): Instance;
}

declare export default typeof Popper;
