/*!
 * VisualEditor DataModel ClassAttribute class.
 *
 * @copyright See AUTHORS.txt
 */

/**
 * DataModel class-attribute node.
 *
 * Used for nodes which use classes to store attributes.
 *
 * @class
 * @abstract
 *
 * @constructor
 */
ve.dm.ClassAttributeNode = function VeDmClassAttributeNode() {};

/* Inheritance */

OO.initClass( ve.dm.ClassAttributeNode );

/* Static methods */

/**
 * Mapping from class names to attributes
 *
 * e.g. { alignLeft: { align: 'left' } } sets the align attribute to 'left'
 * if the element has the class 'alignLeft'
 *
 * @type {Object}
 */
ve.dm.ClassAttributeNode.static.classAttributes = {};

ve.dm.ClassAttributeNode.static.preserveHtmlAttributes = function ( attribute ) {
	return attribute !== 'class';
};

/**
 * Set attributes from a class attribute
 *
 * Unrecognized classes are also preserved.
 *
 * @param {Object} attributes Attributes object to modify
 * @param {string|null} classAttr Class attribute from an element
 */
ve.dm.ClassAttributeNode.static.setClassAttributes = function ( attributes, classAttr ) {
	const classNames = classAttr ? classAttr.trim().split( /\s+/ ) : [];

	if ( !classNames.length ) {
		return;
	}

	const unrecognizedClasses = [];
	for ( let i = 0, l = classNames.length; i < l; i++ ) {
		const className = classNames[ i ];
		if ( Object.prototype.hasOwnProperty.call( this.classAttributes, className ) ) {
			attributes = ve.extendObject( attributes, this.classAttributes[ className ] );
		} else {
			unrecognizedClasses.push( className );
		}
	}

	attributes.originalClasses = classAttr;
	attributes.unrecognizedClasses = unrecognizedClasses;
};

/**
 * Get class attribute from element attributes
 *
 * @param {Object|undefined} attributes Element attributes
 * @return {string|null} Class name, or null if no classes to set
 */
ve.dm.ClassAttributeNode.static.getClassAttrFromAttributes = function ( attributes ) {
	attributes = attributes || {};

	let classNames = [];
	for ( const className in this.classAttributes ) {
		const classAttributeSet = this.classAttributes[ className ];
		let hasClass = true;
		for ( const key in classAttributeSet ) {
			if ( attributes[ key ] !== classAttributeSet[ key ] ) {
				hasClass = false;
				break;
			}
		}
		if ( hasClass ) {
			classNames.push( className );
		}
	}

	if ( attributes.unrecognizedClasses ) {
		classNames = OO.simpleArrayUnion( classNames, attributes.unrecognizedClasses );
	}

	// If no meaningful change in classes, preserve order
	if (
		attributes.originalClasses &&
		ve.compareClassLists( attributes.originalClasses, classNames )
	) {
		return attributes.originalClasses;
	} else if ( classNames.length > 0 ) {
		return classNames.join( ' ' );
	}

	return null;
};

// eslint-disable-next-line jsdoc/require-param
/**
 * @see ve.dm.Node
 */
ve.dm.ClassAttributeNode.static.sanitize = function ( dataElement ) {
	if ( dataElement.attributes ) {
		delete dataElement.attributes.unrecognizedClasses;
	}
};