import type GObject from "gi://GObject?version=2.0"
import Cairo from "gi://cairo?version=1.0"

declare namespace cairo {
    export import Status = Cairo.Status
    export import Content = Cairo.Content
    export import Operator = Cairo.Operator
    export import Antialias = Cairo.Antialias
    export import FillRule = Cairo.FillRule
    export import LineCap = Cairo.LineCap
    export import LineJoin = Cairo.LineJoin
    export import TextClusterFlags = Cairo.TextClusterFlags
    export import FontSlant = Cairo.FontSlant
    export import FontWeight = Cairo.FontWeight
    export import SubpixelOrder = Cairo.SubpixelOrder
    export import HintStyle = Cairo.HintStyle
    export import HintMetrics = Cairo.HintMetrics
    export import FontType = Cairo.FontType
    export import PathDataType = Cairo.PathDataType
    export import DeviceType = Cairo.DeviceType
    export import SurfaceType = Cairo.SurfaceType
    export import Format = Cairo.Format
    export import PatternType = Cairo.PatternType
    export import Extend = Cairo.Extend
    export import Filter = Cairo.Filter
    export import RegionOverlap = Cairo.RegionOverlap

    /**
     * Describes the metrics of a string of text
     */
    export interface TextExtents {
        /** The horizontal distance from the origin to the leftmost part of the text */
        xBearing: number
        /** The vertical distance from the origin to the topmost part of the text */
        yBearing: number
        /** The width of the text */
        width: number
        /** The height of the text */
        height: number
        /** The distance to advance horizontally after drawing the text */
        xAdvance: number
        /** The distance to advance vertically after drawing the text */
        yAdvance: number
    }

    /**
     * The main Cairo drawing context
     *
     * A Cairo context is used to draw to surfaces and perform drawing operations.
     * When you're done with a context, you must call $dispose() to free memory.
     */
    export class Context extends Cairo.Context {
        /**
         * Creates a new Cairo context for drawing to the given surface
         * @param surface The surface to draw on
         */
        constructor(surface: Surface)

        /**
         * Free a Cairo.Context and all associated memory
         *
         * Unlike other objects in GJS, Cairo contexts must be explicitly disposed
         * to avoid memory leaks.
         */
        $dispose(): void

        /**
         * Adds a circular arc of the given radius to the current path
         * @param xc X coordinate of the center of the arc
         * @param yc Y coordinate of the center of the arc
         * @param radius Radius of the arc
         * @param angle1 Starting angle in radians
         * @param angle2 End angle in radians
         */
        arc(
            xc: number,
            yc: number,
            radius: number,
            angle1: number,
            angle2: number,
        ): void

        /**
         * Adds a circular arc of the given radius to the current path, but draws
         * the arc in the opposite direction from arc()
         * @param xc X coordinate of the center of the arc
         * @param yc Y coordinate of the center of the arc
         * @param radius Radius of the arc
         * @param angle1 Starting angle in radians
         * @param angle2 End angle in radians
         */
        arcNegative(
            xc: number,
            yc: number,
            radius: number,
            angle1: number,
            angle2: number,
        ): void

        /**
         * Adds a cubic Bézier spline to the current path
         * @param x1 X coordinate of the first control point
         * @param y1 Y coordinate of the first control point
         * @param x2 X coordinate of the second control point
         * @param y2 Y coordinate of the second control point
         * @param x3 X coordinate of the end point
         * @param y3 Y coordinate of the end point
         */
        curveTo(
            x1: number,
            y1: number,
            x2: number,
            y2: number,
            x3: number,
            y3: number,
        ): void

        /**
         * Establishes a new clip region by intersecting the current clip region
         * with the current path, then clearing the current path
         */
        clip(): void

        /**
         * Like clip() but preserves the current path
         */
        clipPreserve(): void

        /**
         * Returns the current clip extents as [x1, y1, x2, y2]
         * @returns An array with [x1, y1, x2, y2] clip extents
         */
        clipExtents(): [number, number, number, number]

        /**
         * Closes the current path by drawing a line to the beginning of the current subpath
         */
        closePath(): void

        /**
         * Emits the current page, but doesn't clear it
         */
        copyPage(): void

        /**
         * Transforms a coordinate from device space to user space
         * @param x X coordinate
         * @param y Y coordinate
         * @returns An array with [x, y] transformed coordinates
         */
        deviceToUser(x: number, y: number): [number, number]

