var SECONDS = 1000,
    MINUTES = 60 * SECONDS,
    HOURS = 60 * MINUTES;

var config = {
    partnerCode: 'B00015838755',
    originalDuration: 24 * HOURS,
    vizDuration: 8 * MINUTES,
    colorIds: {
        "-2.0": 2,
        "0.5": 0, "1.0": 0, "1.5": 0, "2.0": 0,
        "2.5": 1, "3.0": 1, "3.5": 1,
        "4.0": 2, "4.5": 2, "5.0": 2
    },
    movieCount: 10,
    opinionsPerPage: 100,
    colors: [ "#f62a43", "#f3c000", "#2d9252"],
    borderColors: [ "#f7788e", "#fdde83", "#3bb767" ],
    stripeColors: [ "#c92238", "#c79e03", "#237844" ],
    refreshRate: .25 * SECONDS,
    columnSpacing: 40,
    columnWidth: 150,
    tokenHeight: 16,
    fallSpeed: 4,
    disappearAfter: 4 * SECONDS
}

$(function() {
    
    var originalStart = Date.now() - config.originalDuration,
        timeScale = config.vizDuration / config.originalDuration,
        movies,
        moviesToLoad,
        opinions = [],
        loadTime = Date.now(),
        clockInterval,
        tzDelta = 0,
        tzSuffix = 'Z';
    
    
    // Add Patterns
    var xml = '<svg style="position: absolute;" width="1" height="1"><defs>'
        + config.colors.map(function(color, i) {
            return '<pattern id="pattern_'
                + i
                + '" x="0" y="0" width="3" height="3" patternUnits="userSpaceOnUse"><rect x="0" y="0" width="3" height="3" fill="'
                + color
                + '" />'
                + _.range(3).map(function(n) {
                    return '<rect x="'
                        + n
                        + '" y="'
                        + n
                        + '" width="1" height="1" fill="'
                        + config.stripeColors[i]
                        + '" />'
                }).join("")
                + '</pattern>';
        }).join("")
        + '</defs></svg>';
    $("body").append(xml);
        
    function resizeTimer() {
        var w = Math.max(80, $(window).width() - 158),
            n = Math.min(12, Math.floor(w / 80));
    	$('.timer-bar').width(w);
    	var html = '';
    	for (var k = 0; k < n + 1; k++) {
    	    html += '<li style="left:'
    	       + Math.floor(k * w / n)
    	       + 'px;">'
    	       + new Date(originalStart + k * config.originalDuration / n).toTimeString().substr(0,5)
    	       + '</li>';
    	}
        $('.hours').html(html);
    }
    
    resizeTimer();
    $(window).resize(resizeTimer);
    
    var vsHeight = $(window).height() - $('footer').height() - $('header').height() - 300,
        vsWidth = config.movieCount * (config.columnWidth + config.columnSpacing);
        
    $("#data-viz").css({
        height: vsHeight,
        width: vsWidth
    });
    $('.posters').css('width', vsWidth);
    
    var timeAtPause = 0, paused = true, timeStart;
    
    function currentVizTime() {
        if (paused) {
            return timeAtPause;
        } else {
            return new Date().valueOf() - timeStart;
        }
    }
    
    function vizTimeToOriginalTime(t) {
        return originalStart + t / timeScale;
    }
    
    function originalTimeToVizTime(t) {
        return (t - originalStart) * timeScale;
    }
    
    function play() {
        if (!paused) {
            return;
        }
        timeStart = Date.now() - timeAtPause;
        paused = false;
        $(".play-pause").removeClass("play").addClass("pause").attr("title", "Pause");
    }
    
    function pause() {
        if (paused) {
            return;
        }
        timeAtPause = currentVizTime();
        paused = true;
        $(".play-pause").removeClass("pause").addClass("play").attr("title", "Lecture");
    }
    
    $(".play-pause").click(function() {
        if (paused) {
            play();
        } else {
            pause();
        }
    });
    
    function agoify(timestamp) {
        var h = "Il y a ",
            deltaT = Date.now() - timestamp,
            deltaH = Math.round(deltaT / HOURS);
        if (deltaH) {
            h += deltaH + " heure";
            if (deltaH > 1) {
                h += "s";
            }
        } else {
            var deltaM = Math.round(deltaT / MINUTES);
            h += deltaM + " minute";
            if (deltaM > 1) {
                h += "s";
            }
        }
        return h;
    }
    
    var opinionTemplate = _.template(
        '<div class="comment-content clearfix action-<%- actiontype %>">'
            + '<div class="avatar"><img src="<%- avatar %>" alt="Avatar" /></div>'
            + '<div class="comment-right">'
                + '<div class="comment-metadata">'
                    + '<span class="username"><%- username %> </span>'
                    + ' <span class="date"> <%- ago %></span>'
                    + '<ul class="stars-rating rate-<%- ratevalue %>">'
                        + '<li></li><li></li><li></li><li></li><li></li>'
                    + '</ul>'
                + '</div>'
                + '<p class="review-content"><%- content %></p>'
            + '</div>'
        + '</div>'
        + '<div class="comment-arrow"></div>'
    );
    
    function startViz() {
        
        var barChart = undefined;
        
        timeAtPause = 0;
        
        opinions.sort(function(a,b) {
            return a._timestamp > b._timestamp;
        });
        $("#data-viz").html("").css("text-align","left");
        
        var lastShownToken = null,
            maxData = Math.max.apply(Math, movies.map(function(m) { return m.opinions.length; })),
            aggrHeight = vsHeight - 80,
            aggrScale = aggrHeight / maxData;
        
        var barChartSettings = {
            width : vsWidth,
            height : vsHeight,
            chart : {
                spacer : config.columnSpacing / 2,
                y: 0,
                height: vsHeight
            },
            options : {
                layout: false
            },
            data : {
                model : movies.map(function(movie) {
                    return {
                        label: movie.title
                    };
                }),
                strata : function() {
                    if (typeof barChart === "undefined") {
                        var flocculated = [];
                        _.range(3).map(function(colorId) {
                            $(".layer_"+colorId).css({
                                fill: "url(#pattern_" +colorId + ")",
                                stroke: config.borderColors[colorId]
                            });
                        });
                    } else {
                        var flocculated = barChart.selectAll("state",2);
                    }
                    
                    var res = movies.map(function(movie, i) {
                        var movieTokens = flocculated.filter(function(t) {
                            return t.attr("movie") === movie.id;
                        });
                        var movieHeight = aggrScale * movieTokens.length;
                        movie.poster$.find(".poster-overlay").css("top", -movieHeight);
                        return _.range(3).map(function(colorId) {
                            var stratumTokens = movieTokens.filter(function(t) {
                                return t.attr("colorId") === colorId;
                            }).length;
                            movie.poster$.find(".poster-" + colorId).text(stratumTokens);
                            return {
                                value: function() {
                                    return stratumTokens
                                }
                            };
                        });
                    });
                    
                    return res;
                },
                stream : {
                    provider : "direct"
                }
            },
            sedimentation: {
                token: {
                    size: {
                        original:12,
                        minimum:0
                    }
                },
                aggregation:{
                    height: aggrHeight,
                    maxData: maxData
                },
                suspension:{
                    decay:{
                        power:1.001
                    }
                }
            }
        }
        
        function onTokenMouseover(token) {
            if (token !== lastShownToken) {
                $("body").css("cursor", "pointer");
                commentDiv.html(opinionTemplate({
                    actiontype: token.attr("actiontype"),
                    avatar: token.attr("avatar"),
                    username: token.attr("username"),
                    ago: agoify(token.attr("timestamp")),
                    ratevalue: token.attr("actionvalue"),
                    content: token.attr("content")
                })).show();
                lastShownToken = token;
            }
        }
        
        function onTokenMouseout(token) {
            lastShownToken = null;
            setTimeout(function() {
                if (!lastShownToken) {
                $("body").css("cursor", "");
                    commentDiv.hide();
                }
            }, 200);
        }
        
        //console.log(barChartSettings);
        
        var barChart = $("#data-viz").vs(barChartSettings).data('visualSedimentation');
        
        barChart.world.SetGravity(new barChart.phy.b2Vec2(0,10 * config.fallSpeed));
        
        $(".timer-bar-value").width(0);
        
        play();
        
        clearInterval(clockInterval);
        
        var lastTimestamp = 0;
        
        clockInterval = setInterval(function() {
            
            if (paused) {
                return;
            }
            
            var vizT = currentVizTime();
            
            var tokensToAdd = opinions.filter(function(o) {
                    return o._viztime > lastTimestamp && o._viztime <= vizT
                });
            
            lastTimestamp = vizT;
            
            tokensToAdd.forEach(function(opinion) {
                barChart.addToken({
                    category: opinion._column,
                    datetime: opinion.datetime,
                    timestamp: opinion._timestamp,
                    viztime: opinion._viztime,
                    movie: opinion._movie,
                    colorId: opinion._colorId,
                    fillStyle: config.colors[opinion._colorId],
                    avatar: opinion._avatar,
                    username: opinion.actor.user.userName,
                    actiontype: opinion.action.type,
                    actionvalue: opinion.action.value,
                    content: opinion._content.replace(/(^.{320,340})[\s].+$/,'$1…'),
                    //size: 30,
                    strokeStyle: config.borderColors[opinion._colorId],
                    shape:{
                      type:'box',
                      width: config.columnWidth / 2 - 1,
                      height: config.tokenHeight / 2
                    },
                    callback: {
                        mouseover: onTokenMouseover,
                        mouseout: onTokenMouseout
                    }
                })
            });
            
            var deleteT = vizT - config.disappearAfter;
            
            barChart.selectAll("state",1)
                .filter(function(t) {
                    return t.attr("viztime") <= deleteT;
                }).forEach(function(t) {
                    t.flocculate();
                });
                
            if (deleteT > config.vizDuration) {
                pause();
            }
            
            barChart.strata.update(barChart);

            var w = $('.timer-bar').width() * Math.min(1, vizT / config.vizDuration);
            $(".timer-bar-value").width(w);
            
        }, config.refreshRate);
    }
    
    function loadMovie(movie) {
        $.getJSON(
            "http://api.allocine.fr/rest/v3/opinionlist",
            {
                partner: config.partnerCode,
                format: "json",
                subject: "movie:" + movie.id,
                count: config.opinionsPerPage,
                refresh: refreshtoken(),
                page: movie.opinionPage
            },
            function(d) {
                if (d.feed && d.feed.activity) {
                    var tokens = d.feed.activity.filter(function(a) {
                        return a.action.type !== "notinterested" && a.action.value !== "-3.0";
                    });
                } else {
                    var tokens = [];
                }
                tokens.forEach(function(a) {
                    a._colorId = config.colorIds[a.action.value];
                    a._timestamp = Date.parse(a.datetime + tzSuffix) - tzDelta;
                    a._viztime = originalTimeToVizTime(a._timestamp);
                    a._movie = movie.id;
                    a._column = movie.column;
                    a._avatar = ((typeof a.actor.user.avatar === "undefined" || /https?:\/\/graph\.facebook\.com/.test(a.actor.user.avatar.href)) ? "http://fr.web.img6.acsta.net/r_50_x/commons/emptymedia/AvatarAllocineMr.gif" : resizeAcPicture( a.actor.user.avatar, 50, "x" ));
                    a._content = ( a.action.type === "userreview" ? a.content.$ : (a.action.type === "wanttosee" ? "veut voir ce film." : "" ) ).replace(/[\n\r\s]+/mg,' ');
                });
                var tokcount = tokens.length;
                tokens = tokens.filter(function(a) {
                    return a._timestamp >= originalStart;
                });
                movie.opinions = movie.opinions.concat(tokens);
                opinions = opinions.concat(tokens);
                $("#data-viz").append(".");
                if (tokens.length === tokcount) {
                    console.log("Page " + movie.opinionPage + " of '" + movie.title + "' loaded");
                    movie.opinionPage ++;
                    loadMovie(movie);
                } else {
                    console.log("Page " + movie.opinionPage + " of '" + movie.title + "' -- total : " + movie.opinions.length + " opinions loaded");
                    moviesToLoad--;
                    if (!moviesToLoad) {
                        console.log("**** Everything is loaded, in " + (Date.now() - loadTime) / 1000 + " seconds");
                        startViz();
                    }
                }
            }
        );
    }
    
    var acimgserv = 0, acimgservcount = 6;
    
    function resizeAcPicture(pic, w, h) {
        var path = pic.path || pic.href || pic || "";
        if (/^https?:\/\/[\w\d\.]+\.acsta\.net\//.test(path)) {
            path = path.replace(/^https?:\/\/[^\/]+/,'');
        }
        if (path[0] === "/") {
            return "http://fr.web.img"
                + ( 1 + (acimgserv++ % acimgservcount))
                + ".acsta.net/r_"
                + w
                + "_"
                + h
                + path;
        }
        return path;
    }
    
    $.getJSON(
        "http://api.allocine.fr/rest/v3/movielist",
        {
            partner: config.partnerCode,
            format: "json",
            filter: "top:week",
            count: config.movieCount,
            refresh: refreshtoken()
        },
        function(d) {
            $("#data-viz").html("Chargement des flux d'opinions<br />");
            console.log("Movie List Loaded");
            tzDelta = .5 * HOURS * Math.round((Date.parse(d.feed.updated) - loadTime) / (.5 * HOURS));
            tzSuffix = d.feed.updated.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,'');

            movies = [];
            for (var i = 0; i < d.feed.movie.length; i++) {
                var movie = d.feed.movie[i];
                if (i % 2) {
                    movies.push(movie);
                } else {
                    movies.splice(0,0,movie);
                }
            }
            movies = movies.map(function(movie, i) {
                return {
                    id: movie.code,
                    column: i,
                    title: movie.title,
                    poster: movie.poster.href,
                    opinions: [],
                    opinionPage: 1
                };
            });
            moviesToLoad = movies.length;
            movies.forEach(loadMovie);
            $('.posters').html(movies.map(function(movie, i) {
                return '<li class="poster" style=" margin: 0 '
                    + Math.floor(config.columnSpacing / 2)
                    + 'px" data-movie-index="'
                    + i
                    + '"><img width="150" height="200" src="'
                    + resizeAcPicture(movie.poster,150,200)
                    + '" alt="'
                    + movie.title
                    + '" /><div class="poster-overlay"><div class="poster-datainfo">'
                    + '<ul class="poster-distribution"><li class="poster-2">0</li><li class="poster-1">0</li><li class="poster-0">0</li>'
                    + '</ul></div></div></li>';
            }).join(""));
            
            $(".poster")
                .click(function() {
                    $("#comment-modal").modal({
                        containerCss: {
                            height: $(window).height()-200,
                            width: Math.min($(window).width()-200, 640)
                        },
                        overlayClose: true
                    });
                    var vizT = currentVizTime(),
                        movie = movies[$(this).attr("data-movie-index")],
                        opsToShow = movie.opinions.filter(function(o) {
                            return o._viztime <= vizT;
                        });
                    $(".comment-count").text(opsToShow.length);
                    $(".comment-subject").text(movie.title);
                    $(".comment-start").text(new Date(originalStart).toTimeString().substr(0,5));
                    $(".comment-end").text(new Date(vizTimeToOriginalTime(vizT)).toTimeString().substr(0,5));
                    $(".comment-list").html(opsToShow.map(function(o) {
                        return opinionTemplate({
                            actiontype: o.action.type,
                            avatar: o._avatar,
                            username: o.actor.user.userName,
                            ago: agoify(o._timestamp),
                            ratevalue: o.action.value,
                            content: o._content
                        });
                    }).join(""))
                        .mCustomScrollbar({
                            autoDraggerLength:false,
                            scrollButtons:{
                                enable:false
                            }
                        });
                    return false;
                })
                .each(function(i) {
                    movies[i].poster$ = $(this);
                });
        }
    );
       
    function refreshtoken() {
        return Math.floor(0x100000000 * Math.random()).toString(16) + "-" + (Date.now() % 0x100000000).toString(16);
    }
    
    $(".restart").click(function() {
        startViz();
        return false;
    });
    
    var commentDiv = $("#comment");
    
    $("#data-viz").mousemove(_.throttle(function(e) {
        commentDiv
            .css({
                left: e.pageX,
                top: e.pageY + 2
            })
            .attr("className", (e.pageX < $(window).width() / 2 ? "left" : "right"))
    }, 50));

});