/**
 * Funkcja tworząca obiekt playlisty
 * @param {Object} local_conf obiekt konfiguracji
 */
Playlist = function (local_conf) {
    if (typeof jQuery == 'undefined') {
         throw 'Playlist exception: jQuery not found.';
    }

    if (typeof jQuery.ui.sortable == 'undefined') {
         throw 'Playlist exception: jQuery.ui.sortable not found.';
    }

    if (typeof jQuery.cookie == 'undefined') {
         throw 'Playlist exception: jQuery.cookie not found.';
    }

    jQuery.noConflict();

    // inicjalizacja obiektu konfiguracji
    var conf = {};

    conf.playerObjectId                    = 'playerObject';
    conf.authorTagId                       = 'author';
    conf.titleTagId                        = 'title';
    conf.playlistOuterTagId                = 'playlistOuter';
    conf.playlistInnerTagId                = 'playlistInner';
    conf.qualitySwitch2LoTagId             = 'qualitySwitch2lo';
    conf.qualitySwitch2HiTagId             = 'qualitySwitch2hi';

    conf.playOnClipPage                    = true;

    conf.playlistMaxClips                  = 20;

    conf.playlistClipLoadingText           = '';
    conf.playlistClipLoadingErrorText      = '';
    conf.playlistClipMoveButtonText        = '';
    conf.playlistClipRemoveButtonText      = '';

    conf.mousetipObject                    = null;
    conf.mousetipTextPlaylistClipAdd       = '';
    conf.mousetipTextPlaylistClipAdded     = '';
    conf.mousetipTextPlaylistMaxClipsAdded = '';
    conf.mousetipTextPlaylistClipRemove    = '';
    conf.mousetipTextPlaylistClipRemoved   = '';
    conf.mousetipTextPlaylistClear         = '';
    conf.mousetipTextPlaylistCleared       = '';

    conf.clipJsonDataUrl                  = '/getClipInfo?id={id}';

    conf.cookiesPrefix                    = 'playlist';
    conf.cookiesExpirationDays            = 7;

    // wypełnienie obiektu konfiguracji
    for (var field in local_conf) {
        if(field && local_conf[field]) {
            conf[field] = local_conf[field];
        }
    }

    // inicjalizacja zmiennych i funkcji lokalnych
    var playlistReady = false;

    var clipsList = {};

    var playlistOuterJQ = jQuery([]);
    var playlistInnerJQ = jQuery([]);
    var playlistJQ = jQuery([]);

    var playlistBusy = false;
    var playlist = [];
    var playlistCurrent = -1;

    var playerJQ = jQuery([]);
    var authorJQ = jQuery([]);
    var titleJQ = jQuery([]);

    var clipId = null;
    var playerClipId = null;
    var playerQuality = null;
    var playerIsFullScreen = null;

    // inicjalizacja obiektu narzędzi
    var tools = {};

    /**
     * Funkcja uzyskująca identyfikator z klasy CSS.
     * @param {Object} node sprawdzany węzeł struktury DOM dokumentu HTML
     * @param {String} prefix prefiks identyfikatora
     * @param {String} suffix sufiks identyfikatora
     */
    tools.getClassData = function (node, prefix, suffix) {
        prefix = ' ' + prefix;
        suffix = suffix + ' ';

        var c = ' ' + jQuery(node).attr('class') + ' ';
        var start = c.indexOf(prefix);
        var stop = c.indexOf(suffix, start + prefix.length);

        if (start == -1 || stop == -1) {
            return '';
        }

        return c.slice(start + prefix.length, stop);
    };

    /**
     * Funkcja inicjująca działanie mechanizmu.
     */
    var init = function () {
        jQuery('body').not('.js').addClass('js');
        
        //
        playlistOuterJQ = jQuery('#' + conf.playlistOuterTagId);
        playlistInnerJQ = jQuery('#' + conf.playlistInnerTagId);

        // budowa playlisty na podstawie cookies
        playlistInnerJQ.append(
            playlistJQ = jQuery('<ol>').attr('class', 'clipsList playlist'));

        playlistJQ.sortable({
            start: playlistSortableStart,
            stop: playlistSortableStop,
            distance: 5});

        playlistCurrent = parseInt(jQuery.cookie(conf.cookiesPrefix + '_playlist_current'), 10);

        for (var playlist = playlistLoadGet(), i = 0, mi = playlist.length; i < mi; i++) {
            if (playlist[i]) {
                playlistClipAdd(playlist[i]);
            }
        }

        //
        jQuery('.clipAction').hover(
            function () {
                jQuery(this).addClass('hover');
            },
            function () {
                jQuery(this).removeClass('hover');
            });

        // dowiązanie akcji kliknięcia w .clipsList
        jQuery('.clipsList').click(
            function (event) {
                event = event ? event : window.event;
                var targetJQ = jQuery(event.target || event.srcElement);

                if (!targetJQ.hasClass('clipAction')) {
                    targetJQ = targetJQ.parents('.clipAction').eq(0);
                }

                if (!targetJQ) {
                    return true;
                }

                return clipsListClick(targetJQ);
            });

        // dowiązanie akcji odkrywania / ukrywaniaplaylisty
        jQuery('.playlistShow').bind('click', function () {playlistShow(); });
        jQuery('.playlistHide').bind('click', function () {playlistHide(); });
        jQuery('.playlistToggle').bind('click', function () {playlistToggle(); });

        // dowiązanie akcji czyszczenia playlisty
        jQuery('.playlistClear').bind(
            'click',
            function () {
                playlistClear();
            });

        //
        playlistCount();

        //
        if (jQuery.cookie(conf.cookiesPrefix + '_playlist_visible') == '1') {
            playlistShow();
        }
        else {
            playlistHide();
        }

        //
        playerQuality = jQuery.cookie(conf.cookiesPrefix + '_quality') == 'HI' ? 'HI' : 'LO';

        // dowiązanie akcji zmiany jakości video
        jQuery('#' + conf.qualitySwitch2LoTagId).click(
            function () {
                playerVideoQuality('LO');
            });
        jQuery('#' + conf.qualitySwitch2HiTagId).click(
            function () {
                playerVideoQuality('HI');
            });

        mousetipBind(conf.mousetipTextPlaylistClear, jQuery('.playlistClear'));

        //
        playlistReady = true;

        //
        init = function () {};
    };

    /**
     * Funkcja interpretująca kliknięcie w obiekt klasy clipsList
     * @param {Object} targetJQ
     */
    var clipsListClick = function (targetJQ) {
        var clipJQ = targetJQ.parents('.clip').eq(0);
        var clipId = tools.getClassData(targetJQ, 'id', '') || tools.getClassData(clipJQ, 'id', '') || null;

        // dokładne filtrowanie klikniętego obiektu
        // .clipPlay
        if (targetJQ.hasClass('clipPlay')) {
            playlistPlay(playlistJQ.children('.clip').index(clipJQ));
            return false;
        }

        // .clipPlaylistAdd
        if (targetJQ.hasClass('clipPlaylistAdd')) {
            playlistClipAdd(clipId);
            return false;
        }

        // .clipPlaylistRemove
        if (targetJQ.hasClass('clipPlaylistRemove')) {
            playlistClipRemove(playlistJQ.children('.clip').index(clipJQ));
            return false;
        }

        return true;
    };

    /**
     * Funkcja doładowująca informacje o klipie w przypadku ich braku i wywołująca na nich podany callback.
     * @param {String} id identyfikator klipu
     * @param {callback} success funkcja wywoływana bezpośrednio po pomyślnym załadowaniu danych.
     * @param {callback} error funkcja wywoływana w przypadku błędu AJAX.
     */
    var clip = function (id, success, error) {
        if (clipsList[id]) {
            if (success) {
                success();
            }
        }
        else {
            jQuery.ajax({
                type: 'GET',
                url: conf.clipJsonDataUrl.replace('{id}', id),
                data: {inpl_network_request: '1'},
                dataType: 'json',
                success: function (json, textStatus) {
                    clipsList[id] = json;

                    if (success) {
                        success();
                    }
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    if (error) {
                        error();
                    }
                }
            });
        }
    };

    /**
     * Funkcja rozpoczynająca odtwarzanie klipu.
     * @param {String} id identyfikator klipu
     */
    var clipPlay = function (id) {
        // redirect
        if (playerJQ.length == 0 || (conf.playOnClipPage == true && clipId != id && !playerIsFullScreen)) {
            clip(
                id,
                function(){
                    window.location.href = clipsList[id].url;
                });

            return;
        }

        // player play
        playerJQ.each(
            function (i) {
                try {
                    playerJQ.get(i).playVideo(id);
                }
                catch(exc) {}
            });
    };

    /**
     * Funkcja inicjująca integrację z obiektem playera.
     */
    var playerInit = function () {
        playerJQ = jQuery('#' + conf.playerObjectId);
        authorJQ = jQuery('#' + conf.authorTagId);
        titleJQ = jQuery('#' + conf.titleTagId);

        playerJQ.each(
            function (i) {
                playerJQ.get(i).addEventListener('onStateChange', 'playerStateChange');
                playerJQ.get(i).addEventListener('onError', 'playerError');
                playerJQ.get(i).addEventListener('onRecomendationClick', 'playerRecomendationClick');
                playerJQ.get(i).addEventListener('onFullScreen', 'playerFullScreen');
            });

        playerVideoQuality(playerQuality);
        playerIsFullScreen = false;

        setTimeout(
            function () {
                playerClipChange();
            },
            1000);

        //
        playerInit = function () {};
    };

    /**
     * Funkcja pobierająca informacje o klipie z obiektu playera
     */
    var playerClipChange = function () {
        try {
            videoInfo = playerJQ.get(0).getVideoInfo();
            playerClipId = videoInfo[3];

            if(!clipId) {
                clipId = playerClipId;
            }
        }
        catch(exc) {}
    };

    /**
     * Funkcja kliknięcia w rekomendowany klip wywoływana przez player.
     * @param {String} id identyfikator klipu
     */
    playerRecomendationClick = function (id) {
        clipPlay(id);
    };

    /**
     * Funkcja zmiany stanu odtwarzacza wywoływana przez player.
     * @param {int} state identyfikator nowego stanu
     */
    playerStateChange = function (state) {
        switch(state) {
            case 0: // stop
                if (playlistCurrent > -1 && playlist[playlistCurrent + 1]) {
                    playlistPlay(playlistCurrent + 1);
                }

                break;

            case 1: // resume
                break;

            case 2: // pause
                break;

            case 3: // play
                playerClipChange();

                if (playlistCurrent < 0 || playlist[playlistCurrent] != playerClipId) {
                    playlistCurrent = playlist.indexOf(playerClipId += '');
                    playlistSave();
                }

                displayRefresh();
                playlistDisplayRefresh();
                playerDisplayRefresh();

                break;
        }
    };

    /**
     * Funkcja błędu odtwarzacza wywoływana przez player.
     * @param {int} errorCode kod błędu
     */
    playerError = function (errorCode) {};

    /**
     * Funkcja zmiany trybu fullscreen w playerze.
     * @param {int} isFullscreen flaga statusu trybu fullscreen
     */
    playerFullScreen = function (isFullscreen) {
        if(isFullscreen) {
            playerIsFullScreen = true;
        }
        else {
            playerIsFullScreen = false;

            if(clipId != playerClipId) {
                clip(
                    playerClipId,
                    function(){
                        window.location.href = clipsList[playerClipId].url;
                    });
            }
        }
    };

    /**
     * Funkcja zmieniająca jakość video
     * @param {Object} quality
     */
    var playerVideoQuality = function (quality) {
        jQuery.cookie(conf.cookiesPrefix + '_quality', quality, {expires: conf.cookiesExpirationDays, path: '/'});

        switch(quality) {
            case 'LO':
                jQuery('#' + conf.qualitySwitch2LoTagId).fadeOut(
                    200,
                    function () {
                        jQuery('#' + conf.qualitySwitch2HiTagId).fadeIn(200);
                    });
                break;

            case 'HI':
                jQuery('#' + conf.qualitySwitch2HiTagId).fadeOut(
                    200,
                    function () {
                        jQuery('#' + conf.qualitySwitch2LoTagId).fadeIn(200);
                    });
                break;
        }

        try {
            playerJQ.each(
                function (i) {
                    playerJQ.get(i).changeVideoQuality(quality);
                });
        }
        catch(exc) {}
    };

    /**
     * Funkcja odświeżająca zawartość elementów związanych z playerem.
     */
    var playerDisplayRefresh = function () {
        clip(
            playerClipId,
            function () {
                authorJQ.text(clipsList[playerClipId].author);
                titleJQ.text(clipsList[playerClipId].title);
            });
    };

    /**
     * Funkcja rozpoczynająca odtwarzanie klipu z playlisty.
     * @param {String} index indeks klipu w playliście
     */
    var playlistPlay = function (index) {
        playlistCurrent = index;
        playlistSave();
        clipPlay(playlist[index]);
    };

    /**
     * Funkcja odkrywająca playlistę.
     */
    var playlistShow = function () {
        if (playlistBusy) {
            return;
        }

        playlistBusy = true;
        jQuery('.playlistShow').fadeOut(
            100,
            function () {
                jQuery('.playlistHide').fadeIn(100);
            });
        playlistOuterJQ.slideDown(
            200,
            function () {
                playlistBusy = false;
            });

        jQuery.cookie(conf.cookiesPrefix + '_playlist_visible', 1, {expires: conf.cookiesExpirationDays, path: '/'});

        return false;
    };

    /**
     * Funkcja ukrywająca playlistę.
     */
    var playlistHide = function () {
        if (playlistBusy) {
            return;
        }

        playlistBusy = true;
        jQuery('.playlistHide').fadeOut(
            100,
            function () {
                jQuery('.playlistShow').fadeIn(100);
            });
        playlistOuterJQ.slideUp(
            200,
            function () {
                playlistBusy = false;
            });

        jQuery.cookie(conf.cookiesPrefix + '_playlist_visible', 0, {expires: conf.cookiesExpirationDays, path: '/'});

        return false;
    };

    /**
     * Funkcja ukrywająca widoczną / odkrywająca ukrytą playlistę
     */
    var playlistToggle = function () {
        if (playlistOuterJQ.css('display') == 'none') {
            playlistShow();
        }
        else {
            playlistHide();
        }

        return false;
    };

    /**
     * Funkcja dodająca klip do playlisty.
     * @param {String} id identyfikator klipu
     */
    var playlistClipAdd = function (id) {
        if(playlist.length >= conf.playlistMaxClips) {
            mousetipShow(conf.mousetipTextPlaylistMaxClipsAdded, 1000);
            return;
        }
        
        playlist.push(id);

        var li;
        var author, title;

        if (clipsList[id]) {
            author = clipsList[id].author;
            title = clipsList[id].title;
        }
        else {
            author = conf.playlistClipLoadingText;
            title = conf.playlistClipLoadingText;
        }

        playlistJQ.append(
            li = jQuery('<li>').attr('class', 'clip id' + id).hide().append(
                jQuery('<div>').attr('class', 'clipData').append(
                    jQuery('<div>').attr('class', 'clipAction clipPlay author').text(author),
                    jQuery('<br />'),
                    jQuery('<div>').attr('class', 'clipAction clipPlay title').text(title)),
                jQuery('<ul>').attr('class', 'clipActions').append(
                    jQuery('<li>').append(
                        jQuery('<div>').attr('class', 'clipAction clipPlaylistMove').text(conf.playlistClipMoveButtonText)),
                    jQuery('<li>').append(
                        jQuery('<div>').attr('class', 'clipAction clipPlaylistRemove').text(conf.playlistClipRemoveButtonText)))));

        if (playlistCurrent == -1 && id == playerClipId) {
            playlistCurrent = playlistJQ.children('.clip').index(li);
        }

        if (id == playlist[playlistCurrent]) {
            playlistJQ.children('.clip.id' + id).not('.clipCurrent').addClass('clipCurrent');
        }

        if (playlistCurrent == playlistJQ.children('.clip').index(li)) {
            li.addClass('playlistClipCurrent');
        }

        li.find('.clipAction').hover(
            function () {
                jQuery(this).addClass('hover');
            },
            function () {
                jQuery(this).removeClass('hover');
            });

        //
        mousetipShow(conf.mousetipTextPlaylistClipAdded, 1000);

        //
        displayRefresh();
        playlistDisplayRefresh();
        playlistCount();
        playlistSave();

        li.slideDown(
            200,
            function () {
                li.hide().show();
            });

        //
        if (!clipsList[id]) {
            playlistJQ.children('li.clip.id' + id).addClass('clipLoading');

            clip(
                id,
                function () {
                    playlistJQ.children('li.clip.id' + id).find('.author').text(clipsList[id].author);
                    playlistJQ.children('li.clip.id' + id).find('.title').text(clipsList[id].title);

                    playlistJQ.children('li.clip.id' + id).removeClass('clipLoading').removeClass('clipLoadingError');
                },
                function () {
                    playlistJQ.children('li.clip.id' + id).find('.author').text(conf.playlistClipLoadingErrorText);
                    playlistJQ.children('li.clip.id' + id).find('.title').text(conf.playlistClipLoadingErrorText);

                    playlistJQ.children('li.clip.id' + id).removeClass('clipLoading').addClass('clipLoadingError');
                });
        }

        //
        mousetipBind(conf.mousetipTextPlaylistClipRemove, li.find('.clipPlaylistRemove'));
    };

    /**
     * Funkcja usuwająca klip z playlisty.
     * @param {String} index indeks klipu na playliście
     */
    var playlistClipRemove = function (index) {
        var id = playlist[index];

        playlist.splice(index, 1);

        if (index < playlistCurrent) {
            playlistCurrent--;
        }
        else if (index == playlistCurrent) {
            playlistCurrent = playlist.indexOf(id);
        }

        //
        mousetipShow(conf.mousetipTextPlaylistClipRemoved, 1000);

        //
        playlistJQ.children('.clip').eq(index).fadeOut(
            200,
            function () {
                jQuery(this).remove();

                displayRefresh();
                playlistDisplayRefresh();
                playlistCount();
                playlistSave();
            });
    };

    /**
     * Funkcja przesuwająca klip na playliście.
     * @param {int} i1 bazowy indeks klipu na playliście
     * @param {int} i2 docelowy indeks klipu na playliście
     */
    var playlistClipMove = function (i1, i2) {
        if (i1 == i2 || i1 < 0 || i1 >= playlist.length || i2 < 0 || i2 >= playlist.length) {
            return;
        }

        var tmp = playlistJQ.children('.clip').eq(i1).remove();
        if (i1 < i2) {
            playlistJQ.children('.clip').eq(i2).after(tmp);
        }
        else {
            playlistJQ.children('.clip').eq(i2).before(tmp);
        }

        tmp = playlist[i1];
        playlist[i1] = playlist[i2];
        playlist[i2] = tmp;

        playlistDisplayRefresh();
        playlistSave();
    };

    /**
     * Funkcja czyszcząca playlistę.
     */
    var playlistClear = function () {
        playlist = [];
        playlistCurrent = -1;

        //
        mousetipShow(conf.mousetipTextPlaylistCleared, 1000);

        //
        playlistJQ.fadeOut(
            200,
            function () {
                jQuery(this).empty().show();

                displayRefresh();
                playlistCount();
                playlistDisplayRefresh();
                playlistSave();
            });
    };

    /**
     * Funkcja zapisująca playlistę Cookies.
     */
    var playlistSave = function () {
        jQuery.cookie(conf.cookiesPrefix + '_playlist', playlist.toString(), {expires: conf.cookiesExpirationDays, path: '/'});
        jQuery.cookie(conf.cookiesPrefix + '_playlist_current', playlistCurrent, {expires: conf.cookiesExpirationDays, path: '/'});
    };

    /**
     * Funkcja ładująca playlistę Cookies.
     * @return playlista w postaci tabeli identyfikatorów klipów
     */
    var playlistLoadGet = function () {
        var playlist = jQuery.cookie(conf.cookiesPrefix + '_playlist');
        return playlist ? playlist.split(',') : [];
    };

    /**
     * Funkcja odświeżająca zestawy klas CSS elementów playlisty.
     */
    var playlistDisplayRefresh = function () {
        if (playlistBusy) {
            return;
        }

        playlistJQ.find('.first').removeClass('first');
        playlistJQ.find('.last').removeClass('last');

        if (playlist.length > 0) {
            playlistJQ.children('.clip').eq(0).addClass('first');
            playlistJQ.children('.clip').eq(playlist.length - 1).addClass('last');
        }
    };

    /**
     * Funkcja odświeżająca liczniki pozycji w playliście.
     */
    var playlistCount = function () {
        if (playlist.length > 0) {
            jQuery('.playlistEmpty').hide();
            jQuery('.playlistNotEmpty').show();

            jQuery('.playlistCount').text('(' + playlist.length + ')');
        }
        else {
            jQuery('.playlistNotEmpty').hide();
            jQuery('.playlistEmpty').show();

            jQuery('.playlistCount').text('');
        }
    };

    /**
     * Funkcja podpięta pod sortable-start w playliście.
     */
    var playlistSortableStart = function (event, ui) {
        playlistBusy = true;

        //
        playlistJQ.children('.clip.first').removeClass('first');
        playlistJQ.children('.clip.last').removeClass('last');
    };

    /**
     * Funkcja podpięta pod sortable-stop w playliście.
     */
    var playlistSortableStop = function (event, ui) {
        playlistBusy = false;

        //
        playlist = [];

        playlistJQ.children('.clip').each(
            function () {
                playlist.push(tools.getClassData(this, 'id', ''));
            });

        playlistCurrent = playlistJQ.children('.clip').index(playlistJQ.children('.clip.playlistClipCurrent'));

        playlistDisplayRefresh();
        displayRefresh();
        playlistSave();

        // player drag'n'drop
        playerJQ.each(
            function (i) {
                var x = event.pageX;
                var l = playerJQ.eq(i).offset().left;
                var w = playerJQ.eq(i).width();
                var y = event.pageY;
                var t = playerJQ.eq(i).offset().top;
                var h = playerJQ.eq(i).height();

                if (playerJQ.eq(i) && x > l && x < l + w && y > t && y < t + h) {
                    playlistPlay(playlistJQ.children('.clip').index(ui.item));
                }
            });
    };

    /**
     * Funkcja odświeżająca zestawy klas CSS elementów na stronie.
     */
    var displayRefresh = function () {
        if (playlistBusy) {
            return;
        }

        jQuery('.clip.clipCurrent').removeClass('clipCurrent');
        jQuery('.clip.id' + playlist[playlistCurrent]).addClass('clipCurrent');

        jQuery('.clip.playlistClipCurrent').removeClass('playlistClipCurrent');
        playlistJQ.children('.clip').eq(playlistCurrent).addClass('playlistClipCurrent');

        jQuery('.clipsList .clip.playlistClipPresent').removeClass('playlistClipPresent');
        for (var i = 0; i < playlist.length; i++) {
            jQuery('.clipsList .clip.id' + playlist[i]).addClass('playlistClipPresent');
        }

        mousetipBind(conf.mousetipTextPlaylistClipAdd, jQuery('.clipPlaylistAdd'));
    };

    /**
     * Funkcja pokazująca informację w mousetip porzez podany czas
     * @param {String} text informacja do pokazania
     * @param {int} delay opóźnienie ukrycia mousetipa (milisekundy)
     */
    var mousetipShow = function (content, delay) {
        if(!playlistReady) {
            return;
        }        

        if (conf.mousetipObject) {
            try {
                conf.mousetipObject.show(content, delay);
            }
            catch(exc) {}
        }
    };

    /**
     * Funkcja dowiązującas mousetip do mouseover/mouseout obiektu DOM
     * @param {String} text informacja do pokazania
     * @param {Object} object obiekt DOM (ew. jQuery)
     */
    var mousetipBind = function (content, object) {
        if (conf.mousetipObject) {
            try {
                conf.mousetipObject.bind(content, object);
            }
            catch(exc) {}
        }
    };

    /**
     * Metoda inicjująca integrację z obiektem playera.
     */
    this.playerInit = function () {
        playerInit();
    };

    /**
     * Metoda dodająca rekord do tablicy klipów
     * @param {Object} clip obiekt reprezentujący klip
     */
    this.clipsListAdd = function (clip) {
        clipsList[clip.id] = {id: clip.id, title: clip.title, author: clip.author, url: clip.url};
    };

    /**
     * Metoda renderująca widok
     */
    this.displayRefresh = function () {
        displayRefresh();
    };

    // dowiązanie wywołania metody inicjalizującej mechanizm do zdarzenia document.ready (onload)
    jQuery(document).ready(
        function () {
            init();
        });
};