        /**
         * Transforms a distance vector from device space to user space
         * @param x X component of the distance vector
         * @param y Y component of the distance vector
         * @returns An array with [x, y] transformed distance vector
         */
        deviceToUserDistance(x: number, y: number): [number, number]

        /**
         * Fills the current path using the current fill rule, then clears the path
         */
        fill(): void

        /**
         * Fills the current path using the current fill rule, but doesn't clear the path
         */
        fillPreserve(): void

        /**
         * Returns the current fill extents as [x1, y1, x2, y2]
         * @returns An array with [x1, y1, x2, y2] fill extents
         */
        fillExtents(): [number, number, number, number]

        /**
         * Gets the current antialiasing mode
         * @returns The current antialiasing mode
         */
        getAntialias(): Antialias

        /**
         * Gets the current point of the current path
         * @returns An array with [x, y] coordinates of the current point
         */
        getCurrentPoint(): [number, number]

        /**
         * Gets the current dash count
         * @returns The number of elements in the current dash pattern
         */
        getDashCount(): number

        /**
         * Gets the current fill rule
         * @returns The current fill rule
         */
        getFillRule(): FillRule

        /**
         * Gets the current line cap style
         * @returns The current line cap style
         */
        getLineCap(): LineCap

        /**
         * Gets the current line join style
         * @returns The current line join style
         */
        getLineJoin(): LineJoin

        /**
         * Gets the current line width
         * @returns The current line width
         */
        getLineWidth(): number

        /**
         * Gets the current miter limit
         * @returns The current miter limit
         */
        getMiterLimit(): number

        /**
         * Gets the current compositing operator
         * @returns The current compositing operator
         */
        getOperator(): Operator

        /**
         * Gets the current source pattern
         * @returns The current source pattern
         */
        getSource(): Pattern

        /**
         * Gets the surface the Cairo context is drawing on
         * @returns The target surface
         */
        getTarget(): Surface

        /**
         * Gets the current tolerance value
         * @returns The current tolerance value
         */
        getTolerance(): number

        /**
         * Checks if there is a current point defined
         * @returns True if there is a current point
         */
        hasCurrentPoint(): boolean

        /**
         * Resets the current transformation matrix to the identity matrix
         */
        identityMatrix(): void

        /**
         * Tests whether the given point is inside the area filled by the current path
         * @param x X coordinate of the point to test
         * @param y Y coordinate of the point to test
         * @returns True if the point is inside the path
         */
        inFill(x: number, y: number): boolean

        /**
         * Tests whether the given point is inside the area that would be inked
         * by the current path with the current line width and stroke parameters
         * @param x X coordinate of the point to test
         * @param y Y coordinate of the point to test
         * @returns True if the point would be inked
         */
        inStroke(x: number, y: number): boolean

        /**
         * Adds a line to the current path from the current point to the given point
         * @param x X coordinate of the end point
         * @param y Y coordinate of the end point
         */
        lineTo(x: number, y: number): void

        /**
         * Sets the current mask pattern used for painting operations
         * @param pattern A pattern to use as mask
         */
        mask(pattern: Pattern): void

        /**
         * Sets the current mask to a surface
         * @param surface A surface to use as mask
         * @param x X coordinate at which to place the origin of the surface
         * @param y Y coordinate at which to place the origin of the surface
         */
        maskSurface(surface: Surface, x: number, y: number): void

        /**
         * Begins a new subpath at the given point
         * @param x X coordinate of the new position
         * @param y Y coordinate of the new position
         */
        moveTo(x: number, y: number): void

        /**
         * Clears the current path and begins a new path
         */
        newPath(): void

        /**
         * Begins a new subpath without changing the current point
         */
        newSubPath(): void

        /**
         * Paints the current source everywhere within the current clip region
         */
        paint(): void

        /**
         * Paints the current source everywhere within the current clip region
         * using the given alpha value
         * @param alpha Alpha value to use, between 0 and 1
         */
        paintWithAlpha(alpha: number): void

        /**
         * Returns the current path extents as [x1, y1, x2, y2]
         * @returns An array with [x1, y1, x2, y2] path extents
         */
        pathExtents(): [number, number, number, number]

        /**
         * Terminates the current pattern group and returns a new pattern
         * representing everything drawn to the group
         * @returns A new pattern representing the group
         */
        popGroup(): Pattern

        /**
         * Terminates the current pattern group and makes it the current source pattern
         */
        popGroupToSource(): void

        /**
         * Temporarily redirects drawing to an intermediate surface
         */
        pushGroup(): void

