// @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;
