'use strict';

/*!
 * VisualEditor MediaWiki Cite initialisation code.
 *
 * @copyright 2011-2018 VisualEditor Team's Cite sub-team and others; see AUTHORS.txt
 * @license MIT
 */

( function () {
	const MWCitationContextItem = require( './ve.ui.MWCitationContextItem.js' );
	const MWCitationDialogTool = require( './ve.ui.MWCitationDialogTool.js' );

	const modifiedToolbarGroups = [];

	mw.hook( 've.newTarget' ).add( ( target ) => {
		if ( ![ 'article', 'cx' ].includes( target.constructor.static.name ) ) {
			return;
		}
		const toolbarGroups = target.constructor.static.toolbarGroups;

		if ( modifiedToolbarGroups.includes( toolbarGroups ) ) {
			return;
		}

		if (
			mw.config.get( 'wgCiteVisualEditorOtherGroup' ) ||
			// Mobile doesn't have room for a top-level reference group, so place in the 'insert' menu as well
			( ve.init.mw.MobileArticleTarget && target instanceof ve.init.mw.MobileArticleTarget )
		) {
			// Add to the insert group (demoted)
			const insertGroup = toolbarGroups.find(
				( toolbarGroup ) => toolbarGroup.name === 'insert' ||
				// Name used in CX.
				// TODO: Change this to 'insert'
				toolbarGroup.name === 'extra'
			);
			if ( insertGroup ) {
				insertGroup.demote = [
					...( insertGroup.demote || [] ),
					{ group: 'cite' }, 'reference', 'reference/existing'
				];
			}
		} else {
			// Add after the link group
			const index = toolbarGroups.findIndex( ( toolbarGroup ) => toolbarGroup.name === 'link' );
			if ( index !== -1 ) {
				const group = {
					name: 'cite',
					type: 'list',
					title: OO.ui.deferMsg( 'cite-ve-toolbar-group-label' ),
					label: OO.ui.deferMsg( 'cite-ve-toolbar-group-label' ),
					include: [ { group: 'cite' }, 'reference', 'reference/existing' ],
					demote: [ 'reference', 'reference/existing' ]
				};
				toolbarGroups.splice( index + 1, 0, group );
			} else {
				mw.log.warn( 'No link group find in toolbar to place reference tools next to.' );
			}
		}

		modifiedToolbarGroups.push( toolbarGroups );
	} );

	/**
	 * Add reference insertion tools from on-wiki data.
	 *
	 * By adding a definition in JSON to MediaWiki:Cite-tool-definition.json, the cite menu can
	 * be populated with tools that create references containing specific templates. The
	 * content of the definition should be an array containing a series of
	 * objects, one for each tool. Each object must contain a `name`, `icon` and
	 * `template` property. An optional `title` property can also be used to
	 * define the tool title in plain text. The `name` property is a unique
	 * identifier for the tool, and also provides a fallback title for the tool by
	 * being transformed into a message key. The name is prefixed with
	 * `visualeditor-cite-tool-name-`, and messages can be defined on Wiki. Some
	 * common messages are pre-defined for tool names such as `web`, `book`,
	 * `news` and `journal`.
	 *
	 * Example:
	 * [ { "name": "web", "icon": "browser", "template": "Cite web" }, ... ]
	 */

	/** Maps deprecated icon names to supported OOUI icon names */
	const deprecatedIcons = {
		'ref-cite-book': 'book',
		'ref-cite-journal': 'journal',
		'ref-cite-news': 'newspaper',
		'ref-cite-web': 'browser',
		'reference-existing': 'referenceExisting'
	};
	/** Maps pre-defined citation tool names to OOUI icon names */
	const defaultIcons = {
		book: 'book',
		journal: 'journal',
		news: 'newspaper',
		web: 'browser'
	};

	// TODO: Replace with require( './ve.ui.MWCitationTools.json' );
	ve.ui.mwCitationTools.forEach( ( item ) => {
		const hasOwn = Object.prototype.hasOwnProperty;
		const data = { template: item.template, title: item.title };

		if ( !item.icon && hasOwn.call( defaultIcons, item.name ) ) {
			item.icon = defaultIcons[ item.name ];
		}

		if ( hasOwn.call( deprecatedIcons, item.icon ) ) {
			item.icon = deprecatedIcons[ item.icon ];
		}

		// Generate citation tool
		const name = 'cite-' + item.name;
		if ( !ve.ui.toolFactory.lookup( name ) ) {
			const tool = function GeneratedMWCitationDialogTool() {
				MWCitationDialogTool.apply( this, arguments );
			};
			OO.inheritClass( tool, MWCitationDialogTool );
			tool.static.group = 'cite';
			tool.static.name = name;
			tool.static.icon = item.icon;
			if ( mw.config.get( 'wgCiteVisualEditorOtherGroup' ) ) {
				tool.static.title = mw.msg( 'cite-ve-othergroup-item', item.title );
			} else {
				tool.static.title = item.title;
			}
			tool.static.commandName = name;
			tool.static.template = item.template;
			tool.static.autoAddToCatchall = false;
			tool.static.autoAddToGroup = true;
			tool.static.associatedWindows = [ name ];
			ve.ui.toolFactory.register( tool );
			ve.ui.commandRegistry.register(
				new ve.ui.Command(
					name, 'mwcite', 'open', { args: [ data ], supportedSelections: [ 'linear' ] }
				)
			);
		}

		// Generate citation context item
		if ( !ve.ui.contextItemFactory.lookup( name ) ) {
			const contextItem = function GeneratedMWCitationContextItem() {
				// Parent constructor
				MWCitationContextItem.apply( this, arguments );
			};
			OO.inheritClass( contextItem, MWCitationContextItem );
			contextItem.static.name = name;
			contextItem.static.icon = item.icon;
			contextItem.static.label = item.title;
			contextItem.static.commandName = name;
			contextItem.static.template = item.template;
			// If the grand-parent class (ve.ui.MWReferenceContextItem) is extended
			// and re-registered (e.g. by Citoid), then the inheritance chain is
			// broken, and the generic 'reference' context item would show. Instead
			// manually specify that that context should never show when a more
			// specific context item is shown.
			contextItem.static.suppresses = [ 'reference' ];
			ve.ui.contextItemFactory.register( contextItem );
		}
	} );
}() );