        /**
         * Temporarily redirects drawing to an intermediate surface with the given content
         * @param content The content type for the group
         */
        pushGroupWithContent(content: Content): void

        /**
         * Adds a rectangle to the current path
         * @param x X coordinate of the top-left corner
         * @param y Y coordinate of the top-left corner
         * @param width Width of the rectangle
         * @param height Height of the rectangle
         */
        rectangle(x: number, y: number, width: number, height: number): void

        /**
         * Adds a cubic Bézier spline to the current path using relative coordinates
         * @param dx1 X offset to the first control point from current point
         * @param dy1 Y offset to the first control point from current point
         * @param dx2 X offset to the second control point from current point
         * @param dy2 Y offset to the second control point from current point
         * @param dx3 X offset to the end point from current point
         * @param dy3 Y offset to the end point from current point
         */
        relCurveTo(
            dx1: number,
            dy1: number,
            dx2: number,
            dy2: number,
            dx3: number,
            dy3: number,
        ): void

        /**
         * Adds a line to the current path relative to the current point
         * @param dx X offset from the current point
         * @param dy Y offset from the current point
         */
        relLineTo(dx: number, dy: number): void

        /**
         * Begins a new subpath relative to the current point
         * @param dx X offset from the current point
         * @param dy Y offset from the current point
         */
        relMoveTo(dx: number, dy: number): void

        /**
         * Resets the current clip region to its original, unrestricted state
         */
        resetClip(): void

        /**
         * Restores the context state from the stack
         */
        restore(): void

        /**
         * Rotates the current transformation matrix
         * @param angle Angle of rotation in radians
         */
        rotate(angle: number): void

        /**
         * Saves the current context state to the stack
         */
        save(): void

        /**
         * Scales the current transformation matrix
         * @param sx Scale factor for the X dimension
         * @param sy Scale factor for the Y dimension
         */
        scale(sx: number, sy: number): void

        /**
         * Selects a font face
         * @param family A font family name
         * @param slant A font slant
         * @param weight A font weight
         */
        selectFontFace(family: string, slant: number, weight: number): void

        /**
         * Sets the antialiasing mode
         * @param antialias The new antialiasing mode
         */
        setAntialias(antialias: Antialias): void

        /**
         * Sets the dash pattern to be used by stroke()
         * @param dashes Array of dash lengths
         * @param offset Offset into the dash pattern
         */
        setDash(dashes: number[], offset: number): void

        /**
         * Sets the current font size in user space units
         * @param size Font size in user space units
         */
        setFontSize(size: number): void

        /**
         * Sets the current fill rule
         * @param fillRule The new fill rule
         */
        setFillRule(fillRule: FillRule): void

        /**
         * Sets the current line cap style
         * @param lineCap The new line cap style
         */
        setLineCap(lineCap: LineCap): void

        /**
         * Sets the current line join style
         * @param lineJoin The new line join style
         */
        setLineJoin(lineJoin: LineJoin): void

        /**
         * Sets the current line width
         * @param width The new line width
         */
        setLineWidth(width: number): void

        /**
         * Sets the current miter limit
         * @param limit The new miter limit
         */
        setMiterLimit(limit: number): void

        /**
         * Sets the current compositing operator
         * @param op The new compositing operator
         */
        setOperator(op: Operator): void

        /**
         * Sets the current source pattern
         * @param pattern The new source pattern
         */
        setSource(pattern: Pattern): void

        /**
         * Sets the source pattern to an RGB opaque color
         * @param red Red component, between 0 and 1
         * @param green Green component, between 0 and 1
         * @param blue Blue component, between 0 and 1
         */
        setSourceRGB(red: number, green: number, blue: number): void

        /**
         * Sets the source pattern to an RGBA color
         * @param red Red component, between 0 and 1
         * @param green Green component, between 0 and 1
         * @param blue Blue component, between 0 and 1
         * @param alpha Alpha component, between 0 and 1
         */
        setSourceRGBA(
            red: number,
            green: number,
            blue: number,
            alpha: number,
        ): void

        /**
         * Sets the source pattern to the given surface
         * @param surface The new source surface
         * @param x X coordinate where to place the surface origin
         * @param y Y coordinate where to place the surface origin
         */
        setSourceSurface(surface: Surface, x: number, y: number): void

        /**
         * Sets the tolerance used when converting paths to trapezoids
         * @param tolerance The new tolerance value
         */
        setTolerance(tolerance: number): void

