function drawSummaries() {
    for (var oHtml = $('<div class="j-tree-summaries"></div>'), tree = 0; 3 > tree; tree++) oHtml.append($("<div>").addClass("tree-summary").addClass(treeNames[tree]).attr("data-idx", tree).text(treeNames[tree] + ": 0").css({
        left: TREE_OFFSET * tree + 10,
        cursor: "pointer"
    }));
    return oHtml
}

function drawCalculator() {
    for (var $calculator = $('<div class="j-calculator tallent__list"></div>'), tree = 0; 3 > tree; tree++)
        for (var index = 0; index < data[tree].length; index++) {
            drawButton(tree, index, $calculator);
        }
    var tip, maxDims = {
        width: $calculator.parent().width(),
        height: $calculator.parent().height()
    };
    return $calculator.append($("<div>").addClass("j-tooltip").append($("<strong>")).append($("<div>").addClass("rank")).append($("<div>").addClass("req")).append($("<p>").addClass("tooltip-text").addClass("first")).append($("<p>").addClass("tooltip-text").addClass("second").append($("<div>").addClass("nextRank").text("下一层:")).append($("<div>").addClass("content")))), tip = $calculator.find(".j-tooltip"), $calculator.mousemove(function(event) {
        if (tip.is(":visible")) {
            var pos = $calculator.offset(),
                offsetX = 20,
                offsetY = 20;
            event.pageY - pos.top + tip.height() > maxDims.height && (offsetY = -tip.height() - 30), event.pageX - pos.left > tip.width() && (offsetX = -tip.width()), tip.css({
                left: event.pageX - pos.left + offsetX,
                top: event.pageY - pos.top + offsetY
            })
        }
    }), $calculator
}

function deltaMastery(tree, index, rank, deltaR) {
    if (isValidState(tree, index, rank, deltaR)) {
        var previous = masteryTierFull(tree, index);
        previous >= 0 && deltaR > 0 ? setState(tree, previous, state[tree][previous], -deltaR) : deltaR > 0 && masteryTierEmpty(tree, data[tree][index].tier) && data[tree][index].ranks > 1 && totalPoints + data[tree][index].ranks <= MAX_POINTS && (deltaR = data[tree][index].ranks), setState(tree, index, rank, deltaR)
    }
}

function drawButton(tree, index, $root) {
    var spritePos = masterySpritePos(tree, index),
        buttonPos = masteryButtonPosition(tree, index),
        status = 0 == data[tree][index].tier ? "available" : "unavailable",
        rank = 0,
        tier = data[tree][index].tier;
    $root.append($("<div>").addClass("button").addClass(status).css({
        left: buttonPos.x + "px",
        top: buttonPos.y + "px",
        backgroundPosition: ("full" == status ? -2 : "available" == status ? -2 - BUTTON_SIZE : -2 - 2 * BUTTON_SIZE) + "px " + (spritePos - 1) + "px"
    }).append($("<div>").addClass("buttonFrame").css({
        backgroundPosition: ("full" == status ? tier % 2 == 1 ? 1 == tier ? -76 : -38 * tier : -38 : "available" == status ? -38 : 0) + "px " + ("full" == status ? 3 == tier ? -38 : 5 == tier ? -76 : 0 : 0) + "px"
    })).append($("<div>").addClass("counter").addClass("counter-" + status).text("0/" + data[tree][index].ranks).css({
        visibility: data[tree][index].tier % 2 == 0 ? "visible" : "hidden"
    })).mouseenter(function() {
        var $tooltip = $(this).parents(".j-calculator").find(".j-tooltip"),
            tooltipText = masteryTooltip(tree, index, rank);
        formatTooltip($tooltip.show(), tooltipText), $(this).data("hover", !0), $(this).parent().mousemove()
    }).mouseleave(function() {
        var $tooltip = $(this).parents(".j-calculator").find(".j-tooltip");
        $tooltip.hide(), $tooltip.data("hover", !1)
    }).data("update", function() {
        rank = state[tree][index] || 0, rank > 0 ? status = "full" : (status = masteryPointReq(tree, index) <= treePoints(tree) ? "available" : "unavailable", totalPoints >= MAX_POINTS && (status = masteryTierFull(tree, index) >= 0 ? "available" : "unavailable")), $(this).hasClass(status) || $(this).removeClass(buttonClasses.join(" ")).addClass(status).css({
            backgroundPosition: ("full" == status ? -2 : "available" == status ? -2 - BUTTON_SIZE : -2 - 2 * BUTTON_SIZE) + "px " + (spritePos - 2) + "px"
        });
        var counter = $(this).find(".counter").text(rank + "/" + data[tree][index].ranks);
        counter.hasClass("counter-" + status) || counter.removeClass(counterClasses.join(" ")).addClass("counter-" + status), $(this).find(".buttonFrame").css({
            backgroundPosition: ("full" == status ? tier % 2 == 1 ? 1 == tier ? -76 : -38 * tier : -38 : "available" == status ? -38 : 0) + "px " + ("full" == status ? 3 == tier ? -38 : 5 == tier ? -76 : 0 : 0) + "px"
        }), $(this).data("hover") && $(this).mouseover()
    }))
}

