import { PhotoSwipeOptions } from 'photoswipe';
import PhotoSwipeLightbox from 'photoswipe/lightbox';

declare enum LabelVisibility {
    HOVER = "hover",
    NEVER = "never",
    ALWAYS = "always"
}
declare interface ItemOptions {
    /**
     * Enables ability to zoom images in photoswipe
     */
    lightbox?: boolean;
    /**
     * Add a checkbox to select image
     */
    selectable?: boolean;
    /**
     * Activable emits 'activate' event when link is not provided. It takes place where the link used to be :
     * in caption or in image if lightbox is false
     */
    activable?: boolean;
    gap?: number;
    labelVisibility?: LabelVisibility;
}
declare class Item<Model extends ModelAttributes> {
    private readonly document;
    private readonly options;
    readonly model: Model;
    /**
     * Cleaned title, used for label / button
     */
    readonly sanitizedTitle: string;
    /**
     * Reference to the select button
     */
    private _checkbox;
    /**
     * Element referring the "button" containing the label
     */
    private figcaption;
    /**
     *
     * @param model Contains the source data given for an item (e.g. object instance from database with id etc...)
     */
    constructor(document: Document, options: ItemOptions, model: Model);
    /**
     * Actual row index in the list
     */
    private _row;
    get row(): number;
    set row(value: number);
    /**
     * Computed size (real used size)
     */
    private _width;
    get width(): number;
    set width(value: number);
    private _height;
    get height(): number;
    set height(value: number);
    private _cropped;
    get cropped(): boolean;
    set cropped(value: boolean);
    /**
     * Wherever item is selected or not
     */
    private _selected;
    get selected(): boolean;
    /**
     * Item root element reference (figure)
     */
    private _rootElement;
    get rootElement(): HTMLElement | null;
    get checkbox(): HTMLButtonElement | null;
    get enlargedWidth(): number;
    get enlargedHeight(): number;
    /**
     * Create DOM elements according to element raw data (thumbnail and enlarged urls)
     * Also apply border-radius at this level because it never changed threw time
     *
     *
     * Base structure is always the same :
     *
     * <figure>
     *     <img>
     *     <figcaption>
     * </figure>
     *
     * But depending on settings, we can add <a> inside <figcaption> or wrap around <figure>
     */
    init(): HTMLElement;
    setLabelHover(activate: boolean): void;
    /**
     * Use computed (organized) data to apply style (size and margin) to elements on DOM
     * Does not apply border-radius because is used to restyle data on browser resize, and border-radius don't change.
     */
    style(): void;
    private emitSelectEvent;
    toggleSelect(): void;
    private throwNotSelectableError;
    select(): void;
    unselect(): void;
    remove(): void;
    private updateAriaSelectedStatus;
    private getEmptyLinkOrButton;
    /**
     * Label is visible if options mention hover or always
     * @private
     */
    private showLabel;
    private getFigure;
    private getImage;
    private getEmptyCaption;
    private getCheckbox;
    private handleActivation;
    private handleZoom;
}

type ObjectFit = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down' | 'inherit' | 'initial' | 'unset';
type ObjectPosition = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top left' | 'top right' | 'top center' | 'bottom left' | 'bottom right' | 'bottom center' | 'center left' | 'center right' | string;
/**
 * A map of all possible event and the structure of their details
 */
interface CustomEventDetailMap<T extends ModelAttributes> {
    activate: {
        item: Item<T>;
        event: MouseEvent | KeyboardEvent;
    };
    'item-added-to-dom': Item<T>;
    'item-displayed': Item<T>;
    pagination: {
        offset: number;
        limit: number;
    };
    select: Item<T>[];
}
/**
 * Augment the global namespace with our custom events
 * See: https://github.com/Microsoft/TypeScript/issues/28357
 */
