import '../../../assert/js/assert.js';
import '../../../globals.js';
// Copyright 2013-2024, University of Colorado Boulder
/**
 * Abstract base type for LinearGradient and RadialGradient.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import { isTReadOnlyProperty } from '../../../axon/js/TReadOnlyProperty.js';
import cleanArray from '../../../phet-core/js/cleanArray.js';
import { Color, Paint, scenery } from '../imports.js';
export default class Gradient extends Paint {
    /**
     * TODO: add the ability to specify the color-stops inline. possibly [ [0,color1], [0.5,color2], [1,color3] ] https://github.com/phetsims/scenery/issues/1581
     */
    constructor() {
        super();
        assert && assert(this.constructor.name !== 'Gradient', 'Please create a LinearGradient or RadialGradient. Do not directly use the supertype Gradient.');
        this.stops = [];
        this.lastStopRatio = 0;
        this.canvasGradient = null;
        this.colorStopsDirty = false;
        this.lastColorStopValues = [];
    }
    /**
     * Adds a color stop to the gradient.
     *
     * Color stops should be added in order (monotonically increasing ratio values).
     *
     * NOTE: Color stops should only be added before using the gradient as a fill/stroke. Adding stops afterwards
     *       will result in undefined behavior.
     * TODO: Catch attempts to do the above. https://github.com/phetsims/scenery/issues/1581
     *
     * @param ratio - Monotonically increasing value in the range of 0 to 1
     * @param color
     * @returns - for chaining
     */
    addColorStop(ratio, color) {
        assert && assert(ratio >= 0 && ratio <= 1, 'Ratio needs to be between 0,1 inclusively');
        assert && assert(color === null ||
            typeof color === 'string' ||
            color instanceof Color ||
            (isTReadOnlyProperty(color) && (color.value === null ||
                typeof color.value === 'string' ||
                color.value instanceof Color)), 'Color should match the addColorStop type specification');
        if (this.lastStopRatio > ratio) {
            // fail out, since browser quirks go crazy for this case
            throw new Error('Color stops not specified in the order of increasing ratios');
        }
        else {
            this.lastStopRatio = ratio;
        }
        this.stops.push({
            ratio: ratio,
            color: color
        });
        // Easiest to just push a value here, so that it is always the same length as the stops array.
        this.lastColorStopValues.push('');
        return this;
    }
    /**
     * Returns stops suitable for direct SVG use.
     */
    getSVGStops() {
        return this.stops;
    }
    /**
     * Forces a re-check of whether colors have changed, so that the Canvas gradient can be regenerated if
     * necessary.
     */
    invalidateCanvasGradient() {
        sceneryLog && sceneryLog.Paints && sceneryLog.Paints(`Invalidated Canvas Gradient for #${this.id}`);
        this.colorStopsDirty = true;
    }
    /**
     * Compares the current color values with the last-recorded values for the current Canvas gradient.
     *
     * This is needed since the values of color properties (or the color itself) may change.
     */
    haveCanvasColorStopsChanged() {
        if (this.lastColorStopValues === null) {
            return true;
        }
        for (let i = 0; i < this.stops.length; i++) {
            if (Gradient.colorToString(this.stops[i].color) !== this.lastColorStopValues[i]) {
                return true;
            }
        }
        return false;
    }
    /**
     * Returns an object that can be passed to a Canvas context's fillStyle or strokeStyle.
     */
    getCanvasStyle() {
        // Check if we need to regenerate the Canvas gradient
        if (!this.canvasGradient || (this.colorStopsDirty && this.haveCanvasColorStopsChanged())) {
            sceneryLog && sceneryLog.Paints && sceneryLog.Paints(`Regenerating Canvas Gradient for #${this.id}`);
            sceneryLog && sceneryLog.Paints && sceneryLog.push();
            this.colorStopsDirty = false;
            cleanArray(this.lastColorStopValues);
            this.canvasGradient = this.createCanvasGradient();
            for (let i = 0; i < this.stops.length; i++) {
                const stop = this.stops[i];
                const colorString = Gradient.colorToString(stop.color);
                this.canvasGradient.addColorStop(stop.ratio, colorString);
                // Save it so we can compare next time whether our generated gradient would have changed
                this.lastColorStopValues.push(colorString);
            }
            sceneryLog && sceneryLog.Paints && sceneryLog.pop();
        }
        return this.canvasGradient;
    }
    /**
     * Returns the current value of the generally-allowed color types for Gradient, as a string.
     */
    static colorToString(color) {
        // to {Color|string|null}
        if (isTReadOnlyProperty(color)) {
            color = color.value;
        }
        // to {Color|string}
        if (color === null) {
            color = 'transparent';
        }
        // to {string}
        if (color instanceof Color) {
            color = color.toCSS();
        }
        return color;
    }
}
Gradient.prototype.isGradient = true;
scenery.register('Gradient', Gradient);
//# sourceMappingURL=Gradient.js.map