/**
 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
import { startCase, cloneDeep } from 'lodash-es';
/**
* Helper function for the downcast converter. Updates attributes on the given view element.
*
* @param writer The view writer.
* @param oldViewAttributes The previous GHS attribute value.
* @param newViewAttributes The current GHS attribute value.
* @param viewElement The view element to update.
*/
export function updateViewAttributes(writer, oldViewAttributes, newViewAttributes, viewElement) {
    if (oldViewAttributes) {
        removeViewAttributes(writer, oldViewAttributes, viewElement);
    }
    if (newViewAttributes) {
        setViewAttributes(writer, newViewAttributes, viewElement);
    }
}
/**
 * Helper function for the downcast converter. Sets attributes on the given view element.
 *
 * @param writer The view writer.
 * @param viewAttributes The GHS attribute value.
 * @param viewElement The view element to update.
 */
export function setViewAttributes(writer, viewAttributes, viewElement) {
    if (viewAttributes.attributes) {
        for (const [key, value] of Object.entries(viewAttributes.attributes)) {
            writer.setAttribute(key, value, viewElement);
        }
    }
    if (viewAttributes.styles) {
        writer.setStyle(viewAttributes.styles, viewElement);
    }
    if (viewAttributes.classes) {
        writer.addClass(viewAttributes.classes, viewElement);
    }
}
/**
 * Helper function for the downcast converter. Removes attributes on the given view element.
 *
 * @param writer The view writer.
 * @param viewAttributes The GHS attribute value.
 * @param viewElement The view element to update.
 */
export function removeViewAttributes(writer, viewAttributes, viewElement) {
    if (viewAttributes.attributes) {
        for (const [key] of Object.entries(viewAttributes.attributes)) {
            writer.removeAttribute(key, viewElement);
        }
    }
    if (viewAttributes.styles) {
        for (const style of Object.keys(viewAttributes.styles)) {
            writer.removeStyle(style, viewElement);
        }
    }
    if (viewAttributes.classes) {
        writer.removeClass(viewAttributes.classes, viewElement);
    }
}
/**
* Merges view element attribute objects.
*/
export function mergeViewElementAttributes(target, source) {
    const result = cloneDeep(target);
    let key = 'attributes';
    for (key in source) {
        // Merge classes.
        if (key == 'classes') {
            result[key] = Array.from(new Set([...(target[key] || []), ...source[key]]));
        }
        // Merge attributes or styles.
        else {
            result[key] = { ...target[key], ...source[key] };
        }
    }
    return result;
}
export function modifyGhsAttribute(writer, item, ghsAttributeName, subject, callback) {
    const oldValue = item.getAttribute(ghsAttributeName);
    const newValue = {};
    for (const kind of ['attributes', 'styles', 'classes']) {
        // Properties other than `subject` should be assigned from `oldValue`.
        if (kind != subject) {
            if (oldValue && oldValue[kind]) {
                newValue[kind] = oldValue[kind];
            }
            continue;
        }
        // `callback` should be applied on property [`subject`].
        if (subject == 'classes') {
            const values = new Set(oldValue && oldValue.classes || []);
            callback(values);
            if (values.size) {
                newValue[kind] = Array.from(values);
            }
            continue;
        }
        const values = new Map(Object.entries(oldValue && oldValue[kind] || {}));
        callback(values);
        if (values.size) {
            newValue[kind] = Object.fromEntries(values);
        }
    }
    if (Object.keys(newValue).length) {
        if (item.is('documentSelection')) {
            writer.setSelectionAttribute(ghsAttributeName, newValue);
        }
        else {
            writer.setAttribute(ghsAttributeName, newValue, item);
        }
    }
    else if (oldValue) {
        if (item.is('documentSelection')) {
            writer.removeSelectionAttribute(ghsAttributeName);
        }
        else {
            writer.removeAttribute(ghsAttributeName, item);
        }
    }
}
/**
 * Transforms passed string to PascalCase format. Examples:
 * * `div` => `Div`
 * * `h1` => `H1`
 * * `table` => `Table`
 */
export function toPascalCase(data) {
    return startCase(data).replace(/ /g, '');
}
/**
 * Returns the attribute name of the model element that holds raw HTML attributes.
 */
export function getHtmlAttributeName(viewElementName) {
    return `html${toPascalCase(viewElementName)}Attributes`;
}
