import { createElement, getWrapper } from './dom-utils';
import classNames from 'classnames';
const messageLengthThreshold = 5;
export function renderAnnotations(element, pregradingAnnotations, censoringAnnotations, showTitle = true) {
    const annotations = [...pregradingAnnotations, ...censoringAnnotations];
    const annotationsWithMessages = annotations.filter(a => a.message);
    for (const [i, annotation] of annotations.entries()) {
        const type = pregradingAnnotations.includes(annotation) ? 'pregrading' : 'censoring';
        const index = annotationsWithMessages.indexOf(annotation) + 1 || undefined;
        const listIndex = type === 'pregrading' ? i : i - pregradingAnnotations.length;
        switch (annotation.type) {
            case 'line':
            case 'rect':
                renderImageAnnotation(element, annotation, type, listIndex, index, showTitle);
                break;
            default:
                renderTextAnnotation(element, annotation, type, listIndex, index, showTitle);
        }
    }
}
export function updateImageAnnotationMarkSize(mark, annotation) {
    const style = mark.style;
    const pct = (n) => `${n * 100}%`;
    if (annotation.type === 'rect') {
        style.left = pct(annotation.x);
        style.top = pct(annotation.y);
        style.right = pct(1 - (annotation.x + annotation.width));
        style.bottom = pct(1 - (annotation.y + annotation.height));
    }
    else {
        style.left = pct(annotation.x1);
        style.top = pct(annotation.y1);
        style.right = pct(1 - annotation.x2);
        style.bottom = pct(1 - annotation.y2);
    }
}
export function renderImageAnnotation(answerElement, annotation, type, listIndex, index, showTitle) {
    const { attachmentIndex } = annotation;
    const image = answerElement.querySelectorAll('img')[attachmentIndex];
    const title = showTitle ? annotation.message : '';
    return renderImageAnnotationByImage(image, title, annotation, type, listIndex, index);
}
export function renderImageAnnotationByImage(image, title, annotation, type, listIndex, index) {
    const wrapper = getWrapper(image);
    const shape = mkShape();
    wrapper.appendChild(shape);
    function mkShape() {
        const mark = createElement('mark', {
            ...{
                className: classNames('e-annotation e-annotation--shape', {
                    'e-annotation--pregrading': type === 'pregrading',
                    'e-annotation--censoring': type === 'censoring'
                }),
                title,
                'data-list-index': String(listIndex),
                'data-type': type
            },
            ...(index && { 'data-index': String(index) }),
            ...(annotation.message && { 'data-message': annotation.message })
        });
        updateImageAnnotationMarkSize(mark, annotation);
        if (index) {
            mark.appendChild(createSup(index, 'shape', annotation.message));
        }
        return mark;
    }
    return shape;
}
function renderTextAnnotation(rootElement, annotation, type, listIndex, index, showTitle) {
    const { startIndex, length: annotationLength } = annotation;
    return go(rootElement.childNodes[0], 0, annotationLength);
    function go(
    /* The current node in the answer HTML. */
    node, 
    /** The current index corresponding to the node. */
    currentIndex, 
    /* The remaining number of characters from the annotation to render. */
    remaining) {
        var _a;
        if (!isDiv(node) && !isMark(node) && !isSup(node) && !isBr(node) && currentIndex >= startIndex) {
            // We found the correct spot to start an annotation. So let's create a mark element.
            const mark = mkMark();
            // Insert it before the node.
            (_a = node.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(mark, node);
            // Move the node and its next siblings inside the mark until the mark
            // contains the required amount of content or we run out of sibling nodes.
            // If the node is a Text node and it is too long for the annotation, we
            // split it in two and move the first part inside the mark, leaving the
            // rest in place.
            const stillRemaining = move(mark, node, remaining);
            // If we have run out of sibling nodes but still have characters to mark,
            // create another mark at the next text node if we can find one. This means
            // that marks have been nested.
            const nextNode = nextSibling(mark);
            if (stillRemaining > 0 && nextNode) {
                go(nextNode, currentIndex + annotationLength - stillRemaining, stillRemaining);
            }
            // We know that we're at the last mark of this annotation.
            // Render the superscript index after it, if necessary.
            else if (index) {
                if (stillRemaining > 0) {
                    console.error('Bug: the answer seems to be too short for the current set of annotations.');
                }
                mark.appendChild(createSup(index, 'text', annotation.message));
            }
        }
        else {
            if (node instanceof HTMLDivElement && node.childNodes.length > 0) {
                return go(node.childNodes[0], currentIndex, remaining);
            }
            if (node instanceof Text && currentIndex + length(node) > startIndex) {
                // The annotation should start in the middle of this text node. We need to split it in two.
                node.splitText(startIndex - currentIndex);
            }
            const nextNode = next(node);
            if (nextNode) {
                go(nextNode, currentIndex + length(node), remaining);
            }
        }
    }
    function isDiv(node) {
        return node.nodeName === 'DIV';
    }
    function isBr(node) {
        return node.nodeName === 'BR';
    }
    function isMark(node) {
        return node.nodeName === 'MARK';
    }
    function isSup(node) {
        return node.nodeName === 'SUP';
    }
    function isImage(node) {
        // If we've already added an image annotation to the image, it will be wrapped
        // in a <span> element.
        return node.nodeName === 'IMG' || node.nodeName === 'SPAN';
    }
    function next(node) {
        return isMark(node) ? node.childNodes[0] : nextSibling(node);
    }
    function nextSibling(node) {
        const sibling = node.nextSibling;
        if (sibling) {
            return sibling;
        }
        const parent = node.parentElement;
        // If we encounter the root element, we've run out of text to annotate. This
        // could be a bug in the annotation code or perhaps the annotations are invalid.
        // Return null instead of throwing an exception to stop adding additional marks.
        if (parent === null || parent === rootElement) {
            return null;
        }
        return nextSibling(parent);
    }
    function length(node) {
        return node instanceof Text ? node.length : isImage(node) ? 1 : 0;
    }
    function mkMark() {
        return createElement('mark', {
            ...{
                className: classNames('e-annotation', {
                    'e-annotation--pregrading': type === 'pregrading',
                    'e-annotation--censoring': type === 'censoring'
                }),
                title: showTitle ? annotation.message : '',
                'data-list-index': String(listIndex),
                'data-type': type
            },
            ...(index && { 'data-index': String(index) }),
            ...(annotation.message && { 'data-message': annotation.message })
        });
    }
    function move(mark, node, remaining) {
        if (!node || isMark(node) || remaining === 0) {
            return remaining;
        }
        // Split text nodes if they are too long.
        if (node instanceof Text && length(node) > 0 && length(node) > remaining) {
            node.splitText(remaining);
        }
        const nextSibling = node.nextSibling;
        mark.appendChild(node);
        return move(mark, nextSibling, remaining - length(node));
    }
}
function createSup(index, type, message) {
    return createElement('sup', {
        className: `e-annotation__index e-annotation__index--${type}`,
        'data-content': message.length > messageLengthThreshold ? `${index})` : message,
        'data-message': message
    });
}
//# sourceMappingURL=renderAnnotations.js.map