import { Bounds } from 'pixi.js';
import { CanvasSource } from 'pixi.js';
import { Container } from 'pixi.js';
import { Graphics } from 'pixi.js';
import type { LineCap } from 'pixi.js';
import type { LineJoin } from 'pixi.js';
import type { Matrix } from 'pixi.js';
import type { PointData } from 'pixi.js';
import { Rectangle } from 'pixi.js';
import { Renderer } from 'pixi.js';
import type { RenderTexture } from 'pixi.js';
import { Sprite } from 'pixi.js';
import { TextStyle } from 'pixi.js';
import { Texture } from 'pixi.js';
import { TextureSource } from 'pixi.js';

/**
 * @public
 * @typeParam N - The internal property for marking rectangles.
 */
declare interface AreaAllocator<N>
    {
    readonly width: number;
    readonly height: number;

    allocate(width: number, height: number): Rectangle & N;
    free(area: N): void;
}

/**
 * This allocator issues texture backed by a canvas. You can draw on to that canvas to source
 * each texture.
 *
 * @public
 */
declare class CanvasTextureAllocator extends TextureAllocator<CanvasSource>
    {
    /**
     * Creates a texture slab backed by a canvas.
     */
    protected override createSlabSource(): CanvasSource
        {
        return new CanvasSource({
            height: this.slabHeight,
            width: this.slabWidth,
        });
    }
}

/**
 * @ignore
 * @public
 */
export declare type Contour = Array<number>;

/**
 * The fill rules supported by {@link Path}.
 *
 * @public
 */
export declare enum FILL_RULE {
    NONZERO = "nonzero",
    EVENODD = "evenodd"
}

/**
 * Get information on the internal cache of the SVG loading mechanism.
 *
 * @public
 * @returns A view on the cache - clear() method and a size property.
 */
export declare function getLoaderCache(): {
    clear(): void;
    size: number;
};

/**
 * Inherited paint, used for &lt;use /&gt; elements. The properties used on the inherited paint do not
 * override those on the parent.
 *
 * @public
 */
export declare class InheritedPaintProvider implements Paint {
    parent: Paint;
    provider: Paint;
    /**
     * Composes a `Paint` that will inherit properties from the `parent` if the `provider` does not
     * define them.
     *
     * @param parent
     * @param provider
     */
    constructor(parent: Paint, provider: Paint);
    get dirtyId(): number;
    get fill(): number | string;
    get opacity(): number;
    get stroke(): number | string;
    get strokeDashArray(): number[];
    get strokeDashOffset(): number;
    get strokeLineCap(): LineCap;
    get strokeLineJoin(): LineJoin;
    get strokeMiterLimit(): number;
    get strokeWidth(): number;
}

/**
 * A `MaskServer` will lazily render its content's luminance into its render-texture's alpha
 * channel using the luminance-alpha filter. The `dirtyId` flag can be used to make it re-render its
 * contents. It is intended to be used as a sprite-mask, where black pixels are invisible and white
 * pixels are visible (i.e. black pixels are filtered to alpha = 0, while white pixels are filtered
 * to alpha = 1. The rest are filtered to an alpha such that 0 < alpha < 1.). This is in compliance
 * with [CSS Masking Module Level 1](https://www.w3.org/TR/css-masking-1/#MaskElement).
 *
 * **Note: This functionality is disabled in PixiJS 8's renderer**
 *
 * @public
 * @ignore
 */
export declare class MaskServer extends Sprite {
    /**
     * Flags when re-renders are required due to content updates.
     */
    dirtyId: number;
    /**
     * Flags when the content is re-rendered and should be equal to `this.dirtyId` when the texture
     * is update-to-date.
     */
    updateId: number;
    /**
     * @param texture - The render-texture that will cache the contents.
     */
    constructor(texture: RenderTexture);
    /**
     * @override
     */
    /**
     * Create a mask that will overlay on top of the given display-object using the texture of this
     * mask server.
     *
     * @param displayObject - The mask target.
     */
    createMask(_: Container): MaskSprite;
}

