(function ($) {

    // add extra default options
    $.extend(mejs.MepDefaults, {
        // this will automatically turn on a <track>
        startLanguage: '',

        tracksText: mejs.i18n.t('Captions/Subtitles'),

        // By default, no WAI-ARIA live region - don't make a
        // screen reader speak captions over an audio track.
        tracksAriaLive: false,

        // option to remove the [cc] button when no <track kind="subtitles"> are present
        hideCaptionsButtonWhenEmpty: true,

        // If true and we only have one track, change captions to popup
        toggleCaptionsButtonWhenOnlyOne: false,

        // #id or .class
        slidesSelector: ''
    });

    $.extend(MediaElementPlayer.prototype, {

        hasChapters: false,

        cleartracks: function (player, controls, layers, media) {
            if (player) {
                if (player.captions) player.captions.remove();
                if (player.chapters) player.chapters.remove();
                if (player.captionsText) player.captionsText.remove();
                if (player.captionsButton) player.captionsButton.remove();
            }
        },
        buildtracks: function (player, controls, layers, media) {
            if (player.tracks.length === 0)
                return;

            var t = this,
                attr = t.options.tracksAriaLive ?
                    'role="log" aria-live="assertive" aria-atomic="false"' : '',
                i;

            if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
                for (i = t.domNode.textTracks.length - 1; i >= 0; i--) {
                    t.domNode.textTracks[i].mode = "hidden";
                }
            }
            t.cleartracks(player, controls, layers, media);
            player.chapters =
                $('<div class="mejs-chapters mejs-layer"></div>')
                    .prependTo(layers).hide();
            player.captions =
                $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover" ' +
                    attr + '><span class="mejs-captions-text"></span></div></div>')
                    .prependTo(layers).hide();
            player.captionsText = player.captions.find('.mejs-captions-text');
            player.captionsButton =
                $('<div class="mejs-button mejs-captions-button">' +
                    '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>' +
                    '<div class="mejs-captions-selector">' +
                    '<ul>' +
                    '<li>' +
                    '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
                    '<label for="' + player.id + '_captions_none">' + mejs.i18n.t('None') + '</label>' +
                    '</li>' +
                    '</ul>' +
                    '</div>' +
                    '</div>')
                    .appendTo(controls);


            var subtitleCount = 0;
            for (i = 0; i < player.tracks.length; i++) {
                if (player.tracks[i].kind == 'subtitles') {
                    subtitleCount++;
                }
            }

            // if only one language then just make the button a toggle
            if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1) {
                // click
                player.captionsButton.on('click', function () {
                    if (player.selectedTrack === null) {
                        lang = player.tracks[0].srclang;
                    } else {
                        lang = 'none';
                    }
                    player.setTrack(lang);
                });
            } else {
                // hover or keyboard focus
                player.captionsButton.on('mouseenter focusin', function () {
                    $(this).find('.mejs-captions-selector').removeClass('mejs-offscreen');
                })

                // handle clicks to the language radio buttons
                    .on('click', 'input[type=radio]', function () {
                        lang = this.value;
                        player.setTrack(lang);
                    });

                player.captionsButton.on('mouseleave focusout', function () {
                    $(this).find(".mejs-captions-selector").addClass("mejs-offscreen");
                });

            }

            if (!player.options.alwaysShowControls) {
                // move with controls
                player.container
                    .bind('controlsshown', function () {
                        // push captions above controls
                        player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');

                    })
                    .bind('controlshidden', function () {
                        if (!media.paused) {
                            // move back to normal place
                            player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
                        }
                    });
            } else {
                player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
            }

            player.trackToLoad = -1;
            player.selectedTrack = null;
            player.isLoadingTrack = false;

            // add to list
            for (i = 0; i < player.tracks.length; i++) {
                if (player.tracks[i].kind == 'subtitles') {
                    player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
                }
            }

            // start loading tracks
            player.loadNextTrack();

            media.addEventListener('timeupdate', function (e) {
                player.displayCaptions();
            }, false);

            if (player.options.slidesSelector !== '') {
                player.slidesContainer = $(player.options.slidesSelector);

                media.addEventListener('timeupdate', function (e) {
                    player.displaySlides();
                }, false);

            }

            media.addEventListener('loadedmetadata', function (e) {
                player.displayChapters();
            }, false);

            player.container.hover(
                function () {
                    // chapters
                    if (player.hasChapters) {
                        player.chapters.removeClass('mejs-offscreen');
                        player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
                    }
                },
                function () {
                    if (player.hasChapters && !media.paused) {
                        player.chapters.fadeOut(200, function () {
                            $(this).addClass('mejs-offscreen');
                            $(this).css('display', 'block');
                        });
                    }
                });

            t.container.on('controlsresize', function () {
                t.adjustLanguageBox();
            });

            // check for autoplay
            if (player.node.getAttribute('autoplay') !== null) {
                player.chapters.addClass('mejs-offscreen');
            }
        },

        setTrack: function (lang) {

            var t = this,
                i;

            if (lang == 'none') {
                t.selectedTrack = null;
                t.captionsButton.removeClass('mejs-captions-enabled');
            } else {
                for (i = 0; i < t.tracks.length; i++) {
                    if (t.tracks[i].srclang == lang) {
                        if (t.selectedTrack === null)
                            t.captionsButton.addClass('mejs-captions-enabled');
                        t.selectedTrack = t.tracks[i];
                        t.captions.attr('lang', t.selectedTrack.srclang);
                        t.displayCaptions();
                        break;
                    }
                }
            }
        },

        loadNextTrack: function () {
            var t = this;

            t.trackToLoad++;
            if (t.trackToLoad < t.tracks.length) {
                t.isLoadingTrack = true;
                t.loadTrack(t.trackToLoad);
            } else {
                // add done?
                t.isLoadingTrack = false;

                t.checkForTracks();
            }
        },

        loadTrack: function (index) {
            var
                t = this,
                track = t.tracks[index],
                after = function () {

                    track.isLoaded = true;

                    t.enableTrackButton(track.srclang, track.label);

                    t.loadNextTrack();

                };


            $.ajax({
                url: track.src,
                dataType: "text",
                success: function (d) {

                    // parse the loaded file
                    if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
                        track.entries = mejs.TrackFormatParser.dfxp.parse(d);
                    } else {
                        track.entries = mejs.TrackFormatParser.webvtt.parse(d);
                    }

                    after();

                    if (track.kind == 'chapters') {
                        t.media.addEventListener('play', function (e) {
                            if (t.media.duration > 0) {
                                t.displayChapters(track);
                            }
                        }, false);
                    }

                    if (track.kind == 'slides') {
                        t.setupSlides(track);
                    }
                },
                error: function () {
                    t.removeTrackButton(track.srclang);
                    t.loadNextTrack();
                }
            });
        },

        enableTrackButton: function (lang, label) {
            var t = this;

            if (label === '') {
                label = mejs.language.codes[lang] || lang;
            }

            t.captionsButton
                .find('input[value=' + lang + ']')
                .prop('disabled', false)
                .siblings('label')
                .html(label);

            // auto select
            if (t.options.startLanguage == lang) {
                $('#' + t.id + '_captions_' + lang).prop('checked', true).trigger('click');
            }

            t.adjustLanguageBox();
        },

        removeTrackButton: function (lang) {
            var t = this;

            t.captionsButton.find('input[value=' + lang + ']').closest('li').remove();

            t.adjustLanguageBox();
        },

        addTrackButton: function (lang, label) {
            var t = this;
            if (label === '') {
                label = mejs.language.codes[lang] || lang;
            }

            t.captionsButton.find('ul').append(
                $('<li>' +
                    '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
                    '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>' +
                    '</li>')
            );

            t.adjustLanguageBox();

            // remove this from the dropdownlist (if it exists)
            t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
        },

        adjustLanguageBox: function () {
            var t = this;
            // adjust the size of the outer box
            t.captionsButton.find('.mejs-captions-selector').height(
                t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
                t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
            );
        },

        checkForTracks: function () {
            var
                t = this,
                hasSubtitles = false;

            // check if any subtitles
            if (t.options.hideCaptionsButtonWhenEmpty) {
                for (i = 0; i < t.tracks.length; i++) {
                    if (t.tracks[i].kind == 'subtitles' && t.tracks[i].isLoaded) {
                        hasSubtitles = true;
                        break;
                    }
                }

                if (!hasSubtitles) {
                    t.captionsButton.hide();
                    t.setControlsSize();
                }
            }
        },

        displayCaptions: function () {

            if (typeof this.tracks == 'undefined')
                return;

            var
                t = this,
                i,
                track = t.selectedTrack;

            if (track !== null && track.isLoaded) {
                for (i = 0; i < track.entries.times.length; i++) {
                    if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop) {
                        // Set the line before the timecode as a class so the cue can be targeted if needed
                        t.captionsText.html(track.entries.text[i]).attr('class', 'mejs-captions-text ' + (track.entries.times[i].identifier || ''));
                        t.captions.show().height(0);
                        return; // exit out if one is visible;
                    }
                }
                t.captions.hide();
            } else {
                t.captions.hide();
            }
        },

        setupSlides: function (track) {
            var t = this;

            t.slides = track;
            t.slides.entries.imgs = [t.slides.entries.text.length];
            t.showSlide(0);

        },

        showSlide: function (index) {
            if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
                return;
            }

            var t = this,
                url = t.slides.entries.text[index],
                img = t.slides.entries.imgs[index];

            if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {

                t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
                    .on('load', function () {
                        img.appendTo(t.slidesContainer)
                            .hide()
                            .fadeIn()
                            .siblings(':visible')
                            .fadeOut();

                    });

            } else {

                if (!img.is(':visible') && !img.is(':animated')) {

                    //console.log('showing existing slide');

                    img.fadeIn()
                        .siblings(':visible')
                        .fadeOut();
                }
            }

        },

        displaySlides: function () {

            if (typeof this.slides == 'undefined')
                return;

            var
                t = this,
                slides = t.slides,
                i;

            for (i = 0; i < slides.entries.times.length; i++) {
                if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop) {

                    t.showSlide(i);

                    return; // exit out if one is visible;
                }
            }
        },

        displayChapters: function () {
            var
                t = this,
                i;

            for (i = 0; i < t.tracks.length; i++) {
                if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
                    t.drawChapters(t.tracks[i]);
                    t.hasChapters = true;
                    break;
                }
            }
        },

        drawChapters: function (chapters) {
            var
                t = this,
                i,
                dur,
                //width,
                //left,
                percent = 0,
                usedPercent = 0;

            t.chapters.empty();

            for (i = 0; i < chapters.entries.times.length; i++) {
                dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
                percent = Math.floor(dur / t.media.duration * 100);
                if (percent + usedPercent > 100 || // too large
                    i == chapters.entries.times.length - 1 && percent + usedPercent < 100) // not going to fill it in
                {
                    percent = 100 - usedPercent;
                }
                //width = Math.floor(t.width * dur / t.media.duration);
                //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
                //if (left + width > t.width) {
                //	width = t.width - left;
                //}

                t.chapters.append($(
                    '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
                    '<div class="mejs-chapter-block' + ((i == chapters.entries.times.length - 1) ? ' mejs-chapter-block-last' : '') + '">' +
                    '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
                    '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start, t.options) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop, t.options) + '</span>' +
                    '</div>' +
                    '</div>'));
                usedPercent += percent;
            }

            t.chapters.find('div.mejs-chapter').click(function () {
                t.media.setCurrentTime(parseFloat($(this).attr('rel')));
                if (t.media.paused) {
                    t.media.play();
                }
            });

            t.chapters.show();
        }
    });


    mejs.language = {
        codes: {
            af: 'Afrikaans',
            sq: 'Albanian',
            ar: 'Arabic',
            be: 'Belarusian',
            bg: 'Bulgarian',
            ca: 'Catalan',
            zh: 'Chinese',
            'zh-cn': 'Chinese Simplified',
            'zh-tw': 'Chinese Traditional',
            hr: 'Croatian',
            cs: 'Czech',
            da: 'Danish',
            nl: 'Dutch',
            en: 'English',
            et: 'Estonian',
            fl: 'Filipino',
            fi: 'Finnish',
            fr: 'French',
            gl: 'Galician',
            de: 'German',
            el: 'Greek',
            ht: 'Haitian Creole',
            iw: 'Hebrew',
            hi: 'Hindi',
            hu: 'Hungarian',
            is: 'Icelandic',
            id: 'Indonesian',
            ga: 'Irish',
            it: 'Italian',
            ja: 'Japanese',
            ko: 'Korean',
            lv: 'Latvian',
            lt: 'Lithuanian',
            mk: 'Macedonian',
            ms: 'Malay',
            mt: 'Maltese',
            no: 'Norwegian',
            fa: 'Persian',
            pl: 'Polish',
            pt: 'Portuguese',
            // 'pt-pt':'Portuguese (Portugal)',
            ro: 'Romanian',
            ru: 'Russian',
            sr: 'Serbian',
            sk: 'Slovak',
            sl: 'Slovenian',
            es: 'Spanish',
            sw: 'Swahili',
            sv: 'Swedish',
            tl: 'Tagalog',
            th: 'Thai',
            tr: 'Turkish',
            uk: 'Ukrainian',
            vi: 'Vietnamese',
            cy: 'Welsh',
            yi: 'Yiddish'
        }
    };

    /*
    Parses WebVTT format which should be formatted as
    ================================
    WEBVTT

    1
    00:00:01,1 --> 00:00:05,000
    A line of text

    2
    00:01:15,1 --> 00:02:05,000
    A second line of text

    ===============================

    Adapted from: http://www.delphiki.com/html5/playr
    */
    mejs.TrackFormatParser = {
        webvtt: {
            pattern_timecode: /^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,

            parse: function (trackText) {
                var
                    i = 0,
                    lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
                    entries = {text: [], times: []},
                    timecode,
                    text,
                    identifier;
                for (; i < lines.length; i++) {
                    timecode = this.pattern_timecode.exec(lines[i]);

                    if (timecode && i < lines.length) {
                        if ((i - 1) >= 0 && lines[i - 1] !== '') {
                            identifier = lines[i - 1];
                        }
                        i++;
                        // grab all the (possibly multi-line) text that follows
                        text = lines[i];
                        i++;
                        while (lines[i] !== '' && i < lines.length) {
                            text = text + '\n' + lines[i];
                            i++;
                        }
                        text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
                        // Text is in a different array so I can use .join
                        entries.text.push(text);
                        entries.times.push(
                            {
                                identifier: identifier,
                                start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) === 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
                                stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
                                settings: timecode[5]
                            });
                    }
                    identifier = '';
                }
                return entries;
            }
        },
        // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
        dfxp: {
            parse: function (trackText) {
                trackText = $(trackText).filter("tt");
                var
                    i = 0,
                    container = trackText.children("div").eq(0),
                    lines = container.find("p"),
                    styleNode = trackText.find("#" + container.attr("style")),
                    styles,
                    text,
                    entries = {text: [], times: []};


                if (styleNode.length) {
                    var attributes = styleNode.removeAttr("id").get(0).attributes;
                    if (attributes.length) {
                        styles = {};
                        for (i = 0; i < attributes.length; i++) {
                            styles[attributes[i].name.split(":")[1]] = attributes[i].value;
                        }
                    }
                }

                for (i = 0; i < lines.length; i++) {
                    var style;
                    var _temp_times = {
                        start: null,
                        stop: null,
                        style: null
                    };
                    if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
                    if (!_temp_times.start && lines.eq(i - 1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i - 1).attr("end"));
                    if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
                    if (!_temp_times.stop && lines.eq(i + 1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i + 1).attr("begin"));
                    if (styles) {
                        style = "";
                        for (var _style in styles) {
                            style += _style + ":" + styles[_style] + ";";
                        }
                    }
                    if (style) _temp_times.style = style;
                    if (_temp_times.start === 0) _temp_times.start = 0.200;
                    entries.times.push(_temp_times);
                    text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
                    entries.text.push(text);
                    if (entries.times.start === 0) entries.times.start = 2;
                }
                return entries;
            }
        },
        split2: function (text, regex) {
            // normal version for compliant browsers
            // see below for IE fix
            return text.split(regex);
        }
    };

    // test for browsers with bad String.split method.
    if ('x\n\ny'.split(/\n/gi).length != 3) {
        // add super slow IE8 and below version
        mejs.TrackFormatParser.split2 = function (text, regex) {
            var
                parts = [],
                chunk = '',
                i;

            for (i = 0; i < text.length; i++) {
                chunk += text.substring(i, i + 1);
                if (regex.test(chunk)) {
                    parts.push(chunk.replace(regex, ''));
                    chunk = '';
                }
            }
            parts.push(chunk);
            return parts;
        };
    }

})(mejs.$);
