IriSP.mashupcore = function(project, mashup) {
    
    var currentMedia,
        intervaltemplate = _.template('<span class="frise-indication" style="left:<%= left %>%;"><%= time.toString() %></span>'),
        viztemplate = _.template('<div class="frise-segment annotation" data-segment-id="<%= segmentid %>" style="background-color:<%= color %>; left:<%= left %>%; width:<%= width %>%;"></div>');

    function updateMashupUI() {
        var vizhtml = '', t = 0, k = mashup.duration ? (100 / mashup.duration) : 0;
        mashup.segments.forEach(function(_s) {
            var vizdata = {
                left: k * t,
                width: k * _s.duration,
                color: _s.color,
                segmentid: _s.annotation.id
            };
            vizhtml += viztemplate(vizdata);
            t += _s.duration.milliseconds;
        });
        
        var intervals = [ 1000, 2000, 5000, 10000, 30000, 60000, 120000, 300000, 600000, 900000, 1800000, 3600000, 7200000 ];
        
        function createIntervals(maxn) {
            for (var i = 0; i < intervals.length; i++) {
                if (mashup.duration / intervals[i] <= maxn) {
                    var html = '';
                    for (var j = intervals[i]; j < mashup.duration; j += intervals[i]) {
                        html += intervaltemplate({ left: k * j, time: new IriSP.Model.Time(j) });
                    }
                    return html;
                }
            }
            return "";
        }
        
        $(".mashup-total-duration").text(mashup.duration.toString());
        $(".mashup-frise .frise-segments").html(vizhtml);
        $(".mashup-frise .frise-indications").html(createIntervals(6));
        
        if (currentMedia === mashup) {
            $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
            if (mashupTimecode > mashup.duration) {
                mashup.setCurrentTime(mashup.duration);
            }
            changeCurrentAnnotation();
            mashup.trigger("enter-annotation",mashup.currentAnnotation);
        }
        
    }
    
    /* Slider */
    
    var timeSlider = $(".Ldt-Slider"),
        timeSliderContainer = $(".Ldt-Slider-Container"),
        slidersRange = 920;
    timeSlider.slider({
        range: "min",
        value: 0,
        min: 0,
        max: slidersRange,
        slide: function(event, ui) {
            if (currentMedia) {
                var t = currentMedia.duration * ui.value / slidersRange;
                currentMedia.setCurrentTime(t);
            }
        }
    });
    
    var timeSliderHandle = timeSlider.find('.ui-slider-handle'),
        timeSliderMaximized = false,
        timeSliderTimeoutId = false,
        timeSliderMinimizedHeight = 4,
        timeSliderMaximizedHeight = 10,
        timeSliderTimeoutDuration = 1500,
        timeTooltip = $(".Ldt-Slider-Time");
    
    timeSliderContainer.css(calculateSliderCss(timeSliderMinimizedHeight));
    timeSliderHandle.css(calculateHandleCss(timeSliderMinimizedHeight));
    
    function timeSliderMouseOver() {
        if (timeSliderTimeoutId) {
            window.clearTimeout(timeSliderTimeoutId);
            timeSliderTimeoutId = false;
        }
        if (!timeSliderMaximized) {
           timeSliderAnimateToHeight(timeSliderMaximizedHeight);
           timeSliderMaximized = true;
        }
    }
    
    function timeSliderMouseOut() {
        timeTooltip.hide();
        if (timeSliderTimeoutId) {
            clearTimeout(timeSliderTimeoutId);
            timeSliderTimeoutId = false;
        }
        timeSliderTimeoutId = setTimeout(function() {
            if (timeSliderMaximized) {
                timeSliderAnimateToHeight(timeSliderMinimizedHeight);
                timeSliderMaximized = false;
            }
            timeSliderTimeoutId = false;
        }, timeSliderTimeoutDuration);
    }
    
    timeSliderContainer
        .mouseover(function() {
            timeTooltip.show();
            timeSliderMouseOver();
        })
        .mouseout(timeSliderMouseOut);
    timeSlider.mousemove(function(_e) {
        if (!currentMedia) {
            return;
        }
        var _x = _e.pageX - timeSlider.offset().left,
            _t = new IriSP.Model.Time(
                Math.max(0, currentMedia.duration * Math.min(1, _x / timeSlider.width()))
            );
        timeTooltip.text(_t.toString()).css("left",_x);
    });
    
    $(".Ldt-Ctrl").mouseover(timeSliderMouseOver).mouseout(timeSliderMouseOut);
    
    function timeSliderAnimateToHeight(_height) {
        timeSliderContainer.stop().animate(
            calculateSliderCss(_height),
            500,
            function() {
                IriSP.jQuery(this).css("overflow","visible");
            });
        timeSliderHandle.stop().animate(
            calculateHandleCss(_height),
            500,
            function() {
                IriSP.jQuery(this).css("overflow","visible");
            });
    }

    function calculateSliderCss(_size) {
        return {
            height: _size + "px",
            "margin-top": (timeSliderMinimizedHeight - _size) + "px"
        };
    }

    function calculateHandleCss(_size) {
        return {
            height: (2 + _size) + "px",
            width: (2 + _size) + "px",
            "margin-left": -Math.ceil(2 + _size / 2) + "px" 
        };
    }
    
    /* Controller Widget */
   
    var volBlock = $(".Ldt-Ctrl-Volume-Control");
    
    $('.Ldt-Ctrl-Sound')
        .click(function() {
            if (currentMedia) {
                currentMedia.setMuted(!currentMedia.getMuted());
            }
        })
        .mouseover(function() {
            volBlock.show();
        })
        .mouseout(function() {
            volBlock.hide();
        });
    volBlock.mouseover(function() {
        volBlock.show();
    }).mouseout(function() {
        volBlock.hide();
    });
    
    var volBar = $(".Ldt-Ctrl-Volume-Bar");
    
    function ctrlVolumeUpdater() {
        if (currentMedia) {
            var _muted = currentMedia.getMuted(),
                _vol = currentMedia.getVolume();
            if (_vol === false) {
                _vol = .5;
            }
            var _soundCtl = $(".Ldt-Ctrl-Sound");
            _soundCtl.removeClass("Ldt-Ctrl-Sound-Mute Ldt-Ctrl-Sound-Half Ldt-Ctrl-Sound-Full");
            if (_muted) {        
                _soundCtl.attr("title", gettext("Unmute"))
                    .addClass("Ldt-Ctrl-Sound-Mute");    
            } else {
                _soundCtl.attr("title", gettext("mute"))
                    .addClass(_vol < .5 ? "Ldt-Ctrl-Sound-Half" : "Ldt-Ctrl-Sound-Full" );
            }
            volBar.slider("value", _muted ? 0 : 100 * _vol);
            volBar.attr("title",gettext("Volume:") + Math.floor(100 * _vol) + '%');
        }
    }
    
    volBar.slider({
        slide: function(event, ui) {
            if (currentMedia) {
                currentMedia.setVolume(ui.value / 100);
            }
        }
    });
    
    $(".Ldt-Ctrl-Play").click(function() {
        if (currentMedia) {
            if (currentMedia.getPaused()) {        
                currentMedia.play();
            } else {
                currentMedia.pause();
            }
        }
    });
    
    /* UI Events */

    function onCurrentMediaPlay() {
        $(".Ldt-Ctrl-Play")
            .attr("title", gettext("Pause"))
            .removeClass("Ldt-Ctrl-Play-PlayState")
            .addClass("Ldt-Ctrl-Play-PauseState");
    }
    
    function onCurrentMediaPause() {
        $(".Ldt-Ctrl-Play")
            .attr("title", gettext("Play"))
            .removeClass("Ldt-Ctrl-Play-PauseState")
            .addClass("Ldt-Ctrl-Play-PlayState");
    }
    
    function onCurrentMediaTimeupdate(_time) {
        $(".Ldt-Ctrl-Time-Elapsed").text(_time.toString());
        timeSlider.slider("value",slidersRange * _time / currentMedia.duration);
    }
    
    /* Mashup Player */
   
    mashup.currentMedia = null;
    mashup.currentAnnotation = null;
    mashup.seeking = false;
    var mashupSegmentBegin,
        mashupSegmentEnd,
        mashupTimecode = 0,
        seekdiv = $(".video-wait"),
        mashupTimedelta;
    
    function showSeek() {
        if (currentMedia.seeking) {
            seekdiv.show();
        }
    }
    
    function changeCurrentAnnotation() {
        if (mashupTimecode >= mashup.duration) {
            if (!mashup.paused) {
                mashup.paused = true;
                mashup.trigger("pause");
            }
            mashupTimecode = 0;
        }
        var _annotation = mashup.getAnnotationAtTime( mashupTimecode );
        if (typeof _annotation === "undefined") {
            if (mashup.currentMedia) {
                mashup.currentMedia.pause();
                if (!mashup.paused) {
                    mashup.paused = true;
                    mashup.trigger("pause");
                }
            }
            return;
        }
        mashup.currentAnnotation = _annotation;
        mashupSegmentBegin = mashup.currentAnnotation.annotation.begin.milliseconds;
        mashupSegmentEnd = mashup.currentAnnotation.annotation.end.milliseconds;
        mashupTimedelta = mashupSegmentBegin - mashup.currentAnnotation.begin.milliseconds;
        mashup.currentMedia = mashup.currentAnnotation.getMedia();
        project.getMedias().forEach(function(_media) {
            if (_media !== mashup.currentMedia) {
                _media.hide();
                _media.pause();
            } else {
                _media.show();
            }
        });
        
        mashup.currentMedia.setCurrentTime( mashupTimecode + mashupTimedelta);
        mashup.currentMedia.seeking = true;
        
        if (!mashup.paused) {
            mashup.currentMedia.play();
            mashup.seeking = true;
            setTimeout(showSeek,200);
        }
        mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));

    }
    
    var can_play_mp4 = !!document.createElement('video').canPlayType('video/mp4');
    
    function addMedia(media) {
        if (media.has_player) {
            return;
        }
        media.has_player = true;
        var videourl = media.video;
        if (typeof IriSP.video_url_transform === "function") {
            videourl = IriSP.video_url_transform(media.video);
        }
        var videoid = "video_" + media.id,
            width = $(".video").width(),
            height = $(".video").height(),
            mp4_file = videourl.replace(/\.webm$/i,'.mp4'),
            webm_file = videourl.replace(/\.mp4$/i,'.webm'),
            videourl = (can_play_mp4 ? mp4_file : webm_file);
        
        media.show = function() {
            
            if (document.getElementById(videoid)) {
                return;
            }
            
            media.loaded = false;
            media.paused = true;
            var videoSelector = $('<video>');
            
            videoSelector.attr({
                id : videoid,
                src : videourl,
                width : width,
                height : height
            }).css({
                position : "absolute",
                left: 0,
                top: 0,
                width : "100%",
                height : "100%"
            });
            
            $(".video").append(videoSelector);
            
            var videoElement = document.getElementById(videoid);
            
            // Binding DOM events to media
            
            function getVolume() {
                media.muted = videoElement.muted;
                media.volume = videoElement.volume;
            }
            
            videoSelector.on("loadedmetadata", function() {
                getVolume();
                media.loaded = true;
                media.trigger("loadedmetadata");
                media.trigger("volumechange");
            });
            
            videoSelector.on("timeupdate", function() {
                media.trigger("timeupdate", new IriSP.Model.Time(1000*videoElement.currentTime));
            });
            
            videoSelector.on("volumechange", function() {
                getVolume();
                media.trigger("volumechange");
            });
            
            videoSelector.on("play", function() {
                media.trigger("play");
            });
            
            videoSelector.on("pause", function() {
                media.trigger("pause");
            });
            
            videoSelector.on("seeking", function() {
                media.trigger("seeking");
            });
            
            videoSelector.on("seeked", function() {
                media.trigger("seeked");
            });
        };
        
        media.hide = function() {
            $("#" + videoid).remove();
        };
        
        // Binding functions to Media Element Functions
                
        media.on("setcurrenttime", function(_milliseconds) {
            try {
                document.getElementById(videoid).currentTime = (_milliseconds / 1000);
            } catch(err) {}
        });
        
        media.on("setvolume", function(_vol) {
            try {
                document.getElementById(videoid).volume = _vol;
                media.volume = _vol;
            } catch(err) {}
        });
        
        media.on("setmuted", function(_muted) {
            try {
                document.getElementById(videoid).muted = _muted;
                media.muted = _muted;
            } catch(err) {}
        });
        
        media.on("setplay", function() {
            try {
                document.getElementById(videoid).play();
            } catch(err) {}
        });

        media.on("setpause", function() {
            try {
                document.getElementById(videoid).pause();
            } catch(err) {}
        });
        
        // Binding UI Events and Mashup Playing to Media
        
        media.on("loadedmetadata", function() {
            if (media === currentMedia) {
                seekdiv.hide();
            }
            if (mashup === currentMedia && media === mashup.currentMedia) {
                changeCurrentAnnotation();
            }
        });
        
        media.on("play", function() {
            if (media === currentMedia) {
                onCurrentMediaPlay();
            }
            if (mashup === currentMedia && media === mashup.currentMedia) {
                mashup.trigger("play");
            }
        });
        
        media.on("pause", function() {
            if (media === currentMedia) {
                onCurrentMediaPause();
            }
            if (mashup === currentMedia && media === mashup.currentMedia) {
                mashup.trigger("pause");
            }
        });
        
        media.on("timeupdate", function(_time) {
            if (media === currentMedia) {
                $(".frise-position").css("left",(100*_time/media.duration)+"%");
                onCurrentMediaTimeupdate(_time);
            }
            if (mashup === currentMedia && !mashup.paused && media === mashup.currentMedia && !media.seeking) {
                if ( _time < mashupSegmentEnd ) {
                    if ( _time >= mashupSegmentBegin ) {
                        mashupTimecode = _time - mashupTimedelta;
                    } else {
                        mashupTimecode = mashupSegmentBegin - mashupTimedelta;
                        media.setCurrentTime(mashupSegmentBegin);
                    }
                } else {
                    mashupTimecode = mashupSegmentEnd - mashupTimedelta;
                    media.pause();
                    changeCurrentAnnotation();
                }
                mashup.trigger("timeupdate", new IriSP.Model.Time(mashupTimecode));
            }
        });
        
        media.on("seeked", function() {
            media.seeking = false;
            if (mashup === currentMedia && media === mashup.currentMedia && mashup.seeking) {
                mashup.seeking = false;
            }
            seekdiv.hide();
        });
        
        media.on("volumechange", function() {
            if (media === currentMedia) {
                ctrlVolumeUpdater();
            }
            mashup.muted = media.muted;
            mashup.volume = media.volume;
            mashup.trigger("volumechange");
        });
        
        project.on("set-current", function(_m) {
            if (_m !== media && (_m !== mashup || mashup.currentMedia !== media)) {
                media.hide();
            }
        });
        
    }

    // Mashup Events
    
    mashup.on("setcurrenttime", function(_milliseconds) {
        mashupTimecode = _milliseconds;
        changeCurrentAnnotation();
    });
    
    mashup.on("setvolume", function(_vol) {
        mashup.getMedias().forEach(function(_media) {
            _media.setVolume(_vol);
        });
        mashup.volume = _vol;
    });
    
    mashup.on("setmuted", function(_muted) {
        mashup.getMedias().forEach(function(_media) {
            _media.setMuted(_muted);
        });
        mashup.muted = _muted;
    });
    
    mashup.on("setplay", function() {
        mashup.paused = false;
        changeCurrentAnnotation();
    });
    
    mashup.on("setpause", function() {
        mashup.paused = true;
        if (mashup.currentMedia) {
            mashup.currentMedia.pause();
        }
    });
    
    mashup.on("loadedmetadata", function() {
        if (mashup === currentMedia) {
            changeCurrentAnnotation();
        }
    });
    
    mashup.on("volumechange", function() {
        if (mashup === currentMedia) {
            ctrlVolumeUpdater();
        }
    });
    /* Mashup Events to UI */
   
    mashup.on("play", function() {
        if (mashup === currentMedia) {
            onCurrentMediaPlay();
        }
    });
    
    mashup.on("pause", function() {
        if (mashup === currentMedia) {
            onCurrentMediaPause();
        }
    });
    
    mashup.on("timeupdate", function(_time) {
        if (mashup === currentMedia) {
            $(".frise-position").css("left",(100*_time/mashup.duration)+"%");
            onCurrentMediaTimeupdate(_time);
        }
    });
    
    mashup.on("add", function() {
        mashup.getMedias().forEach(addMedia);
    });
    
    mashup.on("change",updateMashupUI);
    
    mashup.on("enter-annotation", function(segment) {
        var a = segment.annotation;
        $(".annotation-title").text(a.title);
        $(".annotation-begin").text(a.begin.toString());
        $(".annotation-end").text(a.end.toString());
        $(".annotation-tags").text(a.keywords.join(", "));
        $(".annotation-media-title").text(a.getMedia().title);
        $(".annotation-description").text(a.description);
        var p = (segment.begin + segment.end) / (2 * mashup.duration);
        $(".mashup-description .pointer").css("left", (100 * p) + "%");
        project.trigger("mouseout-annotation");
    });
    
    project.on("mouseover-annotation", function(annotation) {
        $(".annotation").removeClass("active");
        if (!annotation) {
            return;
        }
        var segment = mashup.getAnnotation(annotation);
        $(".annotation[data-segment-id='" + annotation.id + "']").addClass("active");
        $(".mashup-tooltip").show().css({
            left: (100 * ( segment.begin + segment.end ) / ( 2 * mashup.duration ) ) + "%"
        });
        $(".mashup-tooltip .segment-tooltip").text(annotation.title);
    });
    
    project.on("mouseout-annotation", function() {
        if (currentMedia === mashup && mashup.currentAnnotation) {
            $(".annotation").removeClass("active");
            $(".item-video.annotation[data-segment-id='" + mashup.currentAnnotation.annotation.id + "']").addClass("active");
        }
        $(".mashup-tooltip").hide();
    });
    
    project.on("click-annotation", function(annotation) {
        if (!annotation) {
            return;
        }
        var segment = mashup.getAnnotation(annotation);
        project.trigger("set-current", mashup);
        if (segment) {
            mashup.setCurrentTime(segment.begin);
        }
    });
    
    project.on("set-current", function(media) {
        currentMedia = media;
        if (currentMedia.elementType === "media") {
            if (!media.has_player) {
                addMedia(media);
            }
            media.show();
            if (!currentMedia.loaded) {
                seekdiv.show();
            }
        }
        $(".Ldt-Ctrl-Time-Total").text(currentMedia.duration.toString());
        currentMedia.trigger("timeupdate",currentMedia.getCurrentTime());
        currentMedia.trigger("pause");
    });
    
    var mouseoverSegment;
    
    $(".mashup-frise")
    .mousemove(function(evt) {
        if (!mashup.duration.milliseconds) {
            return;
        }
        var el = $(this), t = ( evt.pageX - el.offset().left ) * mashup.duration / el.width(), segment = mashup.getAnnotationAtTime(t);
        if (segment) {
            if (segment !== mouseoverSegment) {
                project.trigger("mouseover-annotation", segment.annotation);
            }
        } else {
            if (mouseoverSegment) {
                project.trigger("mouseout-annotation");
            }
        }
        mouseoverSegment = segment;
    })
    .mouseleave(function() {
        project.trigger("mouseout-annotation");
        mouseoverSegment = undefined;
    });
    
    mashup.trigger("add");
    
};

/* END mashupcore.js */