/**
 * A sprite that does not render anything. It can be used as a mask whose bounds can be updated by adding it
 * as a child of the mask-target.
 *
 * @public
 * @see MaskServer.createMask
 * @ignore
 */
export declare class MaskSprite extends Sprite {
    render(_: Renderer): void;
}

/**
 * Internal, parsed form of painting attributes. If a paint attribute was not defined, it **must** be
 * `null` (not `undefined`).
 *
 * @public
 * @see https://www.w3.org/TR/SVG2/painting.html#Introduction
 */
export declare interface Paint {
    /**
     * The interior paint for the shape.
     */
    readonly fill: number | string;
    /**
     * The opacity of the fill.
     */
    readonly opacity: number;
    /**
     * The color of the stroke outline applied on the shape.
     */
    readonly stroke: number | string;
    /**
     * The dash pattern for stroking the shape.
     */
    readonly strokeDashArray: number[];
    /**
     * The distance into the dash pattern at which the stroking is started.
     */
    readonly strokeDashOffset: number;
    /**
     * The line caps applied at the end of the stroke. This is not applied for closed shapes.
     */
    readonly strokeLineCap: LineCap;
    /**
     * The line join applied at the joint to line segments.
     */
    readonly strokeLineJoin: LineJoin;
    /**
     * The maximum miter distance.
     */
    readonly strokeMiterLimit: number;
    /**
     * The width of the stroke outline applied on the shape.
     */
    readonly strokeWidth: number;
    /**
     * Flags when the paint is updated.
     */
    readonly dirtyId: number;
}

/**
 * Provides the `Paint` for an `SVGElement`. It will also respond to changes in the attributes of the element
 * (not implemented).
 *
 * @public
 */
export declare class PaintProvider implements Paint {
    element: SVGElement;
    fill: number | string;
    opacity: number;
    stroke: number | string;
    strokeDashArray: number[];
    strokeDashOffset: number;
    strokeLineCap: LineCap;
    strokeLineJoin: LineJoin;
    strokeMiterLimit: number;
    strokeWidth: number;
    dirtyId: number;
    /**
     * @param element - The element whose paint is to be provided.
     */
    constructor(element: SVGElement);
    /**
     * Parses the color attribute into an RGBA hexadecimal equivalent, if encoded. If the `colorString` is `none` or
     * is a `url(#id)` reference, it is returned as is.
     *
     * @param colorString
     * @see https://github.com/bigtimebuddy/pixi-svg/blob/89e4ab834fa4ef05b64741596516c732eae34daa/src/SVG.js#L106
     */
    static parseColor(colorString: string): number | string;
}

/**
 * [Paint Servers]{@link https://svgwg.org/svg-next/pservers.html} are implemented as textures. This class is a lazy
 * wrapper around paint textures, which can only be generated using the `renderer` drawing to the screen.
 *
 * @public
 */
export declare class PaintServer {
    paintServer: SVGGradientElement | SVGPatternElement;
    paintTexture: RenderTexture;
    paintContexts: Map<Renderer, number>;
    dirtyId: number;
    /**
     * Creates a `PaintServer` wrapper.
     *
     * @param paintServer
     * @param paintTexture
     */
    constructor(paintServer: SVGGradientElement | SVGPatternElement, paintTexture: RenderTexture);
    /**
     * Ensures the paint texture is updated for the renderer's WebGL context. This should be called before using the
     * paint texture to render anything.
     *
     * @param renderer - The renderer that will use the paint texture.
     */
    resolvePaint(renderer: Renderer): void;
    /**
     * Calculates the optimal texture dimensions for the paint texture, given the bounding box of the
     * object applying it. The paint texture is resized accordingly.
     *
     * If the paint texture is sized smaller than the bounding box, then it is expected that it will
     * be scaled up to fit it.
     *
     * @param bbox - The bounding box of the object applying the paint texture.
     */
    resolvePaintDimensions(bbox: Rectangle): void;
    /**
     * Renders the paint texture using the renderer immediately.
     *
     * @param renderer - The renderer to use for rendering to the paint texture.
     */
    updatePaint(renderer: Renderer): void;
    /**
     * Renders `this.paintServer` as a `SVGLinearGradientElement`.
     *
     * @param renderer - The renderer being used to render the paint texture.
     */
    private linearGradient;
    /**
     * Renders `this.paintServer` as a `SVGRadialGradientElement`.
     *
     * @param renderer - The renderer being used to render the paint texture.
     */
    private radialGradient;
    /**
     * Extracts the color-stops from the children of a `SVGGradientElement`.
     *
     * @param stopElements - The children of a `SVGGradientElement`. You can get it via `element.children`.
     * @return The color stops that can be fed into {@link GradientFactory}.
     */
    private createColorStops;
}