        /**
         * Emits the current page and clears it
         */
        showPage(): void

        /**
         * Draws text at the current position
         * @param utf8 A string of text encoded in UTF-8
         */
        showText(utf8: string): void

        /**
         * Strokes the current path using the current line width, line join,
         * line cap, and dash settings, then clears the path
         */
        stroke(): void

        /**
         * Like stroke() but preserves the current path
         */
        strokePreserve(): void

        /**
         * Returns the current stroke extents as [x1, y1, x2, y2]
         * @returns An array with [x1, y1, x2, y2] stroke extents
         */
        strokeExtents(): [number, number, number, number]

        /**
         * Gets the extents of the given text if it were drawn at the current point
         * @param utf8 A string of text encoded in UTF-8
         * @returns Text extents information
         */
        textExtents(utf8: string): TextExtents

        /**
         * Translates the current transformation matrix
         * @param tx Translation in the X direction
         * @param ty Translation in the Y direction
         */
        translate(tx: number, ty: number): void

        /**
         * Transforms a coordinate from user space to device space
         * @param x X coordinate
         * @param y Y coordinate
         * @returns An array with [x, y] transformed coordinates
         */
        userToDevice(x: number, y: number): [number, number]

        /**
         * Transforms a distance vector from user space to device space
         * @param x X component of the distance vector
         * @param y Y component of the distance vector
         * @returns An array with [x, y] transformed distance vector
         */
        userToDeviceDistance(x: number, y: number): [number, number]

        /**
         * Creates a copy of the current path and returns it
         * @returns A copy of the current path
         */
        copyPath(): Path

        /**
         * Appends a path to the current path
         * @param path A path to append
         */
        appendPath(path: Path): void
    }

    /**
     * Base class for all Cairo surfaces
     */
    export abstract class Surface extends Cairo.Surface {
        /**
         * Gets the device scale of the surface
         * @returns An array with [x, y] device scale
         */
        getDeviceScale(): [number, number]

        /**
         * Sets the device scale of the surface
         * @param x X scale factor
         * @param y Y scale factor
         */
        setDeviceScale(x: number, y: number): void

        /**
         * Gets the device offset of the surface
         * @returns An array with [x, y] device offset
         */
        getDeviceOffset(): [number, number]

        /**
         * Sets the device offset of the surface
         * @param x X offset
         * @param y Y offset
         */
        setDeviceOffset(x: number, y: number): void

        /**
         * Performs all pending drawing operations
         */
        flush(): void

        /**
         * Finishes the surface and drops all references to external resources
         */
        finish(): void
    }

    /**
     * A surface that uses in-memory image data buffers
     */
    export class ImageSurface extends Surface {
        /**
         * Creates a new image surface
         * @param format The format of pixels in the surface
         * @param width Width of the surface in pixels
         * @param height Height of the surface in pixels
         */
        constructor(format: Format, width: number, height: number)

        /**
         * Creates a new image surface from a PNG file
         * @param filename Path to a PNG file
         * @returns A new image surface
         */
        static createFromPNG(filename: string): ImageSurface

        /**
         * Gets the format of the surface
         * @returns The format of the surface
         */
        getFormat(): Format

        /**
         * Gets the width of the surface in pixels
         * @returns The width of the surface
         */
        getWidth(): number

        /**
         * Gets the height of the surface in pixels
         * @returns The height of the surface
         */
        getHeight(): number

        /**
         * Writes the contents of the surface to a PNG file
         * @param filename Path to the PNG file to write
         */
        writeToPNG(filename: string): void
    }

    /**
     * A surface that produces output in the PDF format
     */
    export class PDFSurface extends Surface {
        /**
         * Creates a new PDF surface
         * @param filename Path to the PDF file to write to
         * @param width Width of the surface in points (1 point = 1/72 inch)
         * @param height Height of the surface in points (1 point = 1/72 inch)
         */
        constructor(filename: string, width: number, height: number)
    }

    /**
     * A surface that produces output in the PostScript format
     */
    export class PSSurface extends Surface {
        /**
         * Creates a new PostScript surface
         * @param filename Path to the PostScript file to write to
         * @param width Width of the surface in points (1 point = 1/72 inch)
         * @param height Height of the surface in points (1 point = 1/72 inch)
         */
        constructor(filename: string, width: number, height: number)
    }

