export namespace DOMTools {
    export const animate = (e: unknown, animationName: string) => {
        if (e instanceof Event) {
            e = e.target;
        }
        if (e instanceof HTMLElement) {
            e.style.animationName = '';
            e.style.animationName = animationName;
        }
    }

    export const scrollDown = (e: unknown, smooth?: any) => {
        if (e instanceof Event) {
            e = e.target;
        }
        if (e instanceof HTMLElement) {
            e.scrollTo({
                top: e.scrollHeight,
                behavior: smooth !== false ? 'smooth' : 'instant',
            });
        }
    }


    const HIDDEN_TEXTAREA_STYLE = `
min-height:0 !important;
max-height:none !important;
height:0 !important;
visibility:hidden !important;
overflow:hidden !important;
position:absolute !important;
z-index:-1000 !important;
top:0 !important;
right:0 !important
`;

    const SIZING_STYLE = [
        'letter-spacing',
        'line-height',
        'padding-top',
        'padding-bottom',
        'font-family',
        'font-weight',
        'font-size',
        'text-rendering',
        'text-transform',
        'width',
        'text-indent',
        'padding-left',
        'padding-right',
        'border-width',
        'box-sizing'
    ];

    let hiddenTextarea: HTMLTextAreaElement;

    export const calculateNodeHeight = (uiTextNode: HTMLTextAreaElement, minRows = null, maxRows = null) => {
        if (!hiddenTextarea) {
            hiddenTextarea = document.createElement('textarea');
            document.body.appendChild(hiddenTextarea);
        }

        // Copy all CSS properties that have an impact on the height of the content in
        // the textbox
        let {
            paddingSize, borderSize,
            boxSizing, sizingStyle
        } = calculateNodeStyling(uiTextNode);

        // Need to have the overflow attribute to hide the scrollbar otherwise
        // text-lines will not calculated properly as the shadow will technically be
        // narrower for content
        hiddenTextarea.setAttribute('style', sizingStyle + ';' + HIDDEN_TEXTAREA_STYLE);
        hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || 'x';

        let minHeight = -Infinity;
        let maxHeight = Infinity;
        let height = hiddenTextarea.scrollHeight;

        if (boxSizing === 'border-box') {
            // border-box: add border, since height = content + padding + border
            height = height + borderSize;
        } else if (boxSizing === 'content-box') {
            // remove padding, since height = content
            height = height - paddingSize;
        }

        if (minRows !== null || maxRows !== null) {
            // measure height of a textarea with a single row
            hiddenTextarea.value = 'x';
            let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
            if (minRows !== null) {
                minHeight = singleRowHeight * minRows;
                if (boxSizing === 'border-box') {
                    minHeight = minHeight + paddingSize + borderSize;
                }
                height = Math.max(minHeight, height);
            }
            if (maxRows !== null) {
                maxHeight = singleRowHeight * maxRows;
                if (boxSizing === 'border-box') {
                    maxHeight = maxHeight + paddingSize + borderSize;
                }
                height = Math.min(maxHeight, height);
            }
        }
        return { height, minHeight, maxHeight };
    }

    function calculateNodeStyling(node: HTMLElement) {
        let style = window.getComputedStyle(node);

        let boxSizing = (
            style.getPropertyValue('box-sizing') ||
            style.getPropertyValue('-moz-box-sizing') ||
            style.getPropertyValue('-webkit-box-sizing')
        );

        let paddingSize = (
            parseFloat(style.getPropertyValue('padding-bottom')) +
            parseFloat(style.getPropertyValue('padding-top'))
        );

        let borderSize = (
            parseFloat(style.getPropertyValue('border-bottom-width')) +
            parseFloat(style.getPropertyValue('border-top-width'))
        );

        let sizingStyle = SIZING_STYLE
            .map(name => `${name}:${style.getPropertyValue(name)}`)
            .join(';');

        let nodeInfo = {
            sizingStyle,
            paddingSize,
            borderSize,
            boxSizing
        };

        return nodeInfo;
    }
}