var PodcastPlayer = new Class({
    Implements: Options,

    options: {
        'loadTimeout': 10000,
        'playerWidth': 440,
        'playerHeight': 279,
        'tweenDuration': 500,
        'osdDelay': 2500,
        'playerSkin': null,
        'marqueeIncrement': 1,
        'marqueeGap': 50,
        'marqueeSpeed': 28,     //Number from 1 to 50
        'marqueeAttributeDisplay': ['type', 'title', 'author',
            'summary', 'published','copyright'],
        'commentsPerPage': 5,
        'defaultMarqueeText': 'Powered by PrimaryPodcast...',
        'extensionRewrites': {
          // EXT     JW TYPE    PART  REGEX               PORT
            'mp4': ['lighttpd', {3: /(.*)/, 5: /(\d+)\./}, 81]
          //NB. URL Path is split around / to give parts

        },
        'videoCallbackBase': '/download/register-play/',
        'fileIdRegex': /(\d+)\.[a-z0-9]+$/,
	    'scale': 'uniform',
        'mode': 'legacy'
    },

    listeners: {
        'PLAY': ['Controller'],
        'PLAYLIST': ['Controller'],
        'ITEM': ['Controller'],
        'STATE': ['Model'],
        'ERROR': ['Model','View','Controller'],
        'VOLUME': ['Controller'],
        'MUTE': ['Controller']
    },

    currentState: "IDLE",
    oldState: false,
    eventToLoad: false,
    playlist: [],    
    playlistPos: -1,
    id3Pos: -1,
    id3Request: false,
    infoMarquee: false,
    queuedEvents: [],

    cookieDurationDays: 365,
    cookieUpdateTimer: null,

    commentEventAdded: false,

    initialize: function(options) {
        if(!window.console) window.console = {'log': function(){}};

        this.setOptions(options);
        console.log("Initialising player, options: ", options);
        this.initJPlayer( );
        this.initMarquee( );
        this.initControls( );
        this.initComments( );
        this.initOSD( );
        window.addEvent("domready", function( ) {
            this.fixBrowser( );
        }.bind(this));
    },
    fixBrowser: function( ) {
        if(Browser.Engine.name=='trident' && Browser.Engine.version <= 4) {
           for(var i=0; i<document.images.length; i++)
           {
              var img = document.images[i]
              var imgName = img.src.toUpperCase()
              if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
              {
                 var imgID = (img.id) ? "id='" + img.id + "' " : ""
                 var imgClass = (img.className) ? "class='" + img.className + "' " : ""
                 var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
                 var imgStyle = "display:inline-block;" + img.style.cssText 
                 if (img.align == "left") imgStyle = "float:left;" + imgStyle
                 if (img.align == "right") imgStyle = "float:right;" + imgStyle
                 if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
                 var strNewHTML = "<span " + imgID + imgClass + imgTitle
                 + " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
                 + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
                 + "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>" 
                 img.outerHTML = strNewHTML
                 i = i-1
              }
           }
        }
    },
    initOSD: function( ) {
        if(this.osdElement) {
            this.osdElement.empty();
            this.osdElement.setOpacity(0);
            this.osdElement.setStyle("display", "block");

            this.osdElement.addEvent("mouseenter", function(ev) {
                this.pauseOSD( );
            }.bind(this));
            this.osdElement.addEvent("mouseleave", function(ev) {
                this.resumeOSD( );
            }.bind(this));
        }
    },
    flashOSD: function(text) {
        if(this.osdElement) {
            this.osdElement.empty();
            this.osdElement.set('html', text);
            this.osdElement.tween("opacity", 1);
            this.resumeOSD( );
        }
    },
    hideOSD: function( ) {
        this.osdElement.tween("opacity", 0);
    },
    pauseOSD: function(text) {
        if(this.osdElement) {
            this.osdElement.tween("opacity", 1);
            $clear(this.osdTimer);
        }
    },
    resumeOSD: function(text) {
        $clear(this.osdTimer);
        this.osdTimer = this.hideOSD.delay(this.options.osdDelay, this);
    },
    initComments: function( ) {
        if(this.options.mode == 'ng') {
            this.commentsHideElement = this.commentsElement.getElement(".hide");
            this.commentsSlide = new Fx.Slide(this.commentsHideElement);
            this.commentsSlide.slideOut( );

            this.toggleElements = this.commentsElement.getElements(".show");
            this.toggleElements.setStyle("display", "none");
            this.toggleElements.addEvent("click", 
                this.toggleComments.bindWithEvent(this));
            this.addCommentElements = this.commentsElement.getElements(".add");
            this.addCommentElements.addEvent("click",
                    this.addComment.bindWithEvent(this));

            var form = this.commentsElement.getElement("form");
            if(form) {
                this.commentsForm = form;

                this.commentsForm.addEvent("submit", 
                    this.submitCommentsForm.bindWithEvent(this)
                );
            }  

            this.formElement = this.commentsHideElement.getElement(".commentForm");
            this.formElement.setStyle("display", "none");

            this.updateComments();

            return;
        }

        if(this.commentsElement && this.options.commentsBaseUrl) {
            this.commentsElement.setOpacity(0);
            var comments = this.controlsElement.getElement(".commentB");
            if(comments) {
                this.interfaceSize = this.interfaceElement.getSize( );
                this.commentsButton = comments;
                this.commentsSlide = new Fx.Morph(this.commentsElement);
                this.commentsElement.setStyle("display", "block");
                this.commentsWidth = this.commentsElement.getSize( ).x;
                this.commentsElement.setStyle("width",
                    this.interfaceSize.x);

                this.resizeComments(true);
                comments.setStyle("display", "block");
                comments.setStyle("opacity", 0);

                comments.getElement("a").empty();
                new Element("span").inject(comments.getElement("a"));
            }

            var form = this.commentsElement.getElement("form");
            if(form) {
                this.commentsForm = form;

                this.commentsForm.addEvent("submit", 
                    this.submitCommentsForm.bindWithEvent(this)
                );


                this.commentsElement.getElements('a.close-link').addEvent("click",
                    this.toggleComments.bindWithEvent(this));
            }  
        }
    },
    closeCommentsForm: function(ev) {
        if(ev) new Event(ev).stop( );
        this.commentsForm.getParent( ).setStyle("display", "none");
        this.resizeComments.delay(50, this);
    },
    showCommentsForm: function(ev) {
        if(this.commentsForm) {
            var form = this.commentsForm;
            this.commentsForm.getParent( ).setStyle("display", "block");
            form.getElement(".comment-errors").setStyle("display", "none");
            form.getElement(".elements").setStyle("display", "block");
            form.getElements("input").className = '';
            this.resizeComments.delay(50, this);
        }
    },
    checkCommentsResponse: function(resp, text) {
        var form = this.commentsForm;
        if(resp.result == 'error') {
            var ul = new Element("ul", {"class": "errors"});
            for(field in resp.errors) {
                var el = form.getElement('[name='+field+']');
                if(el) {
                    el.addClass("error");
                    var li = new Element("li");
                    li.set('html', resp.errors[field]);
                    li.inject(ul);
                }
            }
            var el = form.getElement(".comment-errors");
            el.empty();
            var err = "<p>Sorry, we could not add your comment. Please "+
                "correct the errors below.";
            el.set('html', err);
            ul.inject(el);
            el.setStyle("display", "block");
            this.resizeComments(false);
            if(this.options.mode == 'ng') this.commentsSlide.slideIn( );
        } else {
            form.getElement(".elements").setStyle("display", "none");
            var el = form.getElement(".comment-errors");

            form.getElements("input").set('value', '');
            form.getElements("textarea").set('html', '');

            el.addClass('success');
            p = new Element("p");
            p.set('html', "Thank you, your comment has been submitted for "+
                "approval. It will appear on the comments list when "+
                "it is approved by a teacher.");
            el.empty();
            p.addClass("success");
            p.inject(el);
            el.setStyle("display", "block");        
            this.updateComments();
        }
    },
    submitCommentsForm: function(ev) {
        new Event(ev).stop( );        
        this.resizeComments( );
        var pl = this.playlist[this.playlistPos];
        if(pl) {
            try{
                var id = this.fileToId(pl['file']);
                this.commentsForm.getElement('[name=podcast-id]').set(
                    'value', id
                );
            } catch(e) { console.log(e); return; }
            var data = this.commentsForm.toQueryString( );
            var action = this.commentsForm.get('action');

            this.commentsForm.getElement(".comment-errors").
                setStyle("display", "none");
            this.commentsForm.getElements("input").className = '';

            if(this.commentsRequest) this.commentsRequest.cancel();
            this.commentsRequest = new Request.JSON({'url': action, 
                'method': 'post',
                'data': data,
                'onComplete': this.checkCommentsResponse.bind(this)
            });
            this.commentsRequest.send();
        }
    },
    toggleComments: function(ev) {
        if(this.options.mode == 'ng') {
            if(ev) new Event(ev).stop( );
            if(this.commentsShown) {
                this.commentsShown = false;
                this.toggleElements.set('html', 'Show Comments');
                this.commentsSlide.slideOut( );
                this.formElement.setStyle("display", "none");
                this.formElement.getElement(".comment-errors").empty();
                this.commentsFormShown = false;
            } else {
                this.commentsShown = true;
                this.toggleElements.set('html', 'Hide Comments');
                (function( ) { this.commentsSlide.slideIn( );}).delay(100,this);
            }

            return;
        }

        if(ev) new Event(ev).stop( );
        if(this.commentsShown) {
            this.commentsButton.removeClass("comments-shown");
            this.commentsShown = false;
            /*if(this.commentsForm.getParent( ).getStyle("display") != "none")
                this.closeCommentsForm( );*/

            this.resizeComments(true);
        } else {
            this.commentsButton.addClass("comments-shown");
            this.commentsShown = true;
            this.resizeComments( );
        }
                
    },
    resizeComments: function(hide) {
        if(this.options.mode == 'ng') {
            return;
        }

        if(hide) {
            this.commentsSlide.start({
                'height': 0,
                'width': this.interfaceSize.x
            }).chain(function(ev) { 
                this.commentsElement.getElement(".podcastComments").setStyle(
                    "display", "none"
                );
                this.commentsElement.setStyle("display", "none");
            }.bind(this));
            this.interfaceElement.getParent( ).tween('height', 
                this.interfaceSize.y
            );
            this.commentsShown = false;
        } else {
            this.commentsElement.setOpacity(1);
            this.commentsElement.setStyle("left", -10000);
            var parent = this.commentsElement.getParent( );
            this.commentsElement.getElement(".comment-errors").setStyle(
                "display", "none"
            );
            this.commentsElement.getElement(".podcastComments").setStyle(
                "display", "block"
            );
            this.commentsElement.dispose( );
            this.commentsElement.inject($$('body')[0]);
            this.commentsElement.setStyle("display", "block");
            var w = this.commentsElement.getSize( ).x;
            var h = this.commentsElement.getSize( ).y; 
            this.commentsElement.setStyle("width", "100%");
            this.commentsElement.setStyle("height", "auto");
            this.commentsSize = this.commentsElement.getSize( );

            console.log(this.commentsSize);

            this.commentsElement.dispose( );
            this.commentsElement.setStyle("left", "");
            this.commentsElement.inject(parent);
            this.commentsElement.setStyle("width", w);
            this.commentsElement.setStyle("height", h);

            this.commentsSlide.start({
                'height': this.commentsSize.y,
                'width': this.commentsWidth
            });
            this.interfaceElement.getParent( ).tween('height', 
                this.interfaceSize.y + this.commentsSize.y
            );
            this.commentsShown = true;

        }
    },
    commentsNext: function( ) {
        this.updateComments(false, this.commentsPage + 1);
    },
    commentsPrev: function( ) {
        this.updateComments(false, this.commentsPage - 1);
    },
    addComment: function(event) {
        var ev = new Event(event);
        ev.stop( );

        var delay = 1;
        if(this.commentsShown) {
            this.toggleComments( );
            delay = 600;            
        }
        this.formElement.getElement(".comment-errors").setStyle("display", "none");
        this.formElement.setStyle("display", "block");
        (function( ) {
            this.commentsSlide.slideIn( );
            this.toggleElements.set('html', 'Hide Comments');
            this.toggleElements.setOpacity(0);
            this.toggleElements.setStyle("display", "");
            this.toggleElements.tween('opacity', 1);
            this.commentsShown = true;
            (function( ) { window.location = ev.target.get('href'); }).delay(500);
        }).delay(delay, this);
    },
    prepareComments: function(json) {
        if(this.options.mode == 'ng') {
            var num = json.num.toInt();
            var unapproved = json.num_unapproved.toInt();
                       
            if(num) {
                if(!this.commentsShown) {
                    this.toggleElements.setOpacity(0);
                    this.toggleElements.setStyle("display", "");
                    this.toggleElements.tween('opacity', 1);
                }
            } else {
                if(!this.commentsShown) {
                    this.toggleElements.tween('opacity', 0);
                    this.toggleElements.setStyle("display", "none");
                }
            }

            var info = this.commentsHideElement.getElement(".info");

            var start = ((json.page - 1) * this.options.commentsPerPage) + 1;
            var end = start + json.comments.length - 1;
            var msg = num+" comment"+(num!=1?"s":"")+" ("+unapproved+" unapproved)";
                var infoMsg = "Currently viewing "+start+"-"+end+" of "+
            json.num+" comments.";

            if(!json.comments.length) {
                infoMsg = "There are currently no comments on this podcast.";
            }
            info.set('html', infoMsg);

            this.commentsElement.getElement(".numbers").set('html', msg);

            var hasNext = end < json.num.toInt();
            var hasPrev = start > 1;      

            info.getElements(".cont").dispose();
      
            if(hasPrev) {
                var n = new Element("a", {'class': 'prev cont', 'href': '#'});
                n.set('html', 'Previous Page');
                n.addEvent("click", function(ev) {
                    this.commentsPrev( );
                    new Event(ev).stop();
                }.bind(this));
                n.inject(info);
            }
            if(hasNext) {
                var n = new Element("a", {'class': 'next cont', 'href': '#'});
                n.set('html', 'Next Page');
                n.addEvent("click", function(ev) {
                    this.commentsNext( );
                    new Event(ev).stop();
                }.bind(this));
                n.inject(info);
            }

            var commentsElement = this.commentsHideElement.getElement(
                ".podcastComments");
            commentsElement.empty();



            var commentEl, commentBody, commentDate, commentMessage, mess, wrapEl;
            json.comments.each(function(comment) {
                try {
                commentEl = new Element("div", {'class': 'comment'});
                wrapEl = new Element("div", {'class': 'wrapper'});
                wrapEl.inject(commentEl);
    
                commentMessage = new Element("div", {'class': 'message', 
                    'html': comment.message});
                commentDate = new Element("div", {'class': 'date'});
                mess = 'By ';
                if(comment.email) {
                    mess += "<a href='mailto:"+comment.email+"'>";
                }
                mess+= comment.from;
                if(comment.email) {
                    mess += "</a> ("+comment.email+")";
                }                
                mess += ", on ";
                mess += comment.created.substring(8, 10)+"/"+
                        comment.created.substring(5, 7)+"/"+
                        comment.created.substring(0, 4)+" at "+
                        comment.created.substring(11, 13)+":"+
                        comment.created.substring(14, 16);

                commentDate.set('html', mess);
                commentMessage.set('html', comment.message);
                commentMessage.inject(wrapEl);
                commentDate.inject(wrapEl);
                commentEl.inject(commentsElement); 
                }catch(e){ console.log(e); }
            });

            if(this.commentsShown) this.commentsSlide.slideIn( );

            return;
        }


        var cHolder = this.commentsElement.getElement(".podcastComments");
        var comments = new Element("div", {'class': 'commentDetail'});
        var info = new Element("div", {'class': 'commentInfo'});
        var start = ((json.page - 1) * 5) + 1;
        var end = start + json.comments.length - 1;
        var msg = "Currently viewing "+start+"-"+end+" of "+
            json.num+" comments.";

        if(!this.commentEventAdded) {
            this.commentsButton.tween('opacity', 1);
            this.commentsButton.getElement("a").addEvent("click", function(ev) {
                new Event(ev).stop();
                this.toggleComments( );
            }.bind(this));
            this.commentEventAdded = true;
        }

        if(json.num_unapproved.toInt()) {
            msg+=" ("+json.num_unapproved+" unapproved comment" + 
                (json.num_unapproved.toInt() > 1 ? "s" : "")+")";

            if(json.last_unapproved) {
                var date = json.last_unapproved;
                
                msg+=" Last comment waiting to be approved added on "+
                date+".";
                console.log(msg);
            }
        }

        info.set('html',msg);
        var hasNext = end < json.num.toInt();
        var hasPrev = start > 1;

        this.commentsButton.getElement("span").set('html', 
            json.num);

        if(hasPrev) {
            var n = new Element("a", {'class': 'prev', 'href': '#'});
            n.set('html', 'Previous Page');
            n.addEvent("click", function(ev) {
                this.commentsPrev( );
                new Event(ev).stop();
            }.bind(this));
            n.inject(info);
        }
        if(hasNext) {
            var n = new Element("a", {'class': 'next', 'href': '#'});
            n.set('html', 'Next Page');
            n.addEvent("click", function(ev) {
                this.commentsNext( );
                new Event(ev).stop();
            }.bind(this));
            n.inject(info);
        }

        if(!cHolder) return;
        cHolder.empty();
        if(json.comments.length > 0) info.inject(cHolder);
        json.comments.each(function(c) {
            this.createComment(c).inject(comments);
        }.bind(this));
        if(json.comments.length == 0) {
            var date = "";
            if(json.last_unapproved) {
                var d2 = json.last_unapproved;
                
                date=" Last comment waiting to be approved added "+
                d2+".";
            }

            var msg = json.num_unapproved.toInt() ? 
                'There '+
                (json.num_unapproved.toInt() > 1 ? "are" : "is")+
                ' currently '+json.num_unapproved+
                ' unapproved comment'+ 
                (json.num_unapproved.toInt() > 1 ? "s" : "")+
                " on this podcast. "+date :
                'There are currently no comments on'+
                ' this podcast.';
            new Element("div", {'class': 'comment',
                'html': msg}).inject(comments);
        }
        comments.inject(cHolder);
        /*var el = new Element("a", {'class': 'add-comment', 'html':
            'Add A Comment', 'href': '#'});
        el.addEvent("click", function(ev) {
            new Event(ev).stop( );
            if(this.commentsForm.getParent( ).getStyle("display") == "block") {
                this.closeCommentsForm( );    
                el.set('html', 'Add A Comment');
            } else {
                this.showCommentsForm( );
                el.set('html', 'Close Comment Form');
            }
        }.bind(this));
        el.inject(cHolder);*/
        if(this.commentsShown && json.comments.length + 
            json.num_unapproved.toInt() != this.lastComments){
            this.resizeComments( );
            this.lastComments = json.comments.length +
                json.num_unapproved.toInt();
        }
    },
   createCommentNew: function(el) {
        var comment = new Element("div", {'class': 'nComment'});
        var tdd = new Element("div", {'class': 'date'});
        var date =   el['created'].substring(8, 10)+"/"+
                    el['created'].substring(5, 7)+"/"+
                    el['created'].substring(0,4)+" at "+
                    el['created'].substring(11, 13)+":"+
                    el['created'].substring(14, 16);

        var msg = "By ";
        if(el['email']) {
            msg += "<a href='mailto:"+
                el['email']+"'>"+el['from']+"</a> ("+el['email']+")";
        } else {
            msg += el['from'];
        }
        msg +=" on "+date;
        tdd.set('html', msg);

        var tdc = new Element("div", {'class': 'c_message'});
        tdc.set('html', el['message'].replace("\n", "<br />"));

        tdc.inject(comment);
        tdd.inject(comment);
        return comment;
    },
    createComment: function(el) {
        var comment = new Element("div", {'class': 'comment'});
        var tdd = new Element("div", {'class': 'date'});
        tdd.set('html',
            el['created'].substring(8, 10)+"/"+
            el['created'].substring(5, 7)+"/"+
            el['created'].substring(0,4)+" "+
            el['created'].substring(11, 13)+":"+
            el['created'].substring(14, 16)
        );
        var tdf = new Element("div", {'class': 'from'});
        if(el['email']) {
            tdf.set('html',"<a href='mailto:"+
                el['email']+"'>"+el['from']+"</a> ("+el['email']+")");
        } else tdf.set('html', el['from']);
        var tdc = new Element("div", {'class': 'c_message'});
        tdc.set('html', el['message'].replace("\n", "<br />"));
        tdd.inject(comment);
        tdf.inject(comment);
        tdc.inject(comment);
        return comment;
    },
    storeCookie: function( ) {
        if(this.swiff && this.swiff.object.getConfig) {
            var vol = this.swiff.object.getConfig()['volume'];
            var opts = {'v': vol, 's': this.shuffle ? 1 : 0,
                'm':this.mute ? 1: 0};
            Cookie.write("PS_"+this.options.JPlayer.ID, 
                JSON.encode(opts), {'duration': this.cookieDurationDays});
            console.log("Write state cookie: ",opts," ("
                +this.cookieDurationDays+" days)");
        }
    },
    readCookie: function( ) {
        var cookie = Cookie.read("PS_"+this.options.JPlayer.ID);
        if(cookie) {
            var json = JSON.decode(cookie);
            console.log("Read state cookie: ",json);
            if(json) {
                if(this.swiff && this.swiff.object.sendEvent) {
                    this.swiff.object.sendEvent("VOLUME", json.v);
                    if(json.mute) this.swiff.object.sendEvent("MUTE", true);
                    this.shufflePlaylist(json.s);
                }
            }
        }
    },

    initJPlayerEvents: function(prefix) {
        console.log("Initialising player listeners:",this.swiff.instance);
        window[this.swiff.instance]= this;
        var player = this.swiff.object;
        for(id in this.listeners) {
            this.listeners[id].each(function(el) {
                player['add'+el+'Listener'](id, 
                 "window."+this.swiff.instance+".JPlayer"+id);
            }.bind(this));
        }
    },
    updateComments: function(force, page) {
        if(this.options.mode == 'ng') {
            if(!page) page = 1;
            this.commentsPage = page;            

            if(this.commentsRequest) this.commentsRequest.cancel();            
            var pl = this.playlist[this.playlistPos];
            if(pl) {
                try{
                var id = this.fileToId(pl['file']);
                if(!id) return;
                this.commentsRequest = new Request.JSON({
                    'url': this.options.commentsBaseUrl+id+"/page="+
                        this.commentsPage,
                    'onSuccess': function(json, text) {
                        try{
                            this.prepareComments(json, text, force);
                        }catch(e) { console.log(e); }
                    }.bind(this)
                });
                this.commentsRequest.get();
                console.log("Loading Comments: ", this.options.commentsBaseUrl+id);
                } catch(e) { console.log(e); }
            }

            return;
        }

        if(!page) page = 1;
        this.commentsPage = page;

        if(!this.options.commentsBaseUrl) {
            console.log("No options.commentsBaseUrl set, cannot load comments.");
            return;
        }

        if(this.commentsRequest) this.commentsRequest.cancel();
        var pl = this.playlist[this.playlistPos];
        if(pl) {
            try{
            var id = this.fileToId(pl['file']);
            if(!id) return;
            this.commentsRequest = new Request.JSON({
                'url': this.options.commentsBaseUrl+id+"/page="+
                    this.commentsPage,
                'onSuccess': function(json, text) {
                    this.prepareComments(json, text, force);
                }.bind(this)
            });
            this.commentsRequest.get();
            console.log("Loading Comments: ", this.options.commentsBaseUrl+id);
            } catch(e) { console.log(e); }
        }

    },
    updateId3Details: function(force) {
        if(!this.options.id3BaseUrl) {
            console.log("No options.id3BaseUrl set, cannot update file info.");
            return;
        }
        if(this.id3Pos == -1 || this.id3Pos != this.playlistPos || force) {
            if(this.id3Request) this.id3Request.cancel();
            var pl = this.playlist[this.playlistPos];
            if(pl) {
                try{
                var purl = this.parseUrl(pl['file']);
                var opts = null;
                var parts = purl.file.split(".");
                var path_parts = purl.path.split("/");
                var ext = parts[parts.length-1];

                this.videoCallbackComplete = false;
                if(ext == "mp4") {
                    this.currentIsVideo = this.options.videoCallbackBase + 
                        path_parts[path_parts.length-2]+"/"+
                        path_parts[path_parts.length-1];
                } else this.currentIsVideo = false;

                var id = this.fileToId(pl['file']);
                if(!id) return;
                this.id3Request = new Request.JSON({
                    'url': this.options.id3BaseUrl+id,
                    'onSuccess': function(json, text) {
                        this.loadId3Details(json, text, force);
                    }.bind(this)
                });
                this.id3Request.get();
                console.log("Loading Id3 Details: ", this.options.id3BaseUrl+id);
                } catch(e) { console.log(e); }
            }
        }
    },
    loadId3Details: function(json, text, no_reset) {
        var text = '';

        if(json) this.options.marqueeAttributeDisplay.each(function(att) {
            if(json[att]) {
                var klass = json[att].match(/^(video|audio)/);
                text+= "<span class='mq-"+(klass ? klass[0] : att)+"'>"+
                    json[att]+"</span>";
            }
        });
        text+= "<span class='default'>"+this.options.defaultMarqueeText+
            "</span>";   

        var rating = 0;
        if(json) {
            if(json.rating) {
                rating = Math.round(json.rating.toFloat());
                if(rating < 0) rating = 0;
                if(rating > 5) rating = 5;
            } 
        }
        if(this.ratingElement) { 
            this.ratingElement.className = 'rating';
            if(rating > 0) this.ratingElement.addClass("cr"+rating);
        }

        this.flashOSD(text);
        this.setMarqueeText(text, no_reset);
    },
    updateRating: function(rating) {
        if(this.playlistPos >= 0 && this.playlist) {
            var el = this.playlist[this.playlistPos];
            if(el) {
                if(this.ratingRequest) this.ratingRequest.cancel();
                if(!this.options.submitBaseUrl) {
                    console.log("Cannot submit rating, as options.submitBaseUrl"+
                        " has not been specified.");
                    return;
                }
                var url = this.options.submitBaseUrl+
                    this.fileToId(el['file'])+"/"+rating;
                this.ratingRequest = new Request({
                    'url': url,
                    'onSuccess': function(html) { 
                        this.loadRating(html);
                    }.bind(this)
                });
                this.ratingRequest.get();
            }
        };
    },
    loadRating: function(text) {
        this.updateId3Details(true);
        if(this.infoElement) {
            var dispDiv = new Element("div", {'class': 'message',
                'html': "<span>"+text+"</span>"});
            dispDiv.setOpacity(0);
            dispDiv.inject(this.controlsElement);
            var tw = new Fx.Tween(dispDiv);
            tw.start('opacity', 1).chain(function( ) {
                tw.start.delay(3000, tw, ['opacity', 0]);
                (function( ) { dispDiv.destroy(); }).delay(4000);
            });
        }
    },
    initControls: function( ) {
        var rating = this.controlsElement.getElement(".rating");
        this.ratingElement = rating;
        if(rating) {
            //Preload classes (and images)
            var el = new Element("li");
            el.setOpacity(0);
            el.setStyle("position", "absolute");
            el.inject(rating);
            for(i = 1; i <=5 ;i++) {
                el.addClass("r"+i);
                el.addClass("cr"+i);
            }
            el.destroy();

            rating.getElements("li").addEvent("mouseenter", function(ev) {
                ["1","2","3","4","5"].each(function(el) {
                    rating.removeClass("r"+el);
                });
                rating.addClass("highlighted");
                rating.addClass('r'+this.getElement("span").get('html'));
            });
            rating.getElements("li").each(function(el) {
                el.addEvent("click", function(ev) {
                    new Event(ev).stop( );
                    var rating = el.getElement("span").get('html').toInt();
                    this.updateRating(rating);
                }.bind(this));
            }.bind(this));
            rating.addEvent("mouseleave", function(ev) {
                ["1","2","3","4","5"].each(function(el) {
                    rating.removeClass("r"+el);
                });
                rating.removeClass("highlighted");
            });
        }
        var shuffle = this.controlsElement.getElement(".shuffle");
        this.shuffleElement = shuffle;
        if(shuffle) {
            shuffle.getElement("a").addEvent("click", function(ev) {
                new Event(ev).stop( );
                this.toggleShuffle( );
            }.bind(this));
        }
        var fullscreen = this.controlsElement.getElement(".fullscreen");
        this.fullscreenElement = fullscreen;
        if(fullscreen) {
            fullscreen.getElement("a").addEvent("click", function(ev) {
                new Event(ev).stop( );
                this.toggleFullscreen( );
            }.bind(this));
        }
    },
    initMarquee: function( ) {
        this.infoMarquee = this.interfaceElement.getElement(".playerInfo");
        if(this.infoMarquee) {
            //this.infoMarquee.setStyle("position", "relative");
            this.infoMarquee.setStyle("overflow", "hidden");
            var size = {
                'x': this.infoMarquee.getStyle("width").toInt(),
                'y': this.infoMarquee.getStyle("height").toInt()
            };

            var marqueeDiv = new Element("table");
            marqueeDiv.set('html', "<tr><td class='content'><div>"+
                this.options.defaultMarqueeText+"</div></td></tr>");
            marqueeDiv.setStyles({
                'position': 'absolute','top': 0, 'left': 10,
                'width': 10000, 'margin': 0, 'padding': 0,
                'text-align': 'left'
            });
            marqueeDiv.inject(this.infoMarquee);
            
            marqueeDiv.addEvent("mouseenter", function(ev) {
                this.stopMarquee( );
            }.bind(this));
            marqueeDiv.addEvent("mouseleave", function(ev) {
                this.startMarquee( );
            }.bind(this));

            this.marquee = marqueeDiv;
            this.marqueeSize = size;
            this.marqueePos = size.x;
            this.startMarquee( );
        }
    },
    stopMarquee: function( ) {
        $clear(this.marqueeTimer);
    },
    startMarquee: function( ) {
        $clear(this.marqueeTimer);
        this.marqueeTimer = this.scrollMarquee.periodical(
            51-this.options.marqueeSpeed, this
        );
    },
    scrollMarquee: function( ) {
        if(this.marquee) {
            this.marqueePos -= this.options.marqueeIncrement;
            var marquee = this.infoMarquee.getElement("table");
            marquee.setStyle("left", this.marqueePos);
            var s = this.marquee.getElement("div").getSize( );
            var sz = this.infoMarquee.getSize( );
            if(this.marqueePos + s.x < -this.options.marqueeGap)
                this.marqueePos = sz.x;
        } else $clear(this.marqueeTimer);
    },
    resetMarquee: function( ) {
        if(this.marquee) {
            this.marqueePos = this.marqueeSize.x;
        }
    },
    setMarqueeText: function(html, no_reset) {
        if(this.marquee) {
            if(!this.marqueeTween) this.marqueeTween = new Fx.Tween(this.marquee);
            this.marqueeTween.start('opacity', 0).chain(function( ) {
                if(!no_reset) this.resetMarquee();
                this.marquee.getElement("div").set('html', html);
                this.marqueeTween.start('opacity', 1);
            }.bind(this));
        }
    },
    JPlayerITEM: function(params) {
        console.log("JPlayer Playlist Item: ", params.index);
        this.playlistPos = params.index;
        this.updateId3Details();
        this.updateComments();

    },
    JPlayerERROR: function(params) {
        console.log("JPlayer Error: ", params, params.message);
        this.sendEvent("LOAD", this.options.errorLoad);
        this.sendEvent('PLAY');
        this.ignoreState = true;
    },
    JPlayerPLAYLIST: function(params) {
        console.log("Loaded Playlist: ", params);
        this.playlist = params.playlist;
        this.playlistPos = 0;
        this.updateId3Details();
        this.updateComments();

    },
    JPlayerSTATE: function(playing) {
        this.currentState = playing.newstate;
        this.oldState = playing.oldstate
        console.log("Current JPlayer State: ", this.currentState,
            "(Prev: "+this.oldState+")");

        if(this.currentState == "PLAYING" && !this.videoCallbackComplete) {
            if(this.currentIsVideo && this.options.videoCallbackBase) {
                this.videoCallbackComplete = true;
                new Request.HTML({'url': this.currentIsVideo}).get();
            }
        }

        if(this.eventToLoad) {
            var doEvent = false;
            if (this.currentState == "IDLE" ||
                this.currentState == "COMPLETED") doEvent = true;

            if(doEvent) {
                ev = this.eventToLoad;
                this.eventToLoad = null;
                console.log("JPlayer state idle/completed, running "+
                    "delayed event: ", this.eventToLoad);
                this.sendEvent(ev[0], ev[1]);
            }
        }
    },
    JPlayerVOLUME: function(vol) {
        if(this.cookieUpdateTimer) $clear(this.cookieUpdateTimer);
        this.flashOSD("<span class='mq-volume'>Volume "+vol.percentage+
            "%</span>");
        this.cookieUpdateTimer = this.storeCookie.delay(1000, this);
    },
    JPlayerMUTE: function(mute) {
        if(this.cookieUpdateTimer) $clear(this.cookieUpdateTimer);
        this.mute = mute.state;
        this.flashOSD("<span class='mq-volume'>Mute "+(mute.state ? "On" :
                "Off")+"</span>");
        this.cookieUpdateTimer = this.storeCookie.delay(1000, this);
    },
    initJPlayer: function( ) {
        console.log("Loading JPlayer SWF...");

        if(!window.playerReady) {
            window.playerReady = this.JPlayerCallback.bind(this);
        }
        var swf = new Swiff(this.options.JPlayer.URL, {
            'id': this.options.JPlayer.ID,
            'width': 352, 'height': 306,
            'container': $(this.options.playerElement),
            'params': {
                'allowfullscreen': 'true',
                'allowscriptaccess': 'always',
                'wmode': 'transparent'
            }, 'vars': {
                'allowfullscreen': 'true',
                'controlbar': 'bottom',
                'autostart': 'false',
                'skin': this.options.playerSkin ? this.options.playerSkin : "",
                'id': this.options.JPlayer.ID,
                'name': this.options.JPlayer.ID,
		        'scale': this.options.scale,
                'plugins': this.options.JPlayer.flashvars
            }
        });

        this.swiff = swf;
        this.playerElement = $(this.options.playerElement);
        this.controlsElement = $(this.options.controlsElement);
        this.commentsElement = $(this.options.commentsElement);
        this.controlsElement.setStyle("display", "none");
        this.interfaceElement = $(this.options.interfaceElement);
        this.interfaceElement.setOpacity(0);
        this.interfaceElement.getElement(".mainWrapper").setStyle(
            "display", "block"        
        );
        this.interfaceElement.getElement(".extControls").setStyle(
            "display", "block"        
        );
        this.errorElement = $(this.options.errorElement);
        this.infoElement = $(this.options.infoElement);
        this.osdElement = $(this.options.onscreenDisplayElement);
    

        console.log("Resultant element: ", this.playerElement.innerHTML,
            "\nEmbedding SWF...");

        this.loadTimer = this.loadTimeout.delay(
            this.options.loadTimeout, this
        );
    },

    fullscreen: function(bool) {
        if(bool) {
            this.fullscr = true;
            this.sendEvent("FULLSCREEN", true);
        } else {
            this.fullscr = false;
            this.sendEvent("FULLSCREEN", false);
        }
    },
    toggleFullscreen: function( ) {
        this.fullscreen(!this.fullscr)
    },
    shufflePlaylist: function(bool) {
        if(bool) {
            if(!this.shuffle) {
                this.shuffle = true;
                if(this.shuffleElement) {
                    this.shuffleElement.addClass("shuffled");
                }
                this.flashOSD("<span class='mq-shuffle-on'>Shuffle On</span>");
                if(this.currentList) this.loadPlaylist(this.currentList);
            }
        } else {
            if(this.shuffle) {
                if(this.shuffleElement) {
                    this.shuffleElement.removeClass("shuffled");
                }
                this.flashOSD("<span class='mq-shuffle-off'>Shuffle Off</span>");
                this.shuffle = false;
                if(this.currentList) this.loadPlaylist(this.currentList);
            }
        }
        if(this.cookieUpdateTimer) $clear(this.cookieUpdateTimer);
        this.cookieUpdateTimer = this.storeCookie.delay(1000, this);
    },
    toggleShuffle: function( ) {
        this.shufflePlaylist(!this.shuffle);
    },

    loadPlaylist: function(list) {
        if(this.shuffle) {
            if(list.contains("?")) list+="&shuffle=1";
                else list+="?shuffle=1";
        } else list = list.replace(/\&|\?shuffle\=1/, '');

        console.log("Loading playlist ("+this.currentState+"): ", list);
        if(this.currentState != "IDLE" &&
            this.currentState != "COMPLETED" && !this.ignoreState) {
            console.log("Delaying playlist load event until player idle.");
            this.eventToLoad = ['LOAD', list];
        } else {
            this.ignoreState = false;
            this.sendEvent("LOAD", list);
            this.currentList = list;
        }
    },
    
    loadTimeout: function( ) {
        this.interfaceElement.setOpacity(1);
        this.interfaceElement.setStyle("background-image", "none");
        this.interfaceElement.getElement(".mainWrapper").setStyle(
            "background-image", "none");
        this.interfaceElement.getElement(".mainWrapper").setStyle("display", "none");
        this.errorElement.setOpacity(0);
        this.errorElement.setStyle("display", "block");
        this.errorElement.tween('opacity', 1);
        this.active = false;
    },

    JPlayerCallback: function(obj) {
        this.initJPlayerEvents();
        if(this.active) return;

        this.active = true;
        $clear(this.loadTimer);

        this.interfaceElement.setStyle("display", "block");
        this.interfaceSize = this.interfaceElement.getSize( );
        this.playerSize = {x: this.options.playerWidth,
            y: this.options.playerHeight};
        
        this.playerElement.setStyle("display", "block");
        this.playerElement.setStyle("position", "relative");
        this.playerElement.setStyle("width", this.playerSize.x);
        this.playerElement.setStyle("height", this.playerSize.y);

        console.log("JPlayer Ready: ", obj);

        this.controlsElement.setStyle("display", "block");

        this.interfaceElement.set('tween', {'duration': 
            this.options.tweenDuration});
        this.interfaceElement.tween('opacity', 1);

        this.activateJPlayer.delay(this.options.tweenDuration * 1.5, this);
    },
    activateJPlayer: function( ) {
        this.swiff.object.width = this.options.playerWidth;
        this.swiff.object.height = this.options.playerHeight;
        if(!this.swiff.object.sendEvent) {
            console.log("No send event command detected, delaying...");
            this.activateJPlayer.delay(2000, this);
            return;
        }
        if(this.options.initialPlaylist) this.loadPlaylist(
            this.options.initialPlaylist
        );
        this.readCookie( );
    },
    fileToId: function(url) {
        var m = url.match(this.options.fileIdRegex);
        if(m) return m[1];
        return null;
    },

    playUrl: function(url, startPos, id3, autoPlay, tlist, video) {
        var purl = this.parseUrl(url)    
        var opts = null;
        var parts = purl.file.split(".");
        var path_parts = purl.path.split("/");
        var ext = parts[parts.length-1];

        var id2 = this.fileToId(purl.file);
        for(var i =0; i<this.playlist.length; i++) {
            var pl = this.playlist[i];
            var ppurl = this.parseUrl(pl.file);
            var id = this.fileToId(ppurl.file);
            if(id == id2) {
                console.log("File already in playlist - changing item instead "+
                    "of loading new.");
                this.sendEvent("ITEM", i);
                return;
            }
        }

        if(this.options.extensionRewrites[ext]) {
            var opt = this.options.extensionRewrites[ext];
            var proc = [];
            var last = null;
            for(k in opt[1]) {
                while(k > path_parts.length - 1) {
                    proc[proc.length-1] = this.options.defaultVideoSlug;
                    path_parts.push(path_parts[path_parts.length-1]);
                }
                var res = path_parts[k].match(opt[1][k]);
                if(res) proc.push(res[1]);
                last = k;
            }
            purl.path = '/'+proc.join('/')+"."+ext;
            if(opt[2]) purl.port = opt[2];
            if(opt[0]) opts = {'type': opt[0]};
        }

        var id = this.fileToId(purl.file);
        if(id) {
            var url = this.options.podcastImageUrl+id;
            if(opts) opts['image'] = url; else opts = {'image': url};
        }

        url = purl.protocol+"://"+purl.host+(purl.port ? ":"+purl.port: "")+
                purl.path+purl.query;

        if(opts) opts['file'] = url;
            else opts = url;
        console.log("Playing: ", opts);

        this.sendEvent('LOAD', opts);
        this.sendEvent('PLAY');
    },

    sendEvent: function(action, p1) {
        if(!this.active) return;

        console.log("JPlayer Sending Event: ", action, p1);
        this.swiff.object.sendEvent(action ,p1);
    },
    getPlaylist: function( ) {
        if(!this.active) return;

        console.log("JPlayer, asking for playlist: ", action, p1);
        JPlayers[this.options.JPlayer.ID].getPlaylist();
    },


    parseUrl: function(url) {
        var a =  document.createElement('a');
        a.href = url;
        return {
            source: url,
            protocol: a.protocol.replace(':',''),
            host: a.hostname,
            port: a.port,
            query: a.search,
            params: (function(){
                var ret = {},
                    seg = a.search.replace(/^\?/,'').split('&'),
                    len = seg.length, i = 0, s;
                for (;i<len;i++) {
                    if (!seg[i]) { continue; }
                    s = seg[i].split('=');
                    ret[s[0]] = s[1];
                }
                return ret;
            })(),
            file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
            hash: a.hash.replace('#',''),
            path: a.pathname.replace(/^([^\/])/,'/$1'),
            relative: (a.href.match(/tp:\/\/[^\/]+(.+)/) || [,''])[1],
            segments: a.pathname.replace(/^\//,'').split('/')
        };
    }


});
