// vim: et:ts=4:sw=4:sts=4:ft=javascript

'use strict';

Ox.VideoTimelinePlayer = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
        .defaults({
            cuts: [],
            duration: 0,
            find: '',
            getFrameURL: null,
            getImageURL: null,
            height: 0,
            'in': 0,
            matches: [],
            out: 0,
            paused: false,
            position: 0,
            subtitles: [],
            timelineURL: '',
            videoRatio: 1,
            width: 0
        })
        .options(options || {})
        .addClass('OxVideoTimelinePlayer');

    self.fps = 25;
    self.frame = self.options.position * self.fps;
    self.frames = self.options.duration * self.fps;
    self.tileWidth = 1500;
    self.tileHeight = 64;
    self.margin = 8;
    self.contentWidth = self.options.width - 2 * self.margin - Ox.UI.SCROLLBAR_SIZE;
    self.contentHeight = self.options.height - 32;
    self.tiles = Math.ceil(self.frames / self.tileWidth);
    self.videoWidth = Math.round(self.tileHeight * self.options.videoRatio);
    self.videoLines = getVideoLines();
    self.lines = getLines();

    self.$topBar = Ox.Bar({size: 16});

    self.$content = Ox.Element()
        .addClass('OxVideoTimelinePlayer')
        .css({
            overflow: 'hidden',
            overflowY: 'scroll'
        })
        .bind({
            mousedown: mousedown,
            mouseleave: mouseleave,
            mousemove: mousemove
        })
        .bindEvent({
            mousedown: function() {
                this.gainFocus();
            },
            key_down: function() {
                self.options.position += self.contentWidth / self.fps;
                setPosition();
            },
            key_enter: function() {
                scrollToPosition();
            },
            key_left: function() {
                self.options.position -= self.videoWidth / self.fps;
                setPosition();
            },
            key_right: function() {
                self.options.position += self.videoWidth / self.fps;
                setPosition();
            },
            key_shift_left: function() {
                self.options.position -= 1 / self.fps;
                setPosition();
            },
            key_shift_right: function() {
                self.options.position += 1 / self.fps;
                setPosition();
            },
            key_space: function() {
                togglePaused()
            },
            key_up: function() {
                self.options.position -= self.contentWidth / self.fps;
                setPosition();
            }
            
        });

    self.$bottomBar = Ox.Bar({size: 16});
    self.$playButton = Ox.Button({
            style: 'symbol',
            title: 'play',
            type: 'image'
        })
        .css({
            float: 'left'
        })
        .bindEvent({
            click: function() {
                togglePaused();
            }
        })
        .appendTo(self.$bottomBar);
    self.$muteButton = Ox.Button({
            style: 'symbol',
            title: 'mute',
            type: 'image'
        })
        .css({
            float: 'left'
        })
        .bindEvent({

        })
        .appendTo(self.$bottomBar);
    self.$smallTimeline = $('<div>')
        .css({
            float: 'left',
            width: self.options.width - 80 + 'px',
            height: '16px',
            borderRadius: '8px',
            background: 'rgb(0, 0, 0)'
        })
        .appendTo(self.$bottomBar);
    self.$smallTimelineImage = $('<img>')
        .attr({
            src: self.options.timelineURL
        })
        .css({
            width: self.options.width - 96 + 'px',
            height: '16px',
            margin: '0 8px 0 8px'
        })
        .appendTo(self.$smallTimeline);
    self.$position = $('<div>')
        .css({
            float: 'left',
            width: '40px',
            height: '12px',
            padding: '2px 4px 2px 4px',
            fontSize: '9px'
        })
        .html(Ox.formatDuration(self.options.position))
        .appendTo(self.$bottomBar);

    self.$panel = Ox.SplitPanel({
        elements: [
            {
                element: self.$topBar,
                size: 16
            },
            {
                element: self.$content
            },
            {
                element: self.$bottomBar,
                size: 16
            }
        ],
        orientation: 'vertical'
    });

    that.setElement(self.$panel);

    self.$lines = [];
    self.$timelines = [];
    self.$interfaces = [];

    self.$timeline = renderTimeline();
    //setSubtitles();
    Ox.loop(self.lines, function(i) {
        addLine(i);
    });

    self.$frameBox = $('<div>')
        .css({
            position: 'absolute',
            right: 0,
            top: self.margin / 2 - 1 + 'px',
            width: self.videoWidth + 'px',
            height: self.tileHeight + 'px',
            borderTop: '1px solid rgba(255, 255, 255, 0.5)',
            borderBottom: '1px solid rgba(255, 255, 255, 0.5)',
            background: 'rgb(0, 0, 0)'
        })
        .appendTo(self.$timelines[self.videoLines[1]][0]);
    self.$frame = $('<img>')
        .attr({
            src: self.options.getFrameURL(self.options.position)
        })
        .css({
            width: self.videoWidth + 'px',
            height: self.tileHeight + 'px',
        })
        .appendTo(self.$frameBox)
    $('<div>')
        .addClass('OxFrameInterface')
        .css({
            position: 'absolute',
            left: 0,
            top: 0,
            width: self.videoWidth + 'px',
            height: self.tileHeight + 'px',
        })
        .appendTo(self.$frameBox);

    self.$videoBox = $('<div>')
        .css({
            position: 'absolute',
            right: 0,
            top: self.margin / 2 - 1 + 'px',
            width: self.videoWidth + 'px',
            height: self.tileHeight + 'px',
            borderTop: '1px solid rgba(255, 255, 255, 0.5)',
            borderBottom: '1px solid rgba(255, 255, 255, 0.5)',
            background: 'rgb(0, 0, 0)',
            zIndex: 5
        })
        .appendTo(self.$timelines[self.videoLines[0]][0]);
    self.$video = Ox.VideoPlayer({
            duration: self.options.duration,
            height: self.tileHeight,
            paused: self.options.paused,
            position: self.options.position,
            scaleToFill: true,
            video: self.options.videoURL,
            width: self.videoWidth
        })
        .bindEvent({
            ended: function() {
                togglePaused(true);
            },
            playing: function(data) {
                self.options.position = data.position;
                setPosition(true);
            }
        })
        .appendTo(self.$videoBox);
    $('<div>')
        .addClass('OxFrameInterface OxVideoInterface')
        .css({
            position: 'absolute',
            left: 0,
            top: 0,
            width: self.videoWidth + 'px',
            height: self.tileHeight + 'px',
        })
        .appendTo(self.$videoBox);

    self.$tooltip = Ox.Tooltip({
            animate: false
        })
        .css({
            textAlign: 'center'
        });

    setTimeout(function() {
        scrollToPosition();
    });

    function addLine(i) {
        self.$lines[i] = $('<div>')
            .css({
                position: 'absolute',
                left: self.margin + 'px',
                top: self.margin / 2 + i * (self.tileHeight + self.margin) + 'px',
                width: self.contentWidth + 'px',
                height: self.tileHeight + self.margin + 'px',
                overflowX: 'hidden'
            })
            .appendTo(self.$content);
        self.$timelines[i] = [
            self.$timeline.clone()
                .css({
                    width: self.frame + self.videoWidth + 'px',
                    marginLeft: -i * self.contentWidth + 'px',
                }),
            self.$timeline.clone()
                .css({
                    marginLeft: -i * self.contentWidth + self.videoWidth - 1 + 'px',
                })
        ];
        self.$lines[i]
            .append(self.$timelines[i][1])
            .append(self.$timelines[i][0]);
        
    }

    function getLines() {
        return Math.ceil((self.frames - 1 + self.videoWidth) / self.contentWidth)
    }

    function getPosition(e) {
        return (
            e.offsetX ? e.offsetX
            : e.clientX - $(e.target).offset().left
        ) / self.fps;
    }

    function getSubtitle(position) {
        var subtitle = '';
        Ox.forEach(self.options.subtitles, function(v) {
            if (v['in'] <= position && v.out > position) {
                subtitle = v;
                return false;
            }
        });
        return subtitle;
    }

    function getVideoLine() {
        self.videoLine = Math.floor(getVideoFrame() / self.contentWidth);
    }

    function getVideoLines() {
        var videoFrame = getVideoFrame(),
            videoLeft = videoFrame % self.contentWidth,
            lines = [];
        lines[0] = Math.floor(videoFrame / self.contentWidth);
        lines[1] = lines[0] + (
            videoLeft + self.videoWidth > self.contentWidth ? 1 : 0
        )
        if (videoLeft + Math.floor(self.videoWidth / 2) > self.contentWidth) {
            lines.reverse();
        }
        return lines;
    }

    function getVideoFrame() {
        return Math.floor(self.options.position * self.fps);
    }

    function mousedown(e) {
        var $target = $(e.target),
            isTimeline = $target.is('.OxTimelineInterface'),
            isVideo = $target.is('.OxFrameInterface');
        if (isTimeline) {
            self.options.position = getPosition(e);
            setPosition();
            if (!self.triggered) {
                that.triggerEvent('position', {
                    position: self.options.position
                });
                self.triggered = true;
                setTimeout(function() {
                    self.triggered = false;
                }, 250);
            }
        } else if (isVideo) {
            togglePaused();
        }
    }

    function mouseleave() {
        self.$tooltip.hide();
    }

    function mousemove(e) {
        var $target = $(e.target),
            isTimeline = $target.is('.OxTimelineInterface'),
            isVideo = $target.is('.OxFrameInterface'),
            position, subtitle;
        if (isTimeline || isVideo) {
            position = isTimeline ? getPosition(e) : self.options.position;
            subtitle = getSubtitle(position);
            self.$tooltip.options({
                    title: (
                        subtitle
                            ? '<span class=\'OxBright\'>' +
                            Ox.highlight(subtitle.text, self.options.find, 'OxHighlight').replace(/\n/g, '<br/>') +
                            '</span><br/>'
                            : ''
                        ) + Ox.formatDuration(position, 3)
                })
                .show(e.clientX, e.clientY);
        } else {
            self.$tooltip.hide();
        }
    }

    function renderTimeline() {
        var $timeline = $('<div>')
            .css({
                position: 'absolute',
                width: self.frames + 'px',
                height: self.tileHeight + self.margin + 'px',
                overflow: 'hidden'
            });
            Ox.loop(self.tiles, function(i) {
                $('<img>')
                    .attr({
                        src: self.options.getImageURL(i)
                    })
                    .css({
                        position: 'absolute',
                        left: i * self.tileWidth + 'px',
                        top: self.margin / 2 + 'px'
                    })
                    .appendTo($timeline);
            });
            $('<div>')
                .addClass('OxTimelineInterface')
                .css({
                    position: 'absolute',
                    left: 0,
                    top: self.margin / 2 + 'px',
                    width: self.frames + 'px',
                    height: self.tileHeight + 'px',
                    //background: 'rgba(255, 0, 0, 0.5)'
                })
                .appendTo($timeline);
            
        return $timeline;
    }

    function scrollToPosition() {
        var scrollTop = self.$content.scrollTop(),
            videoTop = [
                self.margin + Ox.min(self.videoLines) * (self.tileHeight + self.margin),
                self.margin + Ox.max(self.videoLines) * (self.tileHeight + self.margin)
            ],
            offset = self.contentHeight - self.tileHeight - self.margin;
        if (videoTop[0] < scrollTop + self.margin) {
            self.$content.scrollTop(videoTop[0] - self.margin);
        } else if (videoTop[1] > scrollTop + offset) {
            self.$content.scrollTop(videoTop[1] - offset);
        }
    }

    function setPosition(fromVideo) {
        var max, min, videoLines, isPlaying = !self.options.paused;
        self.options.position = Ox.limit(self.options.position, 0, self.options.duration);
        self.frame = Math.floor(self.options.position * self.fps);
        videoLines = getVideoLines();
        min = Ox.min(Ox.flatten([self.videoLines, videoLines]));
        max = Ox.max(Ox.flatten([self.videoLines, videoLines]));
        Ox.loop(min, max + 1, function(i) {
            self.$timelines[i][0].css({
                width: self.frame + self.videoWidth + 'px'
            });
        });
        if (videoLines[1] != self.videoLines[1]) {
            self.videoLines[1] = videoLines[1];
            self.$frameBox.detach().appendTo(self.$timelines[videoLines[1]][0]);
        }
        if (videoLines[0] != self.videoLines[0]) {
            self.videoLines[0] = videoLines[0];
            isPlaying && self.$video.togglePaused();
            self.$videoBox.detach().appendTo(self.$timelines[videoLines[0]][0]);
            isPlaying && self.$video.togglePaused();
        }
        if (videoLines[0] != videoLines[1]) {
            self.$frame.attr({
                src: self.options.getFrameURL(
                    self.paused
                        ? self.options.position
                        : Math.floor(self.options.position)
                )
            });
        }
        self.videoLines = videoLines;
        if (!fromVideo) {
            self.$video.options({position: self.options.position});
            self.$frame.attr({
                src: self.options.getFrameURL(self.options.position)
            });            
            scrollToPosition();
        }
        self.$position.html(Ox.formatDuration(self.options.position))
    }

    function setSubtitles() {
        self.$timeline.find('.OxSubtitle').remove();
        self.$subtitles = [];
        self.options.subtitles.forEach(function(subtitle, i) {
            var found = self.options.find
                && subtitle.text.toLowerCase().indexOf(self.options.find.toLowerCase()) > -1;
            self.$subtitles[i] = $('<div>')
                .addClass('OxSubtitle' + (found ? ' OxHighlight' : ''))
                .css({
                    position: 'absolute',
                    left: (subtitle['in'] * self.fps) + 'px',
                    width: (((subtitle.out - subtitle['in']) * self.fps) - 2) + 'px'
                })
                .html(Ox.highlight(subtitle.text, self.options.find, 'OxHighlight'))
                .appendTo(self.$timeline);
        });
    }

    function setWidth() {
        self.contentWidth = self.options.width - 2 * self.margin - Ox.UI.SCROLLBAR_SIZE;
        self.lines = getLines();
		Ox.loop(self.lines, function(i) {
            if (self.$lines[i]) {
                self.$lines[i].css({
                    width: self.contentWidth + 'px'
                });
                self.$timelines[i][0].css({
                    marginLeft: -i * self.contentWidth + 'px'
                });
                self.$timelines[i][1].css({
                    marginLeft: -i * self.contentWidth + self.videoWidth + 'px'
                });
            } else {
                addLine(i);
            }
        });
        while (self.$lines.length > self.lines) {
            self.$lines[self.$lines.length - 1].remove();
            self.$lines.pop();
            self.$timelines.pop();
        }
    }

    function togglePaused(fromVideo) {
        self.options.paused = !self.options.paused;
        !fromVideo && self.$video.options({
            paused: self.options.paused
        });
        self.$playButton.options({
            title: !self.options.paused ? 'pause' : 'play'
        });
    }

    self.setOption = function(key, value) {
        if (key == 'width') {
            setWidth();
        }
    };

    return that;

};