/** @internal */
export declare const PATH = 100;

/**
 * Shape extension for Graphics
 *
 * @public
 */
export declare class Path {
    /**
     * The list of contours of this path, where a contour is a list of points.
     *
     * @member {Array.Array.<number>>}
     */
    contours: Contour[];
    /** The fill rule of this path. */
    fillRule: FILL_RULE;
    /** The type of shape. This is always equal to 100 for now. */
    type: number;
    /** Whether the calculated bounds are dirty. */
    protected dirty: boolean;
    /** The calculated bounds of this path. */
    protected bounds: Bounds;
    /**
     * Initializes the path with zero contours and a non-zero fill rule.
     */
    constructor();
    /**
     * Gets the points of the last contour in this path. If there are no contours, one is created.
     */
    get points(): number[];
    /**
     * Calculates whether the point (x, y) is inside this path or not.
     *
     * @param x - The x-coordinate of the point.
     * @param y - The y-coordinate of the point.
     * @return Whether (x, y) is inside this path.
     */
    contains(x: number, y: number): boolean;
    /**
     * Clone this path.
     */
    clone(): Path;
    /**
     * Closes the last contour of this path and pushes a new one.
     */
    closeContour(): void;
    /**
     * This should be called when the path is updated so that the hit-testing bounds are recalculated.
     */
    invalidate(): void;
    toString(): string;
    /**
     * Recalculates the bounds of this path and sets {@link Path.bounds this.bounds}.
     */
    private calculateBounds;
    /**
     * Hit-tests the point (x, y) based on the even-odd fill rule.
     *
     * @see http://geomalgorithms.com/a03-_inclusion.html
     */
    private hitEvenOdd;
    /**
     * Hit-tests the point (x, y) based on non-zero fill rule.
     *
     * @see http://geomalgorithms.com/a03-_inclusion.html
     */
    private hitNonZero;
}

/**
 * This node can be used to directly embed the following elements:
 *
 * | Interface           | Element            |
 * | ------------------- | ------------------ |
 * | SVGGElement         | &lt;g /&gt;        |
 * | SVGCircleElement    | &lt;circle /&gt;   |
 * | SVGLineElement      | &lt;line /&gt;     |
 * | SVGPolylineElement  | &lt;polyline /&gt; |
 * | SVGPolygonElement   | &lt;polygon /&gt;  |
 * | SVGRectElement      | &lt;rect /&gt;     |
 *
 * It also provides an implementation for dashed stroking, by adding the `dashArray` and `dashOffset` properties
 * to `LineStyle`.
 *
 * @public
 */