declare global {
    interface HTMLElementEventMap {
        activate: CustomEvent;
        'item-added-to-dom': CustomEvent;
        'item-displayed': CustomEvent;
        pagination: CustomEvent;
        select: CustomEvent;
    }
}
interface SizedModel {
    /**
     * Height in pixels of the enlarged version the image
     * If photoswipe is used, the size of the photoswipe enlarged image is required
     * If photoswipe is not used, any size that match the ratio is enough
     */
    enlargedWidth: number;
    /**
     * Width in pixels of the enlarged version the image
     * If photoswipe is used, the size of the photoswipe enlarged image is required
     * If photoswipe is not used, any size that match the ratio is enough
     */
    enlargedHeight: number;
}
interface ModelAttributes extends SizedModel {
    /**
     * Source link for thumbnail image
     */
    thumbnailSrc: string;
    /**
     * Source link for enlarged (photoswipe) image
     */
    enlargedSrc?: string;
    /**
     * Label of item (or button)
     */
    title?: string;
    /**
     * Href link
     */
    link?: string;
    /**
     * a href target attribute
     */
    linkTarget?: '_blank' | '_self' | '_parent' | '_top';
    /**
     * Hex color
     */
    color?: string;
    /**
     * If item is selected
     */
    selected?: boolean;
    /**
     * Background size, default : cover
     */
    objectFit?: ObjectFit;
    /**
     * Background position, default : center
     */
    objectPosition?: ObjectPosition;
    /**
     * Short text describing specifically the image
     */
    alt?: string;
}
interface GalleryOptions extends ItemOptions {
    rowsPerPage?: number;
    minRowsAtStart?: number;
    infiniteScrollOffset?: number;
    photoSwipeOptions?: PhotoSwipeOptions;
    photoSwipePluginsInitFn?: ((lighbox: PhotoSwipeLightbox) => void) | null;
    ssr?: {
        /**
         * In SSR mode, if the gallery width cannot be computed, it will fallback to this value
         */
        galleryWidth: number;
    };
}
declare abstract class AbstractGallery<Model extends ModelAttributes = ModelAttributes> {
    protected elementRef: HTMLElement;
    protected scrollElementRef?: (HTMLElement | null) | undefined;
    /**
     * Default options
     */
    protected options: Required<GalleryOptions>;
    /**
     * Images wrapper container
     */
    protected bodyElementRef: HTMLElement;
    /**
     * Items for which container has been added to dom, but image has not been queries yet
     */
    protected scrollBufferedItems: Item<Model>[];
    /**
     * Debounce function
     * Runs a small delay after last image has been added to dom
     * When it runs, images are loaded (appear with fade) and more images are queries to preserve a buffer of
     * out-of-dom items
     */
    protected flushBufferedItems: () => void;
    /**
     * Number of items to query on buffer flushing
     */
    protected requiredItems: number;
    protected readonly document: Document;
    /**
     * PhotoSwipe Lightbox object
     */
    protected psLightbox: PhotoSwipeLightbox | null;
    /**
     * Used to test the scroll direction
     * Avoid to load more images when scrolling up
     */
    private old_scroll_top;
    /**
     * Stores page index that have been emitted
     * Keeps a log of pages already asked to prevent to ask them multiple times
     */
    private requestedIndexesLog;
    /**
     * Reference to next button element
     */
    private nextButton;
    /**
     *
     * @param elementRef
     * @param options
     * @param scrollElementRef
     */
    constructor(elementRef: HTMLElement, options: GalleryOptions, scrollElementRef?: (HTMLElement | null) | undefined);
    /**
     * Get PhotoSwipe Lightbox
     */
    get photoSwipe(): PhotoSwipeLightbox | null;
    /**
     * Get currently selected PhotoSwipe image
     */
    get photoSwipeCurrentItem(): Model | null;
    /**
     * Complete collection of images
     * @type {Array}
     */
    protected _collection: Item<Model>[];
    get collection(): Item<Model>[];
    /**
     * Partial set of items that represent the visible items
     * @type {Item[]}
     * @private
     */
    protected _domCollection: Item<Model>[];
    get domCollection(): Item<Model>[];
    get selectedItems(): Item<Model>[];
    get width(): number;
    addItemToPhotoSwipeCollection(item: Item<Model>): void;
    /**
     * Add items to collection
     * Transform given list of models into inner Items
     * @param models list of models
     */
    addItems(models: Model[]): void;
    setLabelHover(activate: boolean): void;
    /**
     * Select all items given to the gallery, whenever they are in the DOM or not
     */
    selectCollection(): Item<Model>[];
    /**
     * Select all items in the DOM
     * Ignores buffered items
     */
    selectDomCollection(): Item<Model>[];
    private selectItems;
    /**
     * Unselect all selected elements
     */
    unselectAllItems(): void;
    /**
     * Allows to use the same approach and method name to listen as gallery events on DOM or on javascript gallery
     * object
     *
     * Gallery requests items when it's instantiated. But user may subscribe after creation, so we need to request
     * again if user subscribes by this function.
     *
     * @param name
     * @param callback
     * @param options An object that specifies characteristics about the event listener. The available options are, see
     *     addEventListener official documentation
     */
    addEventListener<K extends keyof CustomEventDetailMap<Model>>(name: K, callback: (evt: CustomEvent<CustomEventDetailMap<Model>[K]>) => void, options?: boolean | AddEventListenerOptions): void;
    /**
     * Public api for empty function
     * Emits a pagination event
     */
    clear(): void;
    /**
     * Return copy of options to prevent modification
     */
    getOptions(): GalleryOptions;
    /**
     * Override current collection
     * @param {Item[]} items
     */
    setItems(items: Model[]): void;
    /**
     *
     */
    abstract organizeItems(items: Item<Model>[], fromRow?: number, toRow?: number): void;
    /**
     * Initializes PhotoSwipe
     */
    protected photoSwipeInit(): void;
    /**
     *
     */
    protected abstract getEstimatedColumnsPerRow(): number;
    /**
     * AbstractRowGallery + Masonry
     */
    protected abstract onScroll(): void;
    /**
     * AbstractRowGallery + Masonry
     */
    protected abstract onPageAdd(): void;
    protected abstract getFormatName(): string;
    /**
     * Return number of rows to show per page to fill the empty space until the bottom of the screen
     * Should grant all the space is used or more, but not less.
     * @returns {number}
     */
    protected abstract getEstimatedRowsPerPage(): number;
    /**
     * Fire pagination event
     * Information provided in the event allows to retrieve items from the server using given data :
     * "offset" and "limit" that have the same semantic that respective attributes in mySQL.
     *
     * The gallery asks for items it needs, including some buffer items that are not displayed when given but are
     * available to be added immediately to DOM when user scrolls.
     *
     */
    protected requestItems(): void;
    /**
     * Returns option.rowsPerPage is specified.
     * If not returns the estimated number of rows to fill the rest of the vertical space in the screen
     * @returns {number}
     */
    protected getRowsPerPage(): number;
    /**
     * Add given item to DOM and to domCollection
     * @param {Item} item
     * @param destination
     */
    protected addItemToDOM(item: Item<Model>, destination?: HTMLElement): void;
    protected updateNextButtonVisibility(): void;
    /**
     * If infinite scroll (no option.rowsPerPage provided), a minimum height is setted to force gallery to overflow
     * from viewport. This activates the scroll before adding items to dom. This prevents the scroll to fire new resize
     * event and recompute all gallery twice on start.
     */
    protected extendToFreeViewport(): void;
    /**
     * Space between the top of the gallery wrapper (parent of gallery root elementRef) and the bottom of the window
     */
    protected getGalleryVisibleHeight(): number;
    protected startResize(): void;
    protected endResize(): void;
    protected dispatchEvent<K extends keyof CustomEventDetailMap<Model>>(name: K, data: CustomEventDetailMap<Model>[K]): void;
    /**
     * Effectively empty gallery, and should prepare container to receive new items
     */
    protected empty(): void;
    /**
     * Listen to scroll event and manages rows additions for lazy load
     * @param {HTMLElement | Document} element
     */
    private bindScroll;
    get rootElement(): HTMLElement;
    get bodyElement(): HTMLElement;
}

