import * as i0 from '@angular/core';
import { InjectionToken, inject, DestroyRef, isDevMode, DOCUMENT, Inject, Optional, Injectable, provideAppInitializer, NgModule } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i1 from '@angular/router';
import { merge, THEME_SWITCHER_ROUTER_MISSING_ERROR } from '@fundamental-ngx/cdk/utils';
import { BehaviorSubject } from 'rxjs';
import { filter } from 'rxjs/operators';

/**
 * Theming Configuration.
 */
class BaseThemingConfig {
    constructor() {
        /**
         * Array of user-defined themes.
         */
        this.customThemes = [];
        /**
         * Default theme.
         */
        this.defaultTheme = 'sap_horizon';
        /**
         * Whether to exclude default themes from the list of available options. By default, false.
         */
        this.excludeDefaultThemes = false;
        /**
         * URL Query parameter flag to change theme based on the provided parameter.
         */
        this.themeQueryParam = 'theme';
        /**
         * Whether to change theme if URL Query parameter flag has been found or changed. Default is true.
         */
        this.changeThemeOnQueryParamChange = true;
        /**
         * Identifiers of theme style links.
         */
        this.themeStyleLinkIdentifiers = {
            'base-theme': 'base-theme-styles',
            'custom-theme': 'custom-theme-styles'
        };
    }
}

const STANDARD_THEMES = [
    {
        id: 'sap_horizon',
        name: 'Morning Horizon (Light)'
    },
    {
        id: 'sap_horizon_dark',
        name: 'Evening Horizon (Dark)'
    },
    {
        id: 'sap_horizon_hcb',
        name: 'Horizon High Contrast Black',
        description: 'Optimized contrast and accessibility for extremely bright environments'
    },
    {
        id: 'sap_horizon_hcw',
        name: 'Horizon High Contrast White',
        description: 'Optimized contrast and accessibility for extremely dark environments'
    },
    {
        id: 'sap_horizon_set',
        name: 'Horizon Set'
    },
    {
        id: 'sap_fiori_3',
        name: 'Quartz Light',
        description: 'Use in regular office environment'
    },
    {
        id: 'sap_fiori_3_dark',
        name: 'Quartz Dark',
        description: 'Use in dimmed environments'
    },
    {
        id: 'sap_fiori_3_hcb',
        name: 'Quartz High Contrast Black',
        description: 'Optimized contrast and accessibility for extremely bright environments'
    },
    {
        id: 'sap_fiori_3_hcw',
        name: 'Quartz High Contrast White',
        description: 'Optimized contrast and accessibility for extremely dark environments'
    },
    {
        id: 'sap_fiori_3_light_dark',
        name: 'Quartz Auto (Depending on the OS Settings)'
    },
    {
        id: 'sap_fiori_3_set',
        name: 'Quartz Set',
        description: 'Quartz Auto (Depending on the OS Settings)'
    }
];

const THEMING_CONFIG_TOKEN = new InjectionToken('FdThemingConfig', {
    factory: () => new BaseThemingConfig()
});