export declare class SVGGraphicsNode extends Graphics {
    paintServers: PaintServer[];
    protected _sceneContext: SVGSceneContext;
    constructor(context: SVGSceneContext);
    /**
     * Draws an elliptical arc.
     *
     * @param cx - The x-coordinate of the center of the ellipse.
     * @param cy - The y-coordinate of the center of the ellipse.
     * @param rx - The radius along the x-axis.
     * @param ry - The radius along the y-axis.
     * @param startAngle - The starting eccentric angle, in radians (0 is at the 3 o'clock position of the arc's circle).
     * @param endAngle - The ending eccentric angle, in radians.
     * @param xAxisRotation - The angle of the whole ellipse w.r.t. x-axis.
     * @param anticlockwise - Specifies whether the drawing should be counterclockwise or clockwise.
     * @return This Graphics object. Good for chaining method calls.
     */
    ellipticArc(cx: number, cy: number, rx: number, ry: number, startAngle: number, endAngle: number, xAxisRotation?: number, anticlockwise?: boolean): this;
    /**
     * Draws an elliptical arc to the specified point.
     *
     * If rx = 0 or ry = 0, then a line is drawn. If the radii provided are too small to draw the arc, then
     * they are scaled up appropriately.
     *
     * @param endX - the x-coordinate of the ending point.
     * @param endY - the y-coordinate of the ending point.
     * @param rx - The radius along the x-axis.
     * @param ry - The radius along the y-axis.
     * @param xAxisRotation - The angle of the ellipse as a whole w.r.t/ x-axis.
     * @param anticlockwise - Specifies whether the arc should be drawn counterclockwise or clockwise.
     * @param largeArc - Specifies whether the larger arc of two possible should be choosen.
     * @return This Graphics object. Good for chaining method calls.
     * @see https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands
     * @see https://www.w3.org/TR/SVG2/implnote.html#ArcImplementationNotes
     */
    ellipticArcTo(endX: number, endY: number, rx: number, ry: number, xAxisRotation?: number, anticlockwise?: boolean, largeArc?: boolean): this;
    /**
     * Embeds the `SVGCircleElement` into this node.
     *
     * @param element - The circle element to draw.
     */
    embedCircle(element: SVGCircleElement): void;
    /**
     * Embeds the `SVGEllipseElement` into this node.
     *
     * @param element - The ellipse element to draw.
     */
    embedEllipse(element: SVGEllipseElement): void;
    /**
     * Embeds the `SVGLineElement` into this node.
     *
     * @param element - The line element to draw.
     */
    embedLine(element: SVGLineElement): void;
    /**
     * Embeds the `SVGRectElement` into this node.
     *
     * @param element - The rectangle element to draw.
     */
    embedRect(element: SVGRectElement): void;
    /**
     * Embeds the `SVGPolygonElement` element into this node.
     *
     * @param element - The polygon element to draw.
     */
    embedPolygon(element: SVGPolygonElement): void;
    /**
     * Embeds the `SVGPolylineElement` element into this node.
     *
     * @param element - The polyline element to draw.
     */
    embedPolyline(element: SVGPolylineElement): void;
    /**
     * @override
     */
    render(renderer: Renderer): void;
}

/**
 * Draws SVG &lt;image /&gt; elements.
 *
 * @public
 */
export declare class SVGImageNode extends SVGGraphicsNode {
    /**
     * The canvas used into which the `SVGImageElement` is drawn. This is because WebGL does not support
     * using `SVGImageElement` as an `ImageSource` for textures.
     */
    protected _canvas: HTMLCanvasElement;
    /**
     * The Canvas 2D context for `this._canvas`.
     */
    protected _canvasContext: CanvasRenderingContext2D;
    /**
     * A texture backed by `this._canvas`.
     */
    protected _texture: Texture;
    /**
     * Embeds the given SVG image element into this node.
     *
     * @param element - The SVG image element to embed.
     */
    embedImage(element: SVGImageElement): void;
    /**
     * Initializes {@code this._texture} by allocating it from the atlas. It is expected the texture size requested
     * is less than the atlas's slab dimensions.
     *
     * @param width
     * @param height
     */
    private initTexture;
    /**
     * Draws the image into this node's texture.
     *
     * @param image - The image element holding the image.
     */
    private drawTexture;
}

/**
 * Draws SVG &lt;path /&gt; elements.
 *
 * @public
 */
export declare class SVGPathNode extends SVGGraphicsNode {
    /**
     * Embeds the `SVGPathElement` into this node.
     *
     * @param element - the path to draw
     */
    embedPath(element: SVGPathElement): this;
}

/**
 * {@link SVGScene} can be used to build an interactive viewer for scalable vector graphics images. You must specify the size
 * of the svg viewer.
 *
 * ## SVG Scene Graph
 *
 * SVGScene has an internal, disconnected scene graph that is optimized for lazy updates. It will listen to the following
 * events fired by a node:
 *
 * * `nodetransformdirty`: This will invalidate the transform calculations.
 *
 * @public
 */