function customTooltip(tooltip, tooltipText) {
    tooltip.addClass("custom"), tooltip.children(":not(p.first)").hide(), tooltip.find("p.first").text(tooltipText)
}

function formatTooltip(tooltip, tooltipText) {
    tooltip.removeClass("custom");
    var head = tooltip.find("strong").text(tooltipText.header).show();
    head.hasClass(treeNames[tooltipText.tree]) || head.removeClass(treeNames.join(" ")).addClass(treeNames[tooltipText.tree]);
    var rank = tooltip.find(".rank").text(tooltipText.rank).show();
    rank.hasClass(tooltipText.rankClass) || rank.removeClass(rankClasses.join(" ")).addClass(tooltipText.rankClass), tooltip.find(".req").text(tooltipText.req).show(), tooltip.find("p.first").html(tooltipText.body);
    var second = tooltip.find("p.second");
    null == tooltipText.bodyNext ? second.hide() : second.show().find(".content").html(tooltipText.bodyNext)
}

function masteryTooltip(tree, index, rank) {
    var mastery = data[tree][index],
        showNext = !(1 > rank || rank >= mastery.ranks),
        text = {
            tree: tree,
            header: mastery.name,
            rank: "等级: " + rank + "/" + mastery.ranks,
            rankClass: rank == mastery.ranks ? rankClasses[2] : isValidState(tree, index, rank, 1) ? rankClasses[1] : rankClasses[0],
            req: masteryTooltipReq(tree, index),
            body: masteryTooltipBody(mastery, rank),
            bodyNext: showNext ? masteryTooltipBody(mastery, rank + 1) : null
        };
    return text
}