interface RatioLimits {
    min?: number;
    max?: number;
}

declare abstract class AbstractRowGallery<Model extends ModelAttributes = ModelAttributes> extends AbstractGallery<Model> {
    protected elementRef: HTMLElement;
    protected scrollElementRef?: (HTMLElement | null) | undefined;
    protected constructor(elementRef: HTMLElement, options: GalleryOptions, scrollElementRef?: (HTMLElement | null) | undefined);
    protected onScroll(): void;
    protected onPageAdd(): void;
    /**
     * Add given number of rows to DOM
     * @param rows
     */
    protected addRows(rows: number): void;
    protected endResize(): void;
}

interface NaturalGalleryOptions extends GalleryOptions {
    rowHeight: number;
    ratioLimit?: RatioLimits;
}
declare class Natural<Model extends ModelAttributes = ModelAttributes> extends AbstractRowGallery<Model> {
    /**
     * Options after having been defaulted
     */
    protected options: Required<NaturalGalleryOptions>;
    constructor(elementRef: HTMLElement, options: NaturalGalleryOptions, scrollElementRef?: HTMLElement | null);
    static organizeItems<T extends ModelAttributes>(gallery: Natural<T>, items: Item<T>[], fromRow?: number, toRow?: number | null, currentRow?: number | null): void;
    /**
     * Compute sizes for given images to fit in given row width
     * Items are updated
     */
    static computeSizes<T extends ModelAttributes>(chunk: Item<T>[], containerWidth: number | null, margin: number, row: number, maxRowHeight?: number | null, ratioLimits?: RatioLimits): void;
    static getRowWidth(models: SizedModel[], maxRowHeight: number, margin: number, ratioLimits?: RatioLimits): number;
    static getRowHeight(models: SizedModel[], containerWidth: number, margin: number, ratioLimits?: RatioLimits): number;
    /**
     * Return the ratio format of models as if they were a single image
     */
    static getRatios(models: SizedModel[], ratioLimits?: RatioLimits): number;
    addRows(rows: number): void;
    organizeItems(items: Item<Model>[], fromRow?: number, toRow?: number): void;
    protected getFormatName(): string;
    protected endResize(): void;
    protected getEstimatedColumnsPerRow(): number;
    protected getEstimatedRowsPerPage(): number;
    private completeLastRow;
}