export declare class SVGScene extends Container {
    /**
     * The SVG image content being rendered by the scene.
     */
    content: SVGSVGElement;
    /**
     * Display objects that don't render to the screen, but are required to update before the rendering
     * nodes, e.g. mask sprites.
     */
    renderServers: Container;
    /**
     * The scene context
     */
    protected _context: SVGSceneContext;
    /**
     * The width of the rendered scene in local space.
     */
    protected _width: number;
    /**
     * The height of the rendered scene in local space.
     */
    protected _height: number;
    /**
     * Maps content elements to their paint. These paints do not inherit from their parent element. You must
     * compose an {@link InheritedPaintProvider} manually.
     */
    private _elementToPaint;
    /**
     * Maps `SVGMaskElement` elements to their nodes. These are not added to the scene graph directly and are
     * only referenced as a `mask`.
     */
    private _elementToMask;
    private _elementToRenderNode;
    /**
     * Flags whether any transform is dirty in the SVG scene graph.
     */
    protected _transformDirty: boolean;
    sortDirty: boolean;
    /**
     * @param content - The SVG node to render
     * @param context - This can be used to configure the scene
     */
    constructor(content: SVGSVGElement, context?: Partial<SVGSceneContext>);
    initContext(context?: Partial<SVGSceneContext>): void;
    /**
     * Draw the paints required to render the elements in this SVG scene. If not called, gradients
     * and special effects may not render.
     *
     * @param renderer
     */
    drawPaints(renderer: Renderer): void;
    /**
     * Creates a display object that implements the corresponding `embed*` method for the given node.
     *
     * @param element - The element to be embedded in a display object.
     */
    protected createNode(element: SVGElement): Container;
    /**
     * Creates a `Paint` object for the given element. This should only be used when sharing the `Paint`
     * is not desired; otherwise, use {@link SVGScene.queryPaint}.
     *
     * This will return `null` if the passed element is not an instance of `SVGElement`.
     *
     * @alpha
     * @param element
     */
    protected createPaint(element: SVGElement): Paint;
    /**
     * Creates a lazy paint texture for the paint server.
     *
     * @alpha
     * @param paintServer - The paint server to be rendered.
     */
    protected createPaintServer(paintServer: SVGGradientElement): PaintServer;
    /**
     * Creates a lazy luminance mask for the `SVGMaskElement` or its rendering node.
     *
     * @param ref - The `SVGMaskElement` or its rendering node, if already generated.
     */
    protected createMask(ref: SVGMaskElement | Container): MaskServer;
    /**
     * Returns the rendering node for a mask.
     *
     * @alpha
     * @param ref - The mask element whose rendering node is needed.
     */
    protected queryMask(ref: SVGMaskElement): MaskServer;
    /**
     * Returns the cached paint of a content element. The returned paint will not account for any paint
     * attributes inherited from ancestor elements.
     *
     * @alpha
     * @param ref - A reference to the content element.
     */
    protected queryPaint(ref: SVGElement): Paint;
    /**
     * Returns an (uncached) inherited paint of a content element.
     *
     * @alpha
     * @param ref
     */
    protected queryInheritedPaint(ref: SVGElement): Paint;
    /**
     * Parses the internal URL reference into a selector (that can be used to find the
     * referenced element using `this.content.querySelector`).
     *
     * @param url - The reference string, e.g. "url(#id)", "url('#id')", "#id"
     */
    protected parseReference(url: string): string;
    /**
     * Embeds a content `element` into the rendering `node`.
     *
     * This is **not** a stable API.
     *
     * @alpha
     * @param node - The node in this scene that will render the `element`.
     * @param element - The content `element` to be rendered. This must be an element of the SVG document
     *  fragment under `this.content`.
     * @param options - Additional options
     * @param {Paint} [options.basePaint] - The base paint that the element's paint should inherit from
     * @return The base attributes of the element, like paint.
     */
    protected embedIntoNode(node: Container, element: SVGGraphicsElement | SVGMaskElement, options?: {
        basePaint?: Paint;
    }): {
        paint: Paint;
    };
    /**
     * Recursively populates a subscene graph that embeds {@code element}. The root of the subscene is returned.
     *
     * @param element - The SVGElement to be embedded.
     * @param options - Inherited attributes from the element's parent, if any.
     * @return The display object that embeds the element for rendering.
     */
    protected populateSceneRecursive(element: SVGElement, options?: {
        basePaint?: Paint;
    }): Container;
    /**
     * Populates the entire SVG scene. This should only be called once after the {@link SVGScene.content} has been set.
     */
    protected populateScene(): void;
    /**
     * Handles `nodetransformdirty` events fired by nodes. It will set {@link SVGScene._transformDirty} to true.
     *
     * This will also emit `transformdirty`.
     */
    private onNodeTransformDirty;
    /**
     * The width at which the SVG scene is being rendered. By default, this is the viewbox width specified by
     * the root element.
     */
    get width(): number;
    set width(value: number);
    /**
     * The height at which the SVG scene is being rendered. By default, this is the viewbox height specified by
     * the root element.
     */
    get height(): number;
    set height(value: number);
    /**
     * Load the SVG document and create a {@link SVGScene} asynchronously.
     *
     * A cache is used for loaded SVG documents.
     *
     * @param url
     * @param context
     * @returns
     */
    static from(url: string, context?: SVGSceneContext): Promise<SVGScene>;
}

