var template = require("./index")

var whitespaceRegex = /["'\\\n\r\u2028\u2029]/g
var nargs = /\{[0-9a-zA-Z]+\}/g

var replaceTemplate =
"    var args\n" +
"    var result\n" +
"    if (arguments.length === 1 && typeof arguments[0] === \"object\") {\n" +
"        args = arguments[0]\n" +
"    } else {\n" +
"        args = arguments" +
"    }\n\n" +
"    if (!args || !(\"hasOwnProperty\" in args)) {\n" +
"       args = {}\n" +
"    }\n\n" +
"    return {0}"

var literalTemplate = "\"{0}\""
var argTemplate = "(result = args.hasOwnProperty(\"{0}\") ? " +
    "args[\"{0}\"] : null, \n        " +
    "(result === null || result === undefined) ? \"\" : result)"

module.exports = compile

function compile(string, inline) {
    var replacements = string.match(nargs) || []
    var interleave = string.split(nargs)
    var replace = []

    for (var i = 0; i < interleave.length; i++) {
        var current = interleave[i]
        var replacement = replacements[i]
        var escapeLeft = current.charAt(current.length - 1)
        var escapeRight = (interleave[i + 1] || "").charAt(0)

        if (replacement) {
            replacement = replacement.substring(1, replacement.length - 1)
        }

        if (escapeLeft === "{" && escapeRight === "}") {
            replace.push(current + replacement)
        } else {
            replace.push(current)
            if (replacement) {
                replace.push({ name: replacement })
            }
        }
    }

    var prev = [""]

    for (var j = 0; j < replace.length; j++) {
        var curr = replace[j]

        if (String(curr) === curr) {
            var top = prev[prev.length - 1]

            if (String(top) === top) {
                prev[prev.length - 1] = top + curr
            } else {
                prev.push(curr)
            }
        } else {
            prev.push(curr)
        }
    }

    replace = prev

    if (inline) {
        for (var k = 0; k < replace.length; k++) {
            var token = replace[k]

            if (String(token) === token) {
                replace[k] = template(literalTemplate, escape(token))
            } else {
                replace[k] = template(argTemplate, escape(token.name))
            }
        }

        var replaceCode = replace.join(" +\n    ")
        var compiledSource = template(replaceTemplate, replaceCode)
        return new Function(compiledSource)
    }

    return function template() {
        var args

        if (arguments.length === 1 && typeof arguments[0] === "object") {
            args = arguments[0]
        } else {
            args = arguments
        }

        if (!args || !("hasOwnProperty" in args)) {
            args = {}
        }

        var result = []

        for (var i = 0; i < replace.length; i++) {
            if (i % 2 === 0) {
                result.push(replace[i])
            } else {
                var argName = replace[i].name
                var arg = args.hasOwnProperty(argName) ? args[argName] : null
                if (arg !== null || arg !== undefined) {
                    result.push(arg)
                }
            }
        }

        return result.join("")
    }
}

function escape(string) {
    string = '' + string
    return string.replace(whitespaceRegex, escapedWhitespace)
}

function escapedWhitespace(character) {
    // Escape all characters not included in SingleStringCharacters and
    // DoubleStringCharacters on
    // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
    switch (character) {
        case '"':
        case "'":
        case '\\':
            return '\\' + character
        // Four possible LineTerminator characters need to be escaped:
        case '\n':
            return '\\n'
        case '\r':
            return '\\r'
        case '\u2028':
            return '\\u2028'
        case '\u2029':
            return '\\u2029'
    }
}
