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

/*@
Ox.BlockTimeline <f:Ox.Element> BlockTimeline Object
    () ->              <f> BlockTimeline Object
    (options) ->       <f> BlockTimeline Object
    (options, self) -> <f> BlockTimeline Object
    options <o> Options object
    self    <o> shared private variable
@*/

Ox.BlockTimeline = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
            .defaults({
                duration: 0,
                find: '',
                matches: [],
                points: [0, 0],
                position: 0,
                subtitles: [],
                videoId: '',
                width: 0
            })
            .options(options || {})
            .addClass('OxTimelineSmall')
            .mousedown(mousedown)
            .mouseleave(mouseleave)
            .mousemove(mousemove)
            .bindEvent({
                drag: function(data) {
                    mousedown(data);
                }
            });

    Ox.extend(self, {
        $images: [],
        $lines: [],
        $markerPoint: [],
        $selection: [],
        $subtitles: [],
        $tooltip: Ox.Tooltip({
            animate: false
        }).css({
            textAlign: 'center'
        }),
        hasSubtitles: self.options.subtitles.length,
        height: 16,
        lines: Math.ceil(self.options.duration / self.options.width),
        margin: 8
    });

	that.css({
		width: (self.options.width + self.margin) + 'px',
		height: ((self.height + self.margin) * self.lines + 4) + 'px'
	});

    getTimelineImageURL(function(url) {

        self.timelineImageURL = url;

        Ox.range(0, self.lines).forEach(function(i) {
            addLine(i);
        });

        self.$markerPosition = $('<img>')
            .addClass('OxMarkerPosition')
            .attr({
                src: Ox.UI.PATH + 'png/videoMarkerPlay.png'
            })
            .css({
                position: 'absolute',
                width: '9px',
                height: '5px',
                zIndex: 10
            })
            .appendTo(that.$element);

        setPosition();

        ['in', 'out'].forEach(function(v, i) {
            var titleCase = Ox.toTitleCase(v);
            self.$markerPoint[i] = $('<img>')
                .addClass('OxMarkerPoint' + titleCase)
                .attr({
                    src: Ox.UI.PATH + 'png/videoMarker' + titleCase + '.png'
                })
                .appendTo(that.$element);
            setMarkerPoint(i);
        });

    });

    function addLine(i) {
        // fixme: get URLs once, not once for every line
        self.$lines[i] = Ox.Element()
            .css({
                top: i * (self.height + self.margin) + 'px',
                width: self.options.width + 'px'
            })
            .appendTo(that);
        self.$images[i] = $('<img>')
            .addClass('OxTimelineSmallImage')
            .attr({
                src: self.timelineImageURL
            })
            .css({
                marginLeft: (-i * self.options.width) + 'px'
            })
            .appendTo(self.$lines[i].$element);
        if (self.hasSubtitles) {
            self.subtitlesImageURL = getSubtitlesImageURL();
            self.$subtitles[i] = $('<img>')
                .addClass('OxTimelineSmallSubtitles')
                .attr({
                    src: self.subtitlesImageURL
                })
                .css({
                    marginLeft: (-i * self.options.width) + 'px'
                })
                .appendTo(self.$lines[i].$element);
        }
        if (self.options.points[0] != self.options.points[1]) {
            addSelection(i);
        }
    }

    function addSelection(i) {
        self.selectionImageURL = getSelectionImageURL();
        self.$selection[i] && self.$selection[i].remove();
        self.$selection[i] = $('<img>')
            .addClass('OxTimelineSmallSelection')
            .attr({
                src: self.selectionImageURL
            })
            .css({
                marginLeft: (-i * self.options.width) + 'px'
            })
            .appendTo(self.$lines[i].$element);
    }

    function getPosition(e) {
        //FIXME: this might still be broken in opera according to http://acko.net/blog/mouse-handling-and-absolute-positions-in-javascript
        return (e.offsetX ? e.offsetX : e.clientX - $(e.target).offset().left);
    }

    function getSelectionImageURL() {
        var height = 18,
            width = Math.ceil(self.options.duration),
            $canvas = $('<canvas>')
                .attr({
                    height: height,
                    width: width
                }),
            canvas = $canvas[0],
            context = canvas.getContext('2d'),
            imageData = context.createImageData(width, height),
            data = imageData.data,
            points = self.options.points.map(function(v, i) {
                return Math.round(v) + i;
            }),
            top = 0,
            bottom = 18;
        Ox.range(points[0], points[1]).forEach(function(x) {
            Ox.range(top, bottom).forEach(function(y) {
                var color = (y == top || y == bottom - 1) ? [255, 255, 255, 255] : [255, 255, 255, 64],
                    index = x * 4 + y * 4 * width;
                data[index] = color[0];
                data[index + 1] = color[1];
                data[index + 2] = color[2];
                data[index + 3] = color[3];
            });
        });
        context.putImageData(imageData, 0, 0);
        return canvas.toDataURL();
    }

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

    function getSubtitlesImageURL() {
        var height = 18,
            width = Math.ceil(self.options.duration),
            $canvas = $('<canvas>')
                .attr({
                    height: height,
                    width: width
                }),
            canvas = $canvas[0],
            context = canvas.getContext('2d'),
            imageData = context.createImageData(width, height),
            data = imageData.data;
        self.options.subtitles.forEach(function(v) {
            //var color = self.options.matches.indexOf(i) > -1 ? [255, 255, 0] : [255, 255, 255]
            var inPoint = Math.round(v['in']),
                outPoint = Math.round(v.out) + 1,
                lines = v.value.split('\n').length,
                bottom = 15,
                top = bottom - lines - 2;
            Ox.range(inPoint, outPoint).forEach(function(x) {
                Ox.range(top, bottom).forEach(function(y) {
                    var color = (y == top || y == bottom - 1) ? [0, 0, 0] : [255, 255, 255],
                        index = x * 4 + y * 4 * width;
                    data[index] = color[0];
                    data[index + 1] = color[1];
                    data[index + 2] = color[2];
                    data[index + 3] = 128;
                });
            });
        });
        context.putImageData(imageData, 0, 0);
        return canvas.toDataURL();
    }

    function getTimelineImageURL(callback) {
        var height = 16,
            images = Math.ceil(self.options.duration / 3600),
            loaded = 0,
            width = Math.ceil(self.options.duration),
            $canvas = $('<canvas>')
                .attr({
                    height: height,
                    width: width
                }),
            canvas = $canvas[0],
            context = canvas.getContext('2d');
        Ox.range(images).forEach(function(i) {
            var $img = $('<img>')
                .attr({
                    src: '/' + self.options.videoId + '/timelines/timeline.16.' + i + '.png'
                })
                .load(function() {
                    context.drawImage($img[0], i * 3600, 0);
                    //Ox.Log('Video', 'loaded, images', loaded, images, $img[0])
                    if (++loaded == images) {
                        //Ox.Log('Video', 'callback', canvas.toDataURL().length)
                        callback(canvas.toDataURL());
                    }
                });
        });
    }

    function mousedown(e) {
        var $target = $(e.target);
        if (
            $target.hasClass('OxTimelineSmallImage') ||
            $target.hasClass('OxTimelineSmallSubtitles') ||
            $target.hasClass('OxTimelineSmallSelection')
        ) {
            self.options.position = getPosition(e);
            setPosition();
            that.triggerEvent('change', {
                position: self.options.position
            });
        }
        e.preventDefault();
    }

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

    function mousemove(e) {
        var $target = $(e.target),
            position,
            subtitle;
        if (
            $target.hasClass('OxTimelineSmallImage') ||
            $target.hasClass('OxTimelineSmallSubtitles') ||
            $target.hasClass('OxTimelineSmallSelection')
        ) {
            position = getPosition(e);
            subtitle = getSubtitle(position);
            self.$tooltip.options({
                    title: subtitle ?
                        '<span class=\'OxBright\'>' +
                        Ox.highlight(subtitle.value, self.options.find, 'OxHighlight').replace(/\n/g, '<br/>') +
                        '</span><br/>' +
                        Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) :
                        Ox.formatDuration(position, 3)
                })
                .show(e.clientX, e.clientY);
        } else {
            self.$tooltip.hide();
        }

    }

    function setMarker() {
        self.$markerPosition
            .css({
                left: (self.options.position % self.options.width) + 'px',
                top: (parseInt(self.options.position / self.options.width) * (self.height + self.margin) + 2) + 'px',
            });
    }

    function setMarkerPoint(i) {
        var position = Math.round(self.options.points[i]);
        self.$markerPoint[i]
            .css({
                left: (position % self.options.width) + 'px',
                top: (parseInt(position / self.options.width) * (self.height + self.margin) + 16) + 'px',
            });
    }

    function setPosition() {
        self.options.position = Ox.limit(self.options.position, 0, self.options.duration);
        setMarker();
    }

    function setWidth() {
        self.lines = Math.ceil(self.options.duration / self.options.width);
		that.css({
			width: (self.options.width + self.margin) + 'px',
			height: ((self.height + self.margin) * self.lines + 4) + 'px'
		});
        Ox.range(self.lines).forEach(function(i) {
            if (self.$lines[i]) {
                self.$lines[i].css({
                    width: self.options.width + 'px'
                });
                self.$images[i].css({
                    marginLeft: (-i * self.options.width) + 'px'
                });
                if (self.hasSubtitles) {
                    self.$subtitles[i].css({
                        marginLeft: (-i * self.options.width) + 'px'
                    });
                }
            } else {
                addLine(i);
            }
        });
        while (self.$lines.length > self.lines) {
            self.$lines[self.$lines.length - 1].removeElement();
            self.$lines.pop();
        }
        setMarker();
        setMarkerPoint(0);
        setMarkerPoint(1);
    }

    function updateSelection() {
        self.$lines.forEach(function($line, i) {
            addSelection(i);
        });
    }

    self.setOption = function(key, value) {
        //Ox.Log('Video', 'onChange:', key, value)
        if (key == 'points') {
            //Ox.Log('Video', 'key', key, 'value', value)
            setMarkerPoint(0);
            setMarkerPoint(1);
            updateSelection();
        } else if (key == 'position') {
            setPosition();
        } else if (key == 'width') {
            setWidth();
        }
    };

    return that;

};