/**
 * Options to manage the SVG scene
 *
 * @public
 */
export declare interface SVGSceneContext {
    /** The texture allocator for loading images. */
    atlas: CanvasTextureAllocator;
    /** Disable loading SVGs referenced from "href", "xlink:href" attributes of &lt;use /&gt; elements. */
    disableHrefSVGLoading: boolean;
    /** @ignore */
    disableRootPopulation: boolean;
}

/**
 * The `SVGTextEngine` interface is used to layout text content authored in SVG files. The @pixi-essentials/svg
 * package provides {@link SVGTextEngineImpl} as a default implementation for users.
 *
 * Text engines are allowed to have async behaviour so that fonts can be loaded before text metrics are measured.
 *
 * It is expected an implementation inherits from {@link PIXI.DisplayObject}.
 *
 * @public
 * @see SVGTextEngineImpl
 */
export declare interface SVGTextEngine {
    /**
     * Clears the text content laid out already. This should reset the state of the engine to before any calls
     * to {@link SVGTextEngine.put} were made.
     */
    clear(): Promise<void>;
    /**
     * Puts the text {@code content} into the local space of the engine at {@code position}. {@code matrix} can
     * be used to transform the glyphs, although it is as optional feature for implementations.
     *
     * @param id - A locally unique ID that can be used to modify the added block of text later.
     * @param position - The position of the text in the engine's local space.
     * @param content - The text to add.
     * @param style - The text styling applied.
     * @param matrix
     */
    put(id: any, position: PointData, content: string, style: Partial<TextStyle>, matrix?: Matrix): Promise<PointData>;
}

/**
 * `SVGTextEngineImpl` is the default implementation for {@link SVGTextEngine}. It is inspired by {@link PIXI.Text} that
 * is provided by @pixi/text. It uses a &lt;canvas /&gt; to draw and cache the text. This may cause blurring issues when
 * the SVG is viewed at highly zoomed-in scales because it is rasterized.
 *
 * @public
 */
export declare class SVGTextEngineImpl extends Sprite implements SVGTextEngine {
    protected canvas: HTMLCanvasElement;
    protected context: CanvasRenderingContext2D;
    protected contentList: Map<any, {
        position: PointData;
        content: string;
        style: Partial<TextStyle>;
        matrix?: Matrix;
    }>;
    protected dirtyId: number;
    protected updateId: number;
    constructor();
    clear(): Promise<void>;
    put(id: any, position: PointData, content: string, style: Partial<TextStyle>, matrix?: Matrix): Promise<PointData>;
    updateText(): void;
    onRender: () => void;
}

