/**
 * Nowiki treats anything inside it as plain text.
 * @module ext/Nowiki
 */

'use strict';

const ParsoidExtApi = module.parent.require('./extapi.js').versionCheck('^0.11.0');

const { Promise, Util, DOMUtils, WTUtils, DOMDataUtils } = ParsoidExtApi;

const toDOM = Promise.method(function(state, txt, extArgs) {
	const doc = state.env.createDocument();
	const span = doc.createElement('span');
	span.setAttribute('typeof', 'mw:Nowiki');

	txt.split(/(&[#0-9a-zA-Z]+;)/).forEach(function(t, i) {
		if (i % 2 === 1) {
			const cc = Util.decodeWtEntities(t);
			if (cc.length < 3) {
				// This should match the output of the "htmlentity" rule
				// in the tokenizer.
				const entity = doc.createElement('span');
				entity.setAttribute('typeof', 'mw:Entity');
				DOMDataUtils.setDataParsoid(entity, {
					src: t,
					srcContent: cc,
				});
				entity.appendChild(doc.createTextNode(cc));
				span.appendChild(entity);
				return;
			}
			// else, fall down there
		}
		span.appendChild(doc.createTextNode(t));
	});

	span.normalize();
	doc.body.appendChild(span);
	return doc;
});

const serialHandler = {
	handle: Promise.method(function(node, state, wrapperUnmodified) {
		if (!node.hasChildNodes()) {
			state.hasSelfClosingNowikis = true;
			return '<nowiki/>';
		}
		let src = '<nowiki>';
		for (var child = node.firstChild; child; child = child.nextSibling) {
			let out = null;
			if (DOMUtils.isElt(child)) {
				if (DOMUtils.isDiffMarker(child)) {
					/* ignore */
				} else if (child.nodeName === 'SPAN' &&
						child.getAttribute('typeof') === 'mw:Entity' &&
						DOMUtils.hasNChildren(child, 1)
				) {
					const dp = DOMDataUtils.getDataParsoid(child);
					if (dp.src !== undefined && dp.srcContent === child.textContent) {
						// Unedited content
						out = dp.src;
					} else {
						// Edited content
						out = Util.entityEncodeAll(child.firstChild.nodeValue);
					}
				} else {
					/* This is a hacky fallback for what is essentially
					 * undefined behavior. No matter what we emit here,
					 * this won't roundtrip html2html. */
					state.env.log('error/html2wt/nowiki', 'Invalid nowiki content');
					out = child.textContent;
				}
			} else if (DOMUtils.isText(child)) {
				out = child.nodeValue;
			} else {
				console.assert(DOMUtils.isComment(child));
				/* Comments can't be embedded in a <nowiki> */
				state.env.log('error/html2wt/nowiki',
					'Discarded invalid embedded comment in a <nowiki>');
				out = '';
			}

			// Always escape any nowikis found in out
			if (out) {
				src += WTUtils.escapeNowikiTags(out);
			}
		}
		return src + '</nowiki>';
	}),
};

module.exports = function() {
	this.config = {
		tags: [
			{
				name: 'nowiki',
				toDOM,
				// FIXME: This'll also be called on type mw:Extension/nowiki
				serialHandler,
			},
		],
	};
};