function masteryTooltipBody(mastery, rank) {
    rank = Math.max(0, rank - 1);
    var desc = mastery.desc;
    return desc = desc.replace(/#/, mastery.rankInfo[rank]), desc = desc.replace(/\n/g, "<br>"), desc = desc.replace(/\|(.+?)\|/g, "<span class='highlight'>$1</span>"), mastery.rankInfo2 && (desc = desc.replace(/#/, mastery.rankInfo2[rank])), mastery.perLevel && (desc = desc.replace(/#/, Math.round(100 * mastery.perLevel[rank]) / 100)), mastery.perLevel2 && (desc = desc.replace(/#/, Math.round(100 * mastery.perLevel[rank]) / 100)), desc
}

function masteryTooltipReq(tree, index) {
    var missing = [],
        pointReq = masteryPointReq(tree, index);
    if (pointReq > treePoints(tree) && missing.push("需要" + pointReq + "点天赋投入" + treeNames[tree]), (state[tree][index] || 0) < data[tree][index].ranks) {
        var existing = masteryTierFull(tree, index);
        existing >= 0 && missing.push("选择这个将会移除在" + data[tree][existing].name + "的天赋点数。")
    }
    return missing.join("\n")
}

function masteryButtonPosition(tree, index) {
    var idx = data[tree][index].index - 1,
        ix = idx % 4,
        iy = data[tree][index].tier,
        x = 0,
        y = 0;
    x += TREE_OFFSET * tree;
    x += SPACING.margin_left;
    if((tree==0 || tree==1) && index >=2 && index <= 4){

    }else{
         if (iy % 2 == 1 && iy != 5 && iy != 1) x += SPACING.margin_keystone; // 偶数行的偏移
    }
    y += SPACING.margin_top;
    x += ix * (BUTTON_SIZE + SPACING.spacing_x); //x 的最终偏移 单行第2个为2 * (54 + 13) 偶数为 1* (54+13) 
    y += iy * (BUTTON_SIZE + SPACING.spacing_y); // y的最终偏移 为 (54+上边距) * 行数

    var dwOffset = 10;
    if(tree==0){
        if(index==2||index==7||index==12){x=20 + dwOffset;}
        if(index==3||index==8||index==13){x=61 + dwOffset;}
        if(index==4||index==9||index==14){x=100 + dwOffset;}
        if(index==0||index==5||index==10){x=39 + dwOffset;}
        if(index==1||index==6||index==11){x=83 + dwOffset;}
    }
    if(tree==1){
        if(index==2||index==7||index==12){x=184 + dwOffset;}
        if(index==3||index==8||index==13){x=225 + dwOffset;}
        if(index==4||index==9||index==14){x=266 + dwOffset;}
        if(index==0||index==5||index==10){x=202 + dwOffset;}
        if(index==1||index==6||index==11){x=246 + dwOffset;}
    }
    if(tree==2){
        if(index==2||index==7||index==12){x=352 + dwOffset;}
        if(index==3||index==8||index==13){x=393 + dwOffset;}
        if(index==4||index==9||index==14){x=434 + dwOffset;}
        if(index==0||index==5||index==10){x=371 + dwOffset;}
        if(index==1||index==6||index==11){x=415 + dwOffset;}
    }
    return {x: x, y: y};
}

function masterySpritePos(tree, index) {
    return 0 - BUTTON_SIZE * (treeOffsets[tree] + index)
}

function masteryTier(tree, index) {
    return data[tree][index].tier
}

function masteryPointReq(tree, index) {
    return TIER_REQS[masteryTier(tree, index)]
}

function masteryTierFull(tree, index) {
    var tier = data[tree][index].tier;
    for (var i in data[tree])
        if (i != index && data[tree][i].tier == tier && (state[tree][i] || 0) + (state[tree][index] || 0) >= data[tree][i].ranks) return i;
    return -1
}

function masteryTierEmpty(tree, tier) {
    for (var i in data[tree])
        if (data[tree][i].tier == tier && (state[tree][i] || 0) > 0) return !1;
    return !0
}

function treePoints(tree, treeTier) {
    var points = 0;
    for (var i in state[tree])(!treeTier || treeTier > masteryTier(tree, i)) && (points += state[tree][i]);
    return points
}

function isValidState(tree, index, rank, mod) {
    var mastery = data[tree][index];
    if (0 > rank + mod || rank + mod > mastery.ranks) return !1;
    if (mod > 0) {
        if (totalPoints + mod > MAX_POINTS) return masteryTierFull(tree, index) >= 0 ? !0 : !1;
        if (masteryPointReq(tree, index) > treePoints(tree) - rank) return !1
    }
    if (0 > mod) {
        for (var i in state[tree])
            if (i != index && state[tree][i] > 0 && masteryPointReq(tree, i) > treePoints(tree, masteryTier(tree, i)) - (masteryTier(tree, index) < masteryTier(tree, i))) return !1;
        for (var i in state[tree])
            if (i != index && state[tree][i] > 0 && data[tree][i].parent == index) return !1
    }
    return !0
}

function setState(tree, index, rank, mod) {
    state[tree][index] = rank + mod, totalPoints += mod, updateButtons(), updateLabels()
}

function resetStates(quiet) {
    for (var tree = 0; 3 > tree; tree++) resetTree(tree);
    1 != quiet && (updateButtons(), updateLabels())
}

function resetTree(tree) {
    totalPoints -= treePoints(tree);
    for (var index in state[tree]) state[tree][index] = 0
}

function updateButtons($dom) {
    $dom.find(".button").each(function() {
        $(this).data("update").call(this, 0)
    })
}

function updateLabels($dom, $con) {
    for (var tree = 0; 3 > tree; tree++) {
        var points = treePoints(tree);
        $dom.find("div[data-idx=" + tree + "]").text(treeNames[tree] + ": " + points), $con.find(".meda" + (tree + 1)).html(points)
    }
}

function escapeHtml(str) {
    var entityMap = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&#39;",
        "/": "&#x2F;"
    };
    return String(str).replace(/[&<>"'\/]/g, function(s) {
        return entityMap[s]
    })
}

function exportMasteries() {
    var tree, str = "",
        bits = 0,
        collected = 0,
        jumpStart = -1,
        flush = function() {
            str += exportChars[(jumpStart > -1) << maxbits | bits], bits = 0, collected = 0, jumpStart = -1
        };
    for (tree = 0; 3 > tree; tree++) {
        for (var index = 0; index < data[tree].length; index++) {
            var space = bitfit(tree, index, maxbits - collected);
            if (1 > space && (flush(), space = bitfit(tree, index, maxbits)), !(jumpStart > -1) || state[tree][index] > 0)
                if (collected > 0 || [0, 1, 2, 3, 4].filter(function(a) {
                        return space > a
                    }).map(function(a) {
                        return state[tree][index + a] || 0
                    }).some(function(a) {
                        return a > 0
                    })) {
                    jumpStart > -1 && (bits = index - jumpStart, flush());
                    var len = bitlen(tree, index);
                    bits = bits << len | (state[tree][index] || 0), collected += len
                } else 0 > jumpStart && (collected > 0 && flush(), jumpStart = index)
        }
        jumpStart > -1 ? (bits = index - jumpStart, flush()) : collected > 0 && flush()
    }
    return str
}