/**
 * Draws SVG &lt;text /&gt; elements.
 *
 * @public
 */
export declare class SVGTextNode extends Container {
    /**
     * The SVG text rendering engine to be used by default in `SVGTextNode`. This API is not stable and
     * can change anytime.
     *
     * @alpha
     */
    static defaultEngine: {
        new (): SVGTextEngine & Container;
    };
    /**
     * An instance of a SVG text engine used to layout and render text.
     */
    protected engine: SVGTextEngine & Container;
    /**
     * The current text position, where the next glyph will be placed.
     */
    protected currentTextPosition: PointData;
    constructor();
    /**
     * Embeds a `SVGTextElement` in this node.
     *
     * @param {SVGTextElement} element - The `SVGTextElement` to embed.
     */
    embedText(element: SVGTextElement | SVGTSpanElement, style?: Partial<TextStyle>): Promise<void>;
}

/**
 * Container for rendering SVG &lt;use /&gt; elements.
 *
 * @public
 */
export declare class SVGUseNode extends Container {
    isRefExternal: boolean;
    private _ref;
    /**
     * Embeds the `SVGUseElement` into this node.
     *
     * @param element - The &lt;use /&gt; element to draw.
     */
    embedUse(element: SVGUseElement): void;
    /**
     * The node that renders the element referenced by a &lt;element /&gt; element.
     */
    get ref(): SVGGraphicsNode;
    set ref(value: SVGGraphicsNode);
}

/**
 * The texture allocator dynamically manages space on base-texture slabs. It can be used to generate
 * atlases on demand, which improve batching efficiency.
 *
 * @public
 */