    /**
     * A surface that produces output in the SVG format
     */
    export class SVGSurface extends Surface {
        /**
         * Creates a new SVG surface
         * @param filename Path to the SVG file to write to
         * @param width Width of the surface in points (1 point = 1/72 inch)
         * @param height Height of the surface in points (1 point = 1/72 inch)
         */
        constructor(filename: string, width: number, height: number)
    }

    /**
     * Base class for all Cairo patterns
     */
    export class Pattern extends Cairo.Pattern {}

    /**
     * Base class for all gradient patterns
     */
    export class Gradient extends Pattern {
        /**
         * Adds a color stop to the gradient at the given offset
         * @param offset Offset position of the stop, between 0 and 1
         * @param red Red component, between 0 and 1
         * @param green Green component, between 0 and 1
         * @param blue Blue component, between 0 and 1
         * @param alpha Alpha component, between 0 and 1
         */
        addColorStopRGBA(
            offset: number,
            red: number,
            green: number,
            blue: number,
            alpha: number,
        ): void

        /**
         * Adds an opaque color stop to the gradient at the given offset
         * @param offset Offset position of the stop, between 0 and 1
         * @param red Red component, between 0 and 1
         * @param green Green component, between 0 and 1
         * @param blue Blue component, between 0 and 1
         */
        addColorStopRGB(
            offset: number,
            red: number,
            green: number,
            blue: number,
        ): void
    }

    /**
     * A pattern for linear gradients
     */
    export class LinearGradient extends Gradient {
        /**
         * Creates a new linear gradient pattern
         * @param x0 X coordinate of the start point
         * @param y0 Y coordinate of the start point
         * @param x1 X coordinate of the end point
         * @param y1 Y coordinate of the end point
         */
        constructor(x0: number, y0: number, x1: number, y1: number)
    }

    /**
     * A pattern for radial gradients
     */
    export class RadialGradient extends Gradient {
        /**
         * Creates a new radial gradient pattern
         * @param cx0 X coordinate of the start circle
         * @param cy0 Y coordinate of the start circle
         * @param radius0 Radius of the start circle
         * @param cx1 X coordinate of the end circle
         * @param cy1 Y coordinate of the end circle
         * @param radius1 Radius of the end circle
         */
        constructor(
            cx0: number,
            cy0: number,
            radius0: number,
            cx1: number,
            cy1: number,
            radius1: number,
        )
    }

    /**
     * A pattern that uses a surface as its source
     */
    export class SurfacePattern extends Pattern {
        /**
         * Creates a new pattern for the given surface
         * @param surface The surface to use
         */
        constructor(surface: Surface)
    }

    /**
     * A pattern for solid colors
     */
    export class SolidPattern extends Pattern {
        /**
         * Creates a new solid pattern with an opaque color
         * @param red Red component, between 0 and 1
         * @param green Green component, between 0 and 1
         * @param blue Blue component, between 0 and 1
         * @returns A new solid pattern
         */
        static createRGB(red: number, green: number, blue: number): SolidPattern

        /**
         * Creates a new solid pattern with a transparent color
         * @param red Red component, between 0 and 1
         * @param green Green component, between 0 and 1
         * @param blue Blue component, between 0 and 1
         * @param alpha Alpha component, between 0 and 1
         * @returns A new solid pattern
         */
        static createRGBA(
            red: number,
            green: number,
            blue: number,
            alpha: number,
        ): SolidPattern
    }

    export class Path extends Cairo.Path {}

    /**
     * A rectangle
     */
    export class Rectangle extends Cairo.Rectangle {}

    /**
     * A rectangle integer
     */
    export class RectangleInt extends Cairo.RectangleInt {}

    /**
     * A region object used for representing a set of pixels
     */
    export class Region extends Cairo.Region {}

    /**
     * A matrix object used for transforming coordinates
     */
    export class Matrix extends Cairo.Matrix {}

    /**
     * A font face object used for storing and manipulating font faces
     */
    export class FontFace extends Cairo.FontFace {}

    /**
     * A scaled font object used for storing and manipulating scaled fonts
     */
    export class ScaledFont extends Cairo.ScaledFont {}

    /**
     * A glyph object used for storing and manipulating glyphs
     */
    export class Glyph extends Cairo.Glyph {}

    /**
     * A text cluster object used for storing and manipulating text clusters
     */
    export class TextCluster extends Cairo.TextCluster {}

    /**
     * A font options object used for storing and manipulating font options
     */
    export class FontOptions extends Cairo.FontOptions {}

    /**
     * A device object used for storing and manipulating devices
     */
    export class Device extends Cairo.Device {}
}

export default cairo
