var Diff = require('text-diff');
var templateUtil = require('../common/template');

const DISPLAY_TAB_SIZE = 2;
const DEFAULT_STRINGIFICATION_OPTIONS = { tab: DISPLAY_TAB_SIZE };

module.exports = {
    uppercase, concat, replace, trim,
    template, stringify, escape_html, preserve_spaces,
    code, diff, line_count
};

function uppercase(x) {
    return String(x).toUpperCase();
}

function concat(...args) {
    if (args[0] instanceof Array) {
        return args[0].filter(Boolean).join('');
    }
    return args.slice(0, -1).filter(Boolean).join('');
}

function replace(originalText, replacedPattern, replacement, options) {
    if (originalText === null || originalText === undefined) {
        return originalText;
    }
    return String(originalText).replace(
        new RegExp(replacedPattern, options.hash.flags),
        replacement
    );
}

function trim(x) {
    return String(x).trim();
}

function stringify(x, options) {
    switch (typeof x) {
        case 'object':
            return JSON.stringify(x, null, (options.hash || options).tab);
        case 'string':
            return templateUtil.build(x, options);
    }
    return String(x);
}

function template(x, options) {
    return templateUtil.build(x, options.hash.data || options.hash);
}

function preserve_spaces(x, options) {
    const tabSize = (options ? (options.hash || options).tab : null) || DISPLAY_TAB_SIZE;

    return x === null || x === undefined ? '' : String(x)
        .replace(/^ /g, '\u00a0')
        .replace(/  /g, '\u00a0\u00a0')
        .replace(/\t/g, (new Array(tabSize + 1)).join('\u00a0'));
}

function escape_html(x) {
    return x === null || x === undefined ? '' : String(x)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;');
}

function code(x) {
    return escape_html(preserve_spaces(stringify(x, DEFAULT_STRINGIFICATION_OPTIONS)));
}

function diff(a, b) {
    var diff = new Diff();
    var textDiff = diff.main(
        stringify(a, DEFAULT_STRINGIFICATION_OPTIONS),
        stringify(b, DEFAULT_STRINGIFICATION_OPTIONS)
    );
    diff.cleanupSemantic(textDiff);
    return diff.prettyHtml(textDiff);
}

function line_count(x) {
    return x ? String(x).split('\n').length : 0;
}