declare interface ColumnOptions {
    width: number;
    gap: number;
}
declare class Column<Model extends ModelAttributes> {
    private options;
    private readonly collection;
    private readonly _elementRef;
    constructor(document: Document, options: ColumnOptions);
    get height(): number;
    get elementRef(): HTMLElement;
    addItem(item: Item<Model>): void;
}

interface MasonryGalleryOptions extends GalleryOptions {
    columnWidth: number;
    ratioLimit?: RatioLimits;
}
declare class Masonry<Model extends ModelAttributes = ModelAttributes> extends AbstractGallery<Model> {
    /**
     * Options after having been defaulted
     */
    protected options: Required<MasonryGalleryOptions>;
    /**
     * Regroup the list of columns
     */
    protected columns: Column<Model>[];
    constructor(elementRef: HTMLElement, options: MasonryGalleryOptions, scrollElementRef?: HTMLElement | null);
    /**
     * Compute sides with 1:1 ratio
     */
    static organizeItems<T extends ModelAttributes>(gallery: Masonry<T>, items: Item<T>[], fromIndex?: number, toIndex?: number | null): void;
    organizeItems(items: Item<Model>[], fromRow?: number, toRow?: number): void;
    protected onScroll(): void;
    protected onPageAdd(): void;
    protected getFormatName(): string;
    protected getEstimatedColumnsPerRow(): number;
    protected getEstimatedRowsPerPage(): number;
    /**
     * Use current gallery height as reference. To fill free space it add images until the gallery height changes, then are one more row
     */
    protected addUntilFill(): void;
    protected addItemToDOM(item: Item<Model>): void;
    protected endResize(): void;
    protected addColumns(): void;
    protected empty(): void;
    /**
     * Returns true if at least one column doesn't overflow on the bottom of the viewport
     */
    private viewportIsNotFilled;
    private addItemsToDom;
    /**
     * Return square side size
     */
    private getColumnWidth;
    private getShortestColumn;
}

interface SquareGalleryOptions extends GalleryOptions {
    itemsPerRow: number;
}
declare class Square<Model extends ModelAttributes = ModelAttributes> extends AbstractRowGallery<Model> {
    /**
     * Options after having been defaulted
     */
    protected options: Required<SquareGalleryOptions>;
    constructor(elementRef: HTMLElement, options: SquareGalleryOptions, scrollElementRef?: HTMLElement | null);
    /**
     * Compute sides with 1:1 ratio
     */
    static organizeItems<T extends ModelAttributes>(gallery: Square<T>, items: Item<T>[], firstRowIndex?: number, toRow?: number | null): void;
    organizeItems(items: Item<Model>[], fromRow?: number, toRow?: number): void;
    protected getEstimatedColumnsPerRow(): number;
    protected getEstimatedRowsPerPage(): number;
    /**
     * Return square side size
     */
    protected getItemSideSize(): number;
    protected getFormatName(): string;
}

export { type CustomEventDetailMap, Item, type ItemOptions, LabelVisibility, Masonry, type MasonryGalleryOptions, type ModelAttributes, Natural, type NaturalGalleryOptions, Square, type SquareGalleryOptions };
