/*
 * pekka.js V0.4.0
 * (c) 2017 linzb93
 * Released under the MIT License.
 */
 (function () {
'use strict';

function assert(condition, msg) {
    if (!condition) {
        throw new Error(msg);
    }
}

function map(arr, cb) {
    {
        return $.map(arr, cb);
    }
}

function each(arr, cb) {
    {
        return $.each(arr, cb);
    }
}

// e.g: changeVarInJS('if $id < 3') = 'if this._data.id < 3'
function changeVarInJS(str) {
    return str.replace(/\$/g, 'this._data.');
}

// e.g: unwrap('{{title}}') = 'title'
function unwrap(str) {
    return str.slice(2, -2);
}

function clearBlank(str) {
    return str.replace(/ {2}/g, '');
}

// 判断是否是三目运算
function isTernary(match) {
    return match.indexOf('?') < match.indexOf(':') && match.indexOf('?') > 0;
}

var conditionalParser = function (match, data) {
    if (match.indexOf('if ') === 0) {
        /* 
        * slice(3)的含义
        * 'if a < 3'.slice(3) = 'a < 3'
        * 下面 else if 语法的同理
        */
        return 'if (' + changeVarInJS(match.slice(3)) + ') {';
    }
    if (match.indexOf('else if') === 0) {
        return '} else if (' + changeVarInJS(match.slice(8)) + ') {';
    }
    if (match === 'else') {
        return '} else {';
    }
    if (match === '/if') {
        return '}';
    }
    if (isTernary(match)) {
        var cons = match.split('?');
        var con = cons[0].replace(/\$/, 'data.');
        var ret1 = cons[1].split(':')[0].replace(/\$/, '').trim();
        var ret2 = cons[1].split(':')[1].replace(/\$/, '').trim();
        var code = '';

        eval('if(' + con + '){code="{{' + ret1 + '}}"}else{code="{{' + ret2 + '}}"}');

        return 'code+=\'' + code + '\';';
    }
    return '';
};

var rItem = /(for (\w+) in)|(for \((\w+),)/;

function getLastValue(arr) {
    var len = arr.length;
    var i = len - 1;
    while (arr[i] == undefined) {
        i--;
    }
    return arr[i];
}

function listParser(tempSplit, startIndex) {
    var i = startIndex;
    var line = 0;
    var code = '';
    var depth = 0; // 列表循环的深度
    depth++;
    var list = unwrap(tempSplit[i]).match(/\$\w+/)[0].replace(/\$/, 'this._data.');
    var item = getLastValue(unwrap(tempSplit[i]).match(rItem));
    var index = unwrap(tempSplit[i]).match(/, (\w+)\)/) instanceof Array ? unwrap(tempSplit[i]).match(/, (\w+)\)/).pop() : 'index';
    code += 'foreach(' + list + ', function(' + index + ', ' + item + ') {';
    i++;
    while (depth > 0) {
        if (unwrap(tempSplit[i]).indexOf('for ') === 0) {
            var list = getLastValue(tempSplit[i].match(/in ([^}]+)/));
            var item = getLastValue(unwrap(tempSplit[i]).match(rItem));
            var index = unwrap(tempSplit[i]).match(/, (\w+)\)/) instanceof Array ? unwrap(tempSplit[i]).match(/, (\w+)\)/).pop() : 'index';
            code += 'foreach(' + list + ', function(' + index + ', ' + item + ') {';
            depth++;
        } else if (unwrap(tempSplit[i]) === '/for') {
            code += '});';
            depth--;
        } else {
            code += 'code += \'' + tempSplit[i].replace(/{{[^}}]*}}/g, function (match) {
                var mat = unwrap(match).indexOf(index) > -1 ? 'parseInt(' + unwrap(match) + ')' : unwrap(match);
                return '\'+' + mat + '+\'';
            }) + '\';';
        }
        i++;
        line++;
    }
    return {
        code: code,
        line: line
    };
}

// avoid XSS
function encodeStr(str) {
    return str.replace(/&/g, '&amp;') // &
    .replace(/</g, '&lt;') // 左尖号
    .replace(/>/g, '&gt;') // 右尖号
    .replace(/"/g, '&quot;') // 双引号"
    .replace(/'/g, '&#039;'); // IE下不支持&apos;'
}

{
    assert(jQuery, 'pekka requires jQuery!');
}

function Pekka(template, data, $ctx) {
    this._$ctx = $ctx;
    this._data = data;
    this._template = template;

    this._code = ''; // 解析JS后剩下的html代码
    this._html = ''; // 最终生成的html代码
    return this._init();
}

Pekka.prototype = {
    constructor: Pekka,
    _init: function _init() {
        return this.parse();
    },
    parse: function parse() {
        var code = '';
        var foreach = each;

        // 移除html模板里的注释代码，避免解析错误
        var newTemp = this._template.replace(/<!--[^-->]*-->/g, '');

        var tempSplit = newTemp.split('\n');
        tempSplit = map(tempSplit, function (item) {
            return clearBlank(item);
        });

        for (var i = 0, len = tempSplit.length; i < len; i++) {
            var conditionalRet = conditionalParser(unwrap(tempSplit[i]), this._data);
            if (conditionalRet) {
                this._code += conditionalRet;
                continue;
            }
            if (unwrap(tempSplit[i]).indexOf('for ') === 0) {
                var listRet = listParser(tempSplit, i);
                i += listRet.line;
                this._code += listRet.code;
                continue;
            }
            this._code += 'code += \'' + tempSplit[i] + '\';';
        }
        console.log(this._code);
        eval(this._code);
        this._html = this._unwrapAll(code);
    },
    render: function render() {
        {
            this._$ctx.html(this._html);
        }
    },
    _unwrapAll: function _unwrapAll(str) {
        var _this = this;

        return str.replace(/{{[^{}]*}}/g, function (match) {
            return encodeStr(_this._data[unwrap(match)].toString());
        });
    }
};

$.fn.pekka = function (template, data) {
    return new Pekka(template, data, $(this)).render();
};

}());