function importMasteries(param, $list, $sum, $con, championsId) {
    var str = param.talent_link,
        title = param.talent_title;
    resetStates(true);
    for (var tree = 0, index = 0, i = 0; i < str.length; i++) {
        var cur = importChars[str.charAt(i)];
        if (void 0 == cur) return;
        if (0 == (32 & cur))
            for (var num = bitfit(tree, index, maxbits), sizes = [0, 1, 2, 3, 4].filter(function(a) {
                    return num > a
                }).map(function(a) {
                    return bitlen(tree, index + a)
                }), j = 0; j < sizes.length; j++, index++) {
                var shift = sizes.slice(j + 1).reduce(function(a, b) {
                        return a + b
                    }, 0),
                    value = cur >> shift & (1 << sizes[j]) - 1;
                state[tree][index] = value, totalPoints += value
            } else {
                var dist = 31 & cur;
                index += dist
            }
        if (index >= data[tree].length && (tree++, index = 0, tree >= data.length)) break
    }
    var returnData = {
            talent_title: title,
            talent_data: state
        },
        $apply = $con.find(".btn_apply");
    $apply.attr("href", $apply.attr("href") + "?championsId=" + championsId + "&data=" + $.base64.btoa(encodeURIComponent(JSON.stringify(returnData))));
    updateButtons($list);
    updateLabels($sum, $con)
}
for (var treeNames = ["凶猛", "诡诈", "坚决"], treeOffsets = [0, data[0].length, data[0].length + data[1].length], MAX_POINTS = 30, TIER_REQS = [0, 5, 6, 11, 12, 17], TREE_OFFSET = 166, SPACING = {
        margin_left: 32,
        margin_top: 10,
        margin_keystone: 26,
        spacing_x: 10,
        spacing_y: 12
    }, BUTTON_SIZE = 28, state = [{}, {}, {}], totalPoints = 0, buttonClasses = ["unavailable", "available", "full"], rankClasses = ["num-unavailable", "num-available", "num-full"], counterClasses = ["counter-unavailable", "counter-available", "counter-full"], maxbits = 5, exportChars = "WvlgUCsA7pGZ3zSjakbP2x0mTB6htH8JuKMq1yrnwEQDLY5IVNXdcioe9fF4OR_-", bitlen = function(tree, index) {
        return void 0 == data[tree][index] ? 0 : Math.floor(data[tree][index].ranks / 2) + 1
    }, bitfit = function(tree, index, bits) {
        for (var start = index;;) {
            var len = bitlen(tree, index);
            if (len > bits || 0 == len) return index - start;
            bits -= len, index++
        }
    }, importChars = {}, i = 0; i < exportChars.length; i++) importChars[exportChars.charAt(i)] = i;

Array.prototype.filter || (Array.prototype.filter = function(fun) {
    "use strict";
    if (void 0 === this || null === this) throw new TypeError;
    var t = Object(this),
        len = t.length >>> 0;
    if ("function" != typeof fun) throw new TypeError;
    for (var res = [], thisArg = arguments.length >= 2 ? arguments[1] : void 0, i = 0; len > i; i++)
        if (i in t) {
            var val = t[i];
            fun.call(thisArg, val, i, t) && res.push(val)
        }
    return res
}), Array.prototype.map || (Array.prototype.map = function(callback, thisArg) {
    var T, A, k;
    if (null == this) throw new TypeError(" this is null or not defined");
    var O = Object(this),
        len = O.length >>> 0;
    if ("function" != typeof callback) throw new TypeError(callback + " is not a function");
    for (arguments.length > 1 && (T = thisArg), A = new Array(len), k = 0; len > k;) {
        var kValue, mappedValue;
        k in O && (kValue = O[k], mappedValue = callback.call(T, kValue, k, O), A[k] = mappedValue), k++
    }
    return A
}), Array.prototype.some || (Array.prototype.some = function(fun) {
    "use strict";
    if (null == this) throw new TypeError("Array.prototype.some called on null or undefined");
    if ("function" != typeof fun) throw new TypeError;
    for (var t = Object(this), len = t.length >>> 0, thisArg = arguments.length >= 2 ? arguments[1] : void 0, i = 0; len > i; i++)
        if (i in t && fun.call(thisArg, t[i], i, t)) return !0;
    return !1
}), Array.prototype.reduce || (Array.prototype.reduce = function(callback) {
    if (null == this) throw new TypeError("Array.prototype.reduce called on null or undefined");
    if ("function" != typeof callback) throw new TypeError(callback + " is not a function");
    var value, t = Object(this),
        len = t.length >>> 0,
        k = 0;
    if (2 == arguments.length) value = arguments[1];
    else {
        for (; len > k && !(k in t);) k++;
        if (k >= len) throw new TypeError("Reduce of empty array with no initial value");
        value = t[k++]
    }
    for (; len > k; k++) k in t && (value = callback(value, t[k], k, t));
    return value
});