$(function() {
    
    var startYear = -20000,
        endYear = 2012,
        gamma = 2,
        zoomLevel = 4,
        zoomStep = Math.SQRT2,
        minZoomLevel = 1,
        maxZoomLevel = 256,
        sliderWidth = 3000,
        $slider = $(".timeline-mill-slider"),
        $tlcontainer = $(".timeline-container"),
        canvas = $tlcontainer.find('canvas')[0],
        lineDistances = [ 50000, 25000, 10000, 5000, 2500, 1000, 500, 250, 100, 50, 25, 10, 5, 2 ],
        miniLines = [-20000, -5000, -1000, 0, 500, 1000, 1500, 2000],
        wCenter = .875,
        itemCount = 12,
        tlCache = [], tlIdCache = [], currentTerm = null,
        startSlide = startYear, endSlide = endYear, userStartSlide = startSlide, userEndSlide = endSlide,
        fromYear, toYear,
        isRtl = $("html").is("[dir=rtl]");
    
    function tToW(t) {
        //return Math.pow(2 / (2-Math.min(1,Math.max(0,t))) - 1,gamma);
        return Math.pow(t, gamma);
    }
    function wToT(w) {
        //return 2 - 2/(Math.pow(Math.min(1,Math.max(0,w)),1/gamma)+1);
        return Math.pow(w, 1/gamma);
    }
    function tToYr(t) {
        return startYear + (endYear - startYear) * t;
    }
    function yrToT(yr) {
        return (yr - startYear)/(endYear - startYear);
    }
    function wToYr(w) {
         return tToYr(wToT(w));
    }
    function yrToW(yr) {
        return tToW(yrToT(yr));
    }
    function deltaXToDeltaW(dx) {
        return (isRtl ? -1 : 1) * dx / (zoomLevel * cWidth);
    }
    function deltaWToDeltaX(dw) {
        return (isRtl ? -1 : 1) * dw * zoomLevel * cWidth;
    }
    function wToX(w) {
        return (isRtl ? cWidth : 0) + deltaWToDeltaX(w - wCenter + (.5 / zoomLevel));
    }
    function xToW(x) {
        return deltaXToDeltaW(x - (isRtl ? cWidth : 0)) + wCenter - (.5 / zoomLevel);
    }
    function wToMini(w) {
        var res = cWidth * w;
        if (isRtl) {
            res = cWidth - res;
        }
        return res;
    }
    function deltaMiniToDeltaW(m) {
        return (isRtl ? -1 : 1) * m / cWidth;
    }
    function wToSlider(w) {
        var res = zoomLevel * sliderWidth * (w - wCenter + (.5 / zoomLevel));
        if (isRtl) {
            res = sliderWidth - res;
        }
        return res;
    }
    function sliderToW(slidepos) {
        var p = slidepos;
        if (isRtl) {
            p = sliderWidth - p;
        }
        return p / (sliderWidth * zoomLevel) + wCenter - (.5 / zoomLevel);
    }
    function yrToMiniX(yr) {
        return wToMini(yrToW(yr));
    }
    function yrToX(yr) {
        return wToX(yrToW(yr));
    }
    function yrToSliderPos(yr) {
        return wToSlider(yrToW(yr));
    }
    function sliderPosToYr(slidepos) {
        return wToYr(sliderToW(slidepos));
    }
    
    function boundValue(val) {
        return Math.max(Math.floor(fromYear), Math.min(Math.floor(toYear), val));
    }
    function updateCoords() {
        cWidth = $tlcontainer.width();
        cHeight = $tlcontainer.height();
        wLineDist = null;
        fromYear = wToYr(wCenter - .5 / zoomLevel);
        toYear = wToYr(wCenter + .5 / zoomLevel);
        var wAtQuarter = (wCenter - .25 /zoomLevel),
            yearAtQuarter = wToYr(wAtQuarter),
            posAtQuarter = wToX(wAtQuarter);
        for (var i = 0; i < lineDistances.length; i++) {
            var yr = yearAtQuarter + lineDistances[i],
                x = yrToX(yr),
                delta = x - posAtQuarter;
            if (Math.abs(delta) < 50) {
                break;
            }
            wLineDist = lineDistances[i];
        }
        startSlide = boundValue(userStartSlide);
        endSlide = boundValue(userEndSlide);
        var startPos = yrToSliderPos(startSlide),
            endPos = yrToSliderPos(endSlide);
        $slider.slider("values",[Math.min(startPos, endPos),Math.max(startPos, endPos)]);
        updateSpans();
    }
    
    var itemTpl = _.template(
        '<li class="timeline-item"><div class="timeline-item-box<%- current ? " timeline-current" : "" %>"'
        + ' data-dbpedia-uri="<%- item.dbpedia_uri %>" style="left: <%- left %>px; width: <%- width %>px;">'
        + '<div class="timeline-item-label"><%- item.label %></div></div></li>'
    );
    
    function redrawView() {
        canvas.width = cWidth;
        canvas.height = cHeight;
        
        var ctx = canvas.getContext("2d");
        
        var xstart = yrToMiniX(fromYear),
            xend = yrToMiniX(toYear),
            xleft = Math.min(xstart, xend),
            xright = Math.max(xstart, xend);
        ctx.fillStyle = "#f0f2ff";
        ctx.strokeStyle = "#3090ff";
        ctx.beginPath();
        ctx.moveTo(xleft, 0);
        ctx.lineTo(xright, 0);
        ctx.lineTo(xright, 35);
        ctx.lineTo(cWidth, 70);
        ctx.lineTo(cWidth, cHeight);
        ctx.lineTo(0, cHeight);
        ctx.lineTo(0, 70);
        ctx.lineTo(xleft, 35);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
        ctx.fillStyle = "#666666";
        ctx.strokeStyle = "#666666";
        ctx.fillStyle = "rgba(220, 220, 220, .5)";
        ctx.fillRect( 0, 0, cWidth, 30 );
        ctx.font = 'bold 12px Arial,Helvetica';
        ctx.fillStyle = "#333333";
        for (var i = 0; i < miniLines.length; i++ ) {
            var y = miniLines[i],
                x = yrToMiniX(y);
            ctx.beginPath();
            ctx.moveTo(x,0);
            ctx.lineTo(x,6);
            ctx.moveTo(x,24);
            ctx.lineTo(x,30);
            ctx.stroke();
            ctx.textAlign = (i ? (i == miniLines.length - 1 ? (isRtl ? "left" : "right" ) : "center") : (isRtl ? "right" : "left" ) );
            ctx.fillText(y || 1, x,20);
        }
        ctx.textAlign = 'center';
        ctx.font = 'bold 14px Arial,Helvetica';
        ctx.fillStyle = "#000000";
        for (var y = wLineDist * Math.ceil(fromYear/wLineDist); y <= toYear; y += wLineDist ) {
            var x = yrToX(y),
                isPrimary = !((y/wLineDist) % 2);
            ctx.strokeStyle = (isPrimary ? "#000000": "#999999");
            ctx.beginPath();
            ctx.moveTo(x, 60);
            ctx.lineTo(x, cHeight - 20);
            ctx.stroke();
            if (isPrimary) {
                ctx.fillText(y || 1, x, cHeight - 4);
            }
        }
        var html = _(tlCache).chain()
            .filter(function(item) {
                return (item.end_year >= fromYear && item.start_year <= toYear);
            }).first(itemCount)
            .sortBy(function(item) {
                return (item.start_year + item.end_year);
            }).map(function(item) {
                var l = Math.max(0, yrToX(item.start_year)),
                    r = Math.min(cWidth, yrToX(item.end_year + 1));
                return itemTpl({
                    current: (item.dbpedia_uri == currentTerm),
                    item: item,
                    left: Math.min(l,r),
                    width: Math.abs(r-l)
                });
            }).value().join("");
        $(".timeline-list").html(html);
        dbpediaBox.bind(".timeline-item-box");
        $(".timeline-item-box").click(function() {
            var $this = $(this);
            currentTerm = $this.attr("data-dbpedia-uri");
            $(".timeline-item-box").removeClass("timeline-current");
            $this.addClass("timeline-current");
            loadSearchResults({ dbpedia_uri: currentTerm });
        });
    }
  
    function getData() {
        $.getJSON(
            urls.ajax_years,
            {
                from_year: Math.floor(fromYear),
                to_year: Math.floor(toYear)
            },
            function(data) {
                _(data).each(function(term) {
                    if (tlIdCache.indexOf(term.dbpedia_uri) === -1) {
                        tlIdCache.push(term.dbpedia_uri);
                        tlCache.push(term);
                    }
                });
                tlCache = _(tlCache).sortBy(function(item) {
                    return -item.nb_notice;
                });
                throttledRedraw();
            }
        );
    }

    var throttledRedraw = _.throttle(function() {
        updateCoords();
        redrawView();
    }, 100);
    
    var debouncedGetData = _.debounce(getData, 1000);
        
    var mousedown = false, dragging = false, xstart = null, wcStart, dragmini;
    
    $tlcontainer.mousedown(function(e) {
        dragging = false;
        mousedown = true;
        dragmini = (e.clientY - $tlcontainer.offset().top < 40);
        xstart = e.clientX;
        wcStart = wCenter;
        return false;
    }).mousemove(function(e) {
        if (mousedown) {
            dragging = true;
            var halfSpan = .5/zoomLevel;
            wCenter = Math.max(
                halfSpan,
                Math.min(
                    1 - halfSpan,
                    dragmini ?
                        (wcStart + deltaMiniToDeltaW(e.clientX - xstart)) :
                        (wcStart + deltaXToDeltaW(xstart - e.clientX))
                    )
                );
            throttledRedraw();
            debouncedGetData();
            return false;
        }
    }).mousewheel(function(e, d) {
        var wMouse = xToW(e.clientX - $(this).offset().left),
            scaleRatio = (d > 0 ? zoomStep : 1/zoomStep);
        if (d < 0 && zoomLevel <= minZoomLevel) {
            return;
        }
        if (d >= 0 && zoomLevel >= maxZoomLevel) {
            return;
        }
        zoomLevel *= scaleRatio;
        var halfSpan = .5/zoomLevel;
        wCenter = Math.max(halfSpan, Math.min(1 - halfSpan, wMouse * (1 - 1 / scaleRatio) + wCenter / scaleRatio ));
        throttledRedraw();
        debouncedGetData();
    });
    
    $("body").mouseup(function() {
        mousedown = false;
        dragging = false;
    });
    
    var $startSpan = $(".timeline-mill-from-year"),
        $endSpan = $(".timeline-mill-to-year");
    
    function updateSpans() {
        $startSpan.text(startSlide);
        $endSpan.text(endSlide);
    }
    
    updateSpans();
    
    $slider.slider({
        range: true,
        min: 0,
        max: sliderWidth,
        values: [0, sliderWidth],
        slide: function(e, ui) {
            var y0 = Math.floor(sliderPosToYr(ui.values[0])),
                y1 = Math.floor(sliderPosToYr(ui.values[1]));
            userStartSlide = startSlide = Math.min(y0,y1);
            userEndSlide = endSlide = Math.max(y0,y1);
            updateSpans();
        }
    });
    
    $(".timeline-mill-submit").click(function() {
        loadSearchResults({ from_year: startSlide, to_year: endSlide, show_years: 1 });
    });

    throttledRedraw();
    
    getData();
    
    $(window).resize(throttledRedraw);
    
});
