/*!
 * VisualEditor MWDefinedTransclusionContextItem class.
 *
 * @copyright See AUTHORS.txt
 */

/**
 * Context item for a defined MWTransclusion.
 *
 * Templates are defined on-wiki using the message page
 * [[MediaWiki:Visualeditor-template-tools-definition.json]]
 *
 * Example:
 * {
 *   // This key is the static name of the ve.ui.ContextItem
 *   "citationNeeded": [
 *     {
 *       // Normalized title string, or list of strings (for redirects)
 *       "title": [ "Citation needed", "cn" ],
 *       // Extra params. This whole object can be accessed
 *       // via #getMatchedTool
 *       "params": {
 *         "reason": "reason",
 *         "date": "date"
 *       }
 *     },
 *     {
 *       "title": "Cite quote",
 *       "params": {
 *         "date": "date"
 *       }
 *     }
 *   ]
 * }
 *
 * @class
 * @abstract
 * @extends ve.ui.MWTransclusionContextItem
 *
 * @constructor
 * @param {ve.ui.LinearContext} context Context the item is in
 * @param {ve.dm.Model} model Model the item is related to
 * @param {Object} [config]
 */
ve.ui.MWDefinedTransclusionContextItem = function VeUiMWDefinedTransclusionContextItem() {
	// Parent constructor
	ve.ui.MWDefinedTransclusionContextItem.super.apply( this, arguments );

	this.tool = this.constructor.static.getMatchedTool( this.model );
};

/* Inheritance */

OO.inheritClass( ve.ui.MWDefinedTransclusionContextItem, ve.ui.MWTransclusionContextItem );

/* Static Properties */

ve.ui.MWDefinedTransclusionContextItem.static.name = null;

ve.ui.MWDefinedTransclusionContextItem.static.toolDefinitions = ( function () {
	let tools;

	try {
		// Must use mw.message to avoid JSON being parsed as Wikitext
		tools = JSON.parse( mw.message( 'visualeditor-template-tools-definition.json' ).plain() );
	} catch ( e ) {}

	return tools || {};
}() );

/**
 * Only display item for single-template transclusions of these templates.
 *
 * @property {string|string[]|null}
 * @static
 * @inheritable
 */
ve.ui.MWDefinedTransclusionContextItem.static.template = null;

/* Static Methods */

/**
 * @inheritdoc
 */
ve.ui.MWDefinedTransclusionContextItem.static.isCompatibleWith = function ( model ) {
	// Parent method
	return ve.ui.MWDefinedTransclusionContextItem.super.static.isCompatibleWith.apply( this, arguments ) &&
		!!this.getMatchedTool( model );
};

/**
 * Get tool definitions, indexed by normalized title
 *
 * @return {Object} Collection of tool definitions
 */
ve.ui.MWDefinedTransclusionContextItem.static.getToolsByTitle = function () {
	if ( !this.toolsByTitle ) {
		let toolsByTitle;
		this.toolsByTitle = toolsByTitle = {};
		( this.toolDefinitions[ this.name ] || [] ).forEach( ( template ) => {
			const titles = Array.isArray( template.title ) ? template.title : [ template.title ];
			// 'title' can be a single title, or list of titles (including redirects)
			titles.forEach( ( title ) => {
				toolsByTitle[ mw.Title.newFromText( title, mw.config.get( 'wgNamespaceIds' ).template ).getPrefixedText() ] = template;
			} );
		} );
	}
	return this.toolsByTitle;
};

/**
 * Get the tool definition that matches a specific model, if any
 *
 * @param {ve.dm.Model} model
 * @return {Object|null} Tool definition, or null if no match
 */
ve.ui.MWDefinedTransclusionContextItem.static.getMatchedTool = function ( model ) {
	const resource = ve.getProp( model.getAttribute( 'mw' ), 'parts', 0, 'template', 'target', 'href' );
	if ( resource ) {
		const title = mw.Title.newFromText( mw.libs.ve.normalizeParsoidResourceName( resource ) ).getPrefixedText();
		return this.getToolsByTitle()[ title ] || null;
	}
	return null;
};

/**
 * Get a template param using its canonical name
 *
 * @param {string} name Canonical parameter name
 * @return {string|null} Param wikitext, null if not found
 */
ve.ui.MWDefinedTransclusionContextItem.prototype.getCanonicalParam = function ( name ) {
	const params = this.tool.params || {};

	if ( Object.prototype.hasOwnProperty.call( params, name ) ) {
		const aliases = Array.isArray( params[ name ] ) ? params[ name ] : [ params[ name ] ];
		// Find the first non-empty value from the alias list
		for ( let i = 0; i < aliases.length; i++ ) {
			const value = ve.getProp( this.model.getAttribute( 'mw' ), 'parts', 0, 'template', 'params', aliases[ i ], 'wt' );
			if ( value ) {
				return value;
			}
		}
	}
	return null;
};
