// #region Imports
import chroma from 'chroma-js';
import { ControllerBase } from './Abstracts/ControllerBase';
const extractHueAndSaturationRegExp = /^hs[v|l]a?\s?\((\d{1,3}\.?\d*?),?\s?(\d{1,3})/;
/**
 * ColorController - This controller is used to manage color state and transformations.
 *
 * @public
 */
export class ColorController extends ControllerBase {
    // #region Fields
    _applyColorToState;
    _extractColorFromState;
    _maintains;
    _getColorProcesses;
    _saturation;
    _hue;
    _color;
    _previousColor;
    _format;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `ColorController` class.
     *
     * @public
     */
    constructor(host, config) {
        super(host);
        this._applyColorToState = config.applyColorToState.bind(config);
        this._extractColorFromState = config.extractColorFromState.bind(config);
        this._maintains = config.maintains ?? 'hue';
        this._saturation = 0;
        this._hue = 0;
        this._color = chroma.hsv(0, 1, 1);
        this._previousColor = chroma.hsv(0, 1, 1);
        this._format = {
            format: '',
            isString: false
        };
        this._getColorProcesses = {
            rgb: (color, isString) => this.getRgbValue(color, isString),
            prgb: (color, isString) => this.getPrgbValue(color, isString),
            hex8: (color, isString) => this.getHex8Value(color, isString),
            name: (color) => this.getNameValue(color),
            hsl: (color, isString) => this.getHslValue(color, isString),
            hsv: (color, isString) => this.getHsvValue(color, isString),
            hex: (color, isString) => this.getHexValue(color, isString),
            hex3: (color, isString) => this.getHexValue(color, isString),
            hex4: (color, isString) => this.getHexValue(color, isString),
            hex6: (color, isString) => this.getHexValue(color, isString)
        };
    }
    // #endregion
    // #region Properties
    /**
     * Gets the maintains strategy.
     *
     * @public
     */
    get maintains() {
        return this._maintains;
    }
    /**
     * Gets the current hue value.
     *
     * @public
     */
    get hue() {
        return this._hue;
    }
    /**
     * Sets the hue value and updates the host.
     *
     * @public
     */
    set hue(value) {
        const hue = Math.min(360, Math.max(0, value));
        if (hue === this.hue) {
            return;
        }
        const [, s, v] = this._color.hsv();
        const alpha = this._color.alpha();
        this._color = chroma.hsv(hue, s, v).alpha(alpha);
        this._hue = hue;
        this.host.requestUpdate();
    }
    /**
     * Gets the current color value.
     *
     * @public
     */
    get value() {
        return this.color;
    }
    /**
     * Gets the current color in the stored format.
     *
     * @public
     */
    get color() {
        return this._getColorProcesses[this._format.format || 'hex'](this._color, this._format.isString);
    }
    /**
     * Sets the color value and updates the host.
     *
     * @public
     */
    set color(color) {
        if (color === this.color) {
            return;
        }
        if (typeof color === 'string' || typeof color === 'number') {
            this._color = chroma(color);
        }
        else if (typeof color === 'object' && 'hex' in color) {
            this._color = color;
        }
        else if (typeof color === 'object') {
            if ('r' in color && 'g' in color && 'b' in color && 'a' in color) {
                const rgba = color;
                this._color = chroma(rgba.r, rgba.g, rgba.b).alpha(rgba.a);
            }
            else if ('r' in color && 'g' in color && 'b' in color) {
                const rgb = color;
                this._color = chroma(rgb.r, rgb.g, rgb.b);
            }
            else if ('h' in color && 's' in color && 'l' in color && 'a' in color) {
                const hsla = color;
                this._color = chroma.hsl(hsla.h, hsla.s, hsla.l).alpha(hsla.a);
            }
            else if ('h' in color && 's' in color && 'l' in color) {
                const hsl = color;
                this._color = chroma.hsl(hsl.h, hsl.s, hsl.l);
            }
            else if ('h' in color && 's' in color && 'v' in color && 'a' in color) {
                const hsva = color;
                this._color = chroma.hsv(hsva.h, hsva.s, hsva.v).alpha(hsva.a);
            }
            else if ('h' in color && 's' in color && 'v' in color) {
                const hsv = color;
                this._color = chroma.hsv(hsv.h, hsv.s, hsv.v);
            }
        }
        let format = 'hex';
        const isString = typeof color === 'string';
        if (typeof color === 'string') {
            if (color.startsWith('#'))
                format = 'hex';
            else if (color.startsWith('rgb'))
                format = 'rgb';
            else if (color.startsWith('hsl'))
                format = 'hsl';
            else if (color.startsWith('hsv'))
                format = 'hsv';
        }
        else if (typeof color === 'object') {
            if ('h' in color && 's' in color && 'v' in color)
                format = 'hsv';
            else if ('h' in color && 's' in color && 'l' in color)
                format = 'hsl';
            else if ('r' in color && 'g' in color && 'b' in color)
                format = 'rgb';
        }
        this._format = {
            format,
            isString
        };
        this.setColorProcess(this._color, color, format, isString);
        this.host.requestUpdate();
    }
    // #endregion
    // #region Methods
    /**
     * Applies color from state using the configured extraction callback.
     *
     * @public
     */
    applyColorFromState() {
        const colorValue = this._extractColorFromState(this);
        this._color = typeof colorValue === 'string' || typeof colorValue === 'number' ? chroma(colorValue) : colorValue;
    }
    /**
     * Gets the color in a specific format.
     *
     * @param format - The desired color format.
     * @returns The color value in the specified format.
     * @public
     */
    getColor(format) {
        if (format === 'hsl') {
            const [h, s, l] = this._color.hsl();
            return {
                h,
                s,
                l,
                a: this._color.alpha()
            };
        }
        return this._color.hex();
    }
    /**
     * Sets the color directly from a Color object.
     *
     * @param color - The color to set.
     * @public
     */
    setColor(color) {
        this._color = color;
        const isString = false;
        const format = 'hex';
        this.setColorProcess(this._color, color, format, isString);
    }
    /**
     * Gets the color as an HSL string.
     *
     * @returns The HSL string representation.
     * @public
     */
    getHslString() {
        return this._color.css('hsl');
    }
    /**
     * Saves the current color as the previous color.
     *
     * @public
     */
    savePreviousColor() {
        this._previousColor = chroma(this._color.hex());
    }
    /**
     * Restores the previously saved color.
     *
     * @public
     */
    restorePreviousColor() {
        this.setColor(this._previousColor);
    }
    /**
     * Processes color changes based on the maintains strategy.
     *
     * @param currentColor - The current color.
     * @param nextColor - The next color value.
     * @param format - The color format.
     * @param isString - Whether the color is a string.
     * @private
     */
    setColorProcess(currentColor, nextColor, format, isString) {
        if (this._maintains === 'hue') {
            this.setColorMaintainHue(nextColor, format, isString);
        }
        else {
            this.setColorMaintainSaturation(currentColor, nextColor, format, isString);
        }
    }
    /**
     * Sets color while maintaining the hue value.
     *
     * @param nextColor - The next color value.
     * @param format - The color format.
     * @param isString - Whether the color is a string.
     * @private
     */
    setColorMaintainHue(nextColor, format, isString) {
        const [h, s, v] = this._color.hsv();
        let originalHue = undefined;
        if (isString && format.startsWith('hs')) {
            const values = extractHueAndSaturationRegExp.exec(nextColor);
            if (values !== null) {
                const [, hStr] = values;
                originalHue = Number(hStr);
            }
        }
        else if (!isString && format.startsWith('hs')) {
            if (typeof nextColor === 'object' && 'h' in nextColor) {
                originalHue = nextColor.h;
            }
        }
        this.hue = originalHue ?? h;
        this._applyColorToState({
            h,
            s,
            v
        });
    }
    /**
     * Sets color while maintaining the saturation value.
     *
     * @param currentColor - The current color.
     * @param nextColor - The next color value.
     * @param format - The color format.
     * @param isString - Whether the color is a string.
     * @private
     */
    setColorMaintainSaturation(currentColor, nextColor, format, isString) {
        if (isString && format.startsWith('hs')) {
            const values = extractHueAndSaturationRegExp.exec(nextColor);
            if (values !== null) {
                const [, hStr, sStr] = values;
                this.hue = Number(hStr);
                this._saturation = Number(sStr);
            }
        }
        else if (!isString && format.startsWith('hs')) {
            if (typeof nextColor === 'object' && 'h' in nextColor && 's' in nextColor) {
                const colorObj = nextColor;
                this.hue = colorObj.h;
                this._saturation = colorObj.s;
            }
        }
        else {
            const [h] = currentColor.hsv();
            this.hue = h;
        }
        const [h, s, v] = currentColor.hsv();
        this._applyColorToState({
            h,
            s,
            v
        });
    }
    /**
     * Gets the hexadecimal color value.
     *
     * @param color - The color to convert.
     * @param isString - Whether to return as string.
     * @returns The hex color value.
     * @private
     */
    getHexValue(color, isString) {
        if (isString) {
            return color.alpha() < 1 ? color.hex('rgba') : color.hex();
        }
        const hex = color.hex();
        return hex.replace('#', '');
    }
    /**
     * Gets the RGB color value.
     *
     * @param color - The color to convert.
     * @param isString - Whether to return as string.
     * @returns The RGB color value.
     * @private
     */
    getRgbValue(color, isString) {
        if (isString) {
            return color.css();
        }
        const [r, g, b] = color.rgb();
        return {
            r,
            g,
            b,
            a: color.alpha()
        };
    }
    /**
     * Gets the percentage RGB color value.
     *
     * @param color - The color to convert.
     * @param isString - Whether to return as string.
     * @returns The percentage RGB color value.
     * @private
     */
    getPrgbValue(color, isString) {
        if (isString) {
            const [r, g, b] = color.rgb();
            const a = color.alpha();
            return a < 1
                ? `rgba(${(r / 255 * 100).toFixed(1)}%, ${(g / 255 * 100).toFixed(1)}%, ${(b / 255 * 100).toFixed(1)}%, ${a})`
                : `rgb(${(r / 255 * 100).toFixed(1)}%, ${(g / 255 * 100).toFixed(1)}%, ${(b / 255 * 100).toFixed(1)}%)`;
        }
        const [r, g, b] = color.rgb();
        return {
            r: r / 255 * 100,
            g: g / 255 * 100,
            b: b / 255 * 100,
            a: color.alpha()
        };
    }
    /**
     * Gets the 8-digit hexadecimal color value (with alpha).
     *
     * @param color - The color to convert.
     * @param isString - Whether to return as string.
     * @returns The hex8 color value.
     * @private
     */
    getHex8Value(color, isString) {
        const hex = color.hex('rgba');
        return isString ? hex : hex.replace('#', '');
    }
    /**
     * Gets the named color value.
     *
     * @param color - The color to convert.
     * @returns The named color value or CSS string.
     * @private
     */
    getNameValue(color) {
        return color.name() || color.css();
    }
    /**
     * Gets the HSL color value.
     *
     * @param color - The color to convert.
     * @param isString - Whether to return as string.
     * @returns The HSL color value.
     * @private
     */
    getHslValue(color, isString) {
        if (this._maintains === 'hue') {
            if (isString) {
                const [, s, l] = color.hsl();
                const a = color.alpha();
                return a < 1
                    ? `hsla(${this.hue}, ${(s * 100).toFixed(1)}%, ${(l * 100).toFixed(1)}%, ${a})`
                    : `hsl(${this.hue}, ${(s * 100).toFixed(1)}%, ${(l * 100).toFixed(1)}%)`;
            }
            const [, s, l] = color.hsl();
            return {
                h: this.hue,
                s,
                l,
                a: color.alpha()
            };
        }
        if (isString) {
            const [, , l] = color.hsl();
            const a = color.alpha();
            return a < 1
                ? `hsla(${this.hue}, ${this._saturation}%, ${(l * 100).toFixed(1)}%, ${a})`
                : `hsl(${this.hue}, ${this._saturation}%, ${(l * 100).toFixed(1)}%)`;
        }
        const [, , l] = color.hsl();
        return {
            h: this.hue,
            s: this._saturation / 100,
            l,
            a: color.alpha()
        };
    }
    /**
     * Gets the HSV color value.
     *
     * @param color - The color to convert.
     * @param isString - Whether to return as string.
     * @returns The HSV color value.
     * @private
     */
    getHsvValue(color, isString) {
        if (this._maintains === 'hue') {
            if (isString) {
                const [, s, v] = color.hsv();
                const a = color.alpha();
                return a < 1
                    ? `hsva(${this.hue}, ${(s * 100).toFixed(1)}%, ${(v * 100).toFixed(1)}%, ${a})`
                    : `hsv(${this.hue}, ${(s * 100).toFixed(1)}%, ${(v * 100).toFixed(1)}%)`;
            }
            const [, s, v] = color.hsv();
            return {
                h: this.hue,
                s,
                v,
                a: color.alpha()
            };
        }
        if (isString) {
            const [, , v] = color.hsv();
            const a = color.alpha();
            return a < 1
                ? `hsva(${this.hue}, ${this._saturation}%, ${(v * 100).toFixed(1)}%, ${a})`
                : `hsv(${this.hue}, ${this._saturation}%, ${(v * 100).toFixed(1)}%)`;
        }
        const [, , v] = color.hsv();
        return {
            h: this.hue,
            s: this._saturation / 100,
            v,
            a: color.alpha()
        };
    }
}
//# sourceMappingURL=ColorController.js.map