declare abstract class TextureAllocator<S extends TextureSource, T extends Texture = Texture>
    {
    /**
     * The width of texture slabs.
     */
    public readonly slabWidth: number;

    /**
     * The height of texture slabs.
     */
    public readonly slabHeight: number;

    /**
     * The list of base-textures that are used to allocate texture space.
     */
    protected textureSlabs: TextureSlab<S>[];

    /**
     * @param slabWidth - The width of base-texture slabs. This should be at most 2048.
     * @param slabHeight - The height of base-texture slabs. This should be at most 2048.
     */
    constructor(slabWidth = 2048, slabHeight = 2048)
        {
        this.slabWidth = slabWidth;
        this.slabHeight = slabHeight;

        this.textureSlabs = [];
    }

    get maxWidth(): number
        {
        return this.slabWidth - (2 * this.calculatePadding(this.slabWidth, this.slabHeight));
    }

    get maxHeight(): number
        {
        return this.slabHeight - (2 * this.calculatePadding(this.slabWidth, this.slabHeight));
    }

    /**
     * Allocates a texture from this allocator.
     *
     * If its existing slab pool has enough space, the texture is issued from one. Otherwise,
     * a new slab is created and the texture is issued from it. However, if the requested
     * dimensions are larger than slabs themselves, then `null` is always returned.
     *
     * To upload a texture source, you will have to create an atlas-managing {@link TextureSource}
     * yourself on the base-texture. The {@link AtlasAllocator} does this for you, while the
     * {@link CanvasTextureAllocator} can be used to draw on a canvas-based atlas.
     *
     * @param width - The width of the requested texture.
     * @param height - The height of the requested texture.
     * @param padding - The padding requested around the texture, to prevent bleeding.
     * @return The allocated texture, if successful; otherwise, `null`.
     */
    allocate(width: number, height: number, padding = this.calculatePadding(width, height)): T
        {
        // Cannot allocate a texture larger than a texture-slab.
        if (padded(width, padding) > this.slabWidth
        || padded(height, padding) > this.slabHeight)
            {
            return null;
        }

        const slabs = this.textureSlabs;

        // Loop through the slabs and find one with enough space, if any.
        for (let i = 0, j = slabs.length; i < j; i++)
            {
            const slab = slabs[i];
            const texture = this.issueTexture(slab, width, height, padding);

            if (texture)
                {
                return texture;
            }
        }

        // Issue a new slab.
        const slab = this.createSlab();

        // Append this slab to the head of the list.
        this.textureSlabs.unshift(slab);

        // Issue the texture from this blank slab.
        return this.issueTexture(slab, width, height, padding);
    }

    /**
     * Frees the texture and reclaims its space. It is assumed you will not use it again, and have
     * destroyed any resource uploading its data.
     *
     * @param texture
     * @throws When the texture was not located in this allocator.
     */
    free(texture: T): void
        {
        const slab = this.textureSlabs.find((sl) => sl.slab === texture.source);

        if (!slab)
            {
            throw new Error('The texture cannot be freed because '
            + 'its base-texture is not pooled by this allocator. '
            + 'This is either a bug in TextureAllocator or you tried to free a '
            + 'texture that was never allocated by one.');
        }

        const textureEntry = slab.managedTextures.find((entry) => entry.texture === texture);

        if (!textureEntry)
            {
            throw new Error('The texture cannot be freed because it was not found '
            + 'in the managed list of issued textures on its slab. This may be because you '
            + 'duplicated this texture or a bug in TextureAllocator');
        }

        slab.managedArea.free(textureEntry.area);
        slab.managedTextures.splice(slab.managedTextures.indexOf(textureEntry), 1);
    }

    protected calculatePadding(width: number, height: number): number
        {
        const dimen = Math.max(width, height);

        if (dimen < 64)
            {
            return 2;
        }
        else if (dimen < 128)
            {
            return 4;
        }
        else if (dimen < 1024)
            {
            return 8;
        }

        return 16;
    }

    /**
     * Creates a texture slab. Uses {@link this.createSlabSource} to initialize the texture data.
     */
    protected createSlab(): TextureSlab<S>
        {
        return {
            managedArea: new GuilloteneAllocator(this.slabWidth, this.slabHeight),
            managedTextures: [],
            slab: this.createSlabSource(),
        };
    }

    /**
     * Creates a new texture source to initialize a texture slab.
     */
    protected abstract createSlabSource(): S;

    /**
     * Creates a texture on the given base-texture at {@code frame}.
     *
     * @param source - The atlas source that will hold the texture's space.
     * @param frame - The frame in which the texture will be stored.
     */
    protected createTexture(source: S, frame: Rectangle): T
        {
        // Override this method to return correct texture type T.
        return new Texture({ source, frame }) as unknown as T;
    }

    /**
     * Issues a texture from the given texture slab, if possible.
     *
     * @param slab - The texture slab to allocate frame.
     * @param width - The width of the requested texture.
     * @param height - The height of the requested texture.
     * @param padding - Padding required around the texture.
     * @return The issued texture, if successful; otherwise, `null`.
     */
    protected issueTexture(slab: TextureSlab<S>, width: number, height: number, padding = 0): T
        {
        const area = slab.managedArea.allocate(width + (2 * padding), height + (2 * padding));

        if (!area)
            {
            return null;
        }

        tempRect.copyFrom(area);
        tempRect.pad(-padding);

        const issuedTexture = this.createTexture(slab.slab, tempRect.clone());

        slab.managedTextures.push({
            area,
            texture: issuedTexture,
        });

        return issuedTexture;
    }
}

/**
 * An entry of an issued texture from a {@link TextureSlab}.
 *
 * @public
 */
declare type TextureEntry =
    {
    /**
     * The area returned by the area allocator, with the `__mem_area` key.
     */
    area: Rectangle;

    /**
     * The issued texture.
     */
    texture: Texture;
};

/**
 * A texture slab holds a managed base-texture that is used to issue allocated texture space. The
 * texture allocator maintains a pool of these texture slabs.
 *
 * @public
 */
declare type TextureSlab<T extends TextureSource> =
    {
    /**
     * The area allocator that issues texture space.
     */
    managedArea: AreaAllocator<any>;

    /**
     * The list of allocated textures and their area.
     */
    managedTextures: TextureEntry[];

    /**
     * The base-texture that holds all the issued textures.
     */
    slab: T;
};

export { }