class ThemingService {
    /**
     * Observable of the current theme, applied to the application.
     */
    get currentTheme() {
        return this._currentThemeSubject.asObservable();
    }
    /**
     * @hidden
     */
    constructor(_rendererFactory, _document, _activatedRoute, _providedConfig) {
        this._rendererFactory = _rendererFactory;
        this._document = _document;
        this._activatedRoute = _activatedRoute;
        this._providedConfig = _providedConfig;
        /**
         * Theming configuration.
         */
        this.config = new BaseThemingConfig();
        /** @hidden */
        this._standardThemes = [];
        /** @hidden **/
        this._destroyRef = inject(DestroyRef);
        /** @hidden */
        this._currentThemeSubject = new BehaviorSubject(null);
        this.config = merge(structuredClone(this.config), structuredClone(this._providedConfig));
        this._renderer = this._rendererFactory.createRenderer(this._document, null);
        if (!this.config.excludeDefaultThemes) {
            this._standardThemes = STANDARD_THEMES;
        }
        const mergedThemes = this._formatProvidedThemes(this.config.customThemes);
        this._availableThemes = new Map(mergedThemes.map((theme) => [theme.id, theme]));
        const defaultTheme = this._availableThemes.get(this.config.defaultTheme);
        if (!defaultTheme) {
            return;
        }
        this._currentThemeSubject.next(defaultTheme);
    }
    /**
     * Initiates theme change based on query parameter or provided default theme in config.
     */
    init() {
        if (this.config.changeThemeOnQueryParamChange) {
            this._setThemeByRoute();
        }
        else if (this._currentThemeSubject.value?.id) {
            this.setTheme(this._currentThemeSubject.value.id);
        }
    }
    /**
     * Sets defined theme.
     * @param themeId ID of theme that needs to be used by the application.
     */
    setTheme(themeId) {
        const theme = this._availableThemes.get(themeId);
        if (!theme) {
            console.warn(`Theme with ID '${themeId}' is not found. Please check if ID is correct or provide custom theme for ThemingModule.`);
            return false;
        }
        this._setThemeResource('base-theme', theme.theming.themingBasePath);
        this._setThemeResource('custom-theme', theme.theming.themePath);
        this._currentThemeSubject.next(theme);
        return true;
    }
    /**
     * Returns array of available themes.
     */
    getThemes() {
        return [...this._availableThemes].map((entry) => entry[1]);
    }
    /**
     * Returns current theme definition.
     */
    getCurrentTheme() {
        return this._currentThemeSubject.value;
    }
    /**
     * @hidden
     * @param type
     * @param resourceUrl
     * @private
     */
    _setThemeResource(type, resourceUrl) {
        const linkElm = this._getStyleLinkElement(type);
        if (linkElm.getAttribute('href') === resourceUrl) {
            return;
        }
        this._renderer.setAttribute(linkElm, 'href', resourceUrl);
    }
    /**
     * @hidden
     * @param type
     * @private
     */
    _getStyleLinkElement(type) {
        const existingLinkElement = this._document.getElementById(this.config.themeStyleLinkIdentifiers[type]);
        if (existingLinkElement) {
            return existingLinkElement;
        }
        const newLinkElement = this._renderer.createElement('link');
        this._renderer.setAttribute(newLinkElement, 'rel', 'stylesheet');
        this._renderer.setAttribute(newLinkElement, 'id', this.config.themeStyleLinkIdentifiers[type]);
        this._renderer.appendChild(this._document.head, newLinkElement);
        return newLinkElement;
    }
    /**
     * @hidden
     * @param themes
     * @private
     */
    _formatProvidedThemes(themes) {
        themes = themes || [];
        const mergedThemes = [...this._standardThemes, ...themes];
        if (mergedThemes.length === 0 && isDevMode()) {
            console.error('Error. No themes were provided. Please check your configuration.');
        }
        return mergedThemes.map((theme) => {
            theme.theming = this._getThemingResourceConfig(theme);
            return theme;
        });
    }
    /** @hidden */
    _getThemingResourceConfig(theme) {
        const existingConfig = theme.theming || {};
        const defaultConfig = {
            themingBasePath: `assets/theming-base/${theme.id}/css_variables.css`,
            themePath: `assets/fundamental-styles-theming/${theme.id}.css`
        };
        return Object.assign(defaultConfig, existingConfig);
    }
    /** @hidden */
    _getNativeParameterByName(paramName) {
        paramName = paramName.replace(/[[\\\]]/g, '\\$&');
        const regex = new RegExp('[?&]' + paramName + '(=([^&#]*)|&|#|$)'), results = regex.exec(window.location.href);
        if (!results || !results[2]) {
            return '';
        }
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }
    /**
     * @hidden
     * Set theme according to additional URL parameter.
     * This parameter can be changed in function argument.
     **/
    _setThemeByRoute() {
        const paramName = this.config.themeQueryParam;
        if (!this._activatedRoute) {
            throw new Error(THEME_SWITCHER_ROUTER_MISSING_ERROR);
        }
        this._activatedRoute.queryParams
            .pipe(takeUntilDestroyed(this._destroyRef), filter((param) => param && param[paramName]))
            .subscribe((param) => this.setTheme(param[paramName]));
        const nativeTheme = this._getNativeParameterByName(paramName) || this.config.defaultTheme;
        this.setTheme(nativeTheme);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemingService, deps: [{ token: i0.RendererFactory2 }, { token: DOCUMENT }, { token: i1.ActivatedRoute, optional: true }, { token: THEMING_CONFIG_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemingService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemingService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: Document, decorators: [{
                    type: Inject,
                    args: [DOCUMENT]
                }] }, { type: i1.ActivatedRoute, decorators: [{
                    type: Optional
                }] }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [THEMING_CONFIG_TOKEN]
                }] }] });

/**
 * Provides theming configuration
 * @param config
 */
function provideTheming(config = {}) {
    return [
        ThemingService,
        {
            provide: THEMING_CONFIG_TOKEN,
            useValue: config
        }
    ];
}

/**
 * Initializes theming service
 */
function themingInitializer() {
    return provideAppInitializer(() => {
        const initializerFn = (() => {
            const themingService = inject(ThemingService);
            return () => themingService.init();
        })();
        return initializerFn();
    });
}

/**
 * @deprecated
 * Use direct imports of components and directives.
 */
class ThemingModule {
    /** Module with providers */
    static withConfig(config) {
        return {
            ngModule: ThemingModule,
            providers: [provideTheming(config)]
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: ThemingModule }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemingModule, providers: [ThemingService] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemingModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [],
                    providers: [ThemingService]
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { BaseThemingConfig, STANDARD_THEMES, THEMING_CONFIG_TOKEN, ThemingModule, ThemingService, provideTheming, themingInitializer };
//# sourceMappingURL=fundamental-ngx-core-theming.mjs.map
