/**
 * @fileOverview Classes and code run on the document.ready event.
 * @author       Dean Hall
 * @version      1.2
 */

(function($) {

    var article_manager = nav_link_manager = audio_mood_manager = video_manager = null;

    var _debug = function(s) {
        try { console.debug(s); } catch(e) { }
    };

    var assert = function(val) {
        try {
            if (!val) throw 'EXCEPTION: assert: ' + this.caller.toString();
        }
        catch (e) { _debug('assert not supported'); }
    };

    var transform_button_rect = function($node) {
        $node.replaceWith(
            $(document.createElement('div'))
                .attr('id', $node.attr('id'))
                .addClass('button-rect')
                .append(
                    $(document.createElement('div')).addClass('left')
                )
                .append(
                    $(document.createElement('div'))
                        .addClass('middle')
                        .append($(document.createElement('a')).text($node.text()))
                )
                .append($(document.createElement('div')).addClass('right'))
        );
        return $node;
    };

    /**
     * an enumeration of possible media states
     */
    var media_states = {
        INACTIVE : 1,
        ACTIVE   : 2,
        LOADING  : 3,
        PLAYING  : 4,
        PAUSED   : 5,
        ERROR    : 6
    };

    /**
     * abstract class activation_manager
     */
    var activation_manager_abstract_class = {
        bucket    : null,
        active_id : '',

        /**
         * activation_manager::init
         */
        init : function(id) {
            _debug('activation_manager::init: ABSTRACT METHOD');
        },

        /**
         * activation_manager::id_exists
         */
        id_exists : function(id) {
            if (!id)                return false;
            if (!id in this.bucket) return false;
            if (!this.bucket[id])   return false;

            return true;
        },

        /**
         * activation_manager::activate
         */
        activate : function(id) {
            var old_id = this.active_id;

            if (this.id_exists(id)) {
                this.active_id = id;

                if (this.bucket[id].activate)
                    this.bucket[id].activate();
            }

            this.deactivate(old_id);
        },

        /**
         * activation_manager::deactivate
         */
        deactivate : function(id) {
            if (this.id_exists(id) && this.bucket[id].deactivate)
                this.bucket[id].deactivate();
        },

        /**
         * activation_manager::get_current
         */
        get_current : function() {
            if (this.active_id && this.bucket[this.active_id])
                return this.bucket[this.active_id];

            return null;
        }
    };

    /**
     * class article_manager
     *
     * This instantiates article objects and
     * dispatches relevant events to them.
     */
    var article_manager_class = function(id) {
        this.init(id);
    };

    $.extend(
        article_manager_class.prototype,
        activation_manager_abstract_class,
        /** @lends article_manager_class.prototype */
        {
            /**
             * article_manager::init
             */
            init : function(id) {
                this.bucket = { };

                // Find all article nodes and create new article objects.
                $('.article').each($.proxy(function(i, a_node) {
                    var a = new article_class($(a_node));

                    this.bucket[a.id] = a;

                    // Activate or deactivate it depending on the initial active article.
                    if (a.id == id)
                        this.activate(a.id);
                    else
                        this.deactivate(a.id);
                }, this));

                // Listen to clicks on all nav links to show/hide articles.
                $('.nav-link').bind('click', $.proxy(this.click, this));
            },

            /**
             * article_manager::click
             * event-handler for nav-link click
             */
            click : function(event) {
                assert(event.currentTarget.mlp.article_id);
                assert(this.active_id);

                var aid = event.currentTarget.mlp.article_id;

                if (this.active_id != aid)
                    this.activate(aid);

                // This event should bubble.
                return true;
            }
        }
    );


    /**
     * class nav_link_manager
     *
     * This instantiates nav_link objects and
     * dispatches relevant events to them.
     */
    var nav_link_manager_class = function(article_id) {
        this.init(article_id);
    };

    $.extend(
        nav_link_manager_class.prototype,
        activation_manager_abstract_class,
        /** @lends nav_link_manager_class.prototype */
        {
            /**
             * nav_link_manager::init
             */
            init : function(article_id) {
                this.bucket = { };

                var id = article_id + '-link';

                $('.nav-link').each($.proxy(function(i, item) {
                    var nl = new nav_link_class($(item));

                    this.bucket[nl.id] = nl;

                    if (nl.id == id)
                        nl.$.click();
                    else
                        this.deactivate(nl.id);

                    nl.$.bind('click', $.proxy(this.click, this));
                }, this));
            },

            /**
             * nav_link_manager::click
             * event-handler for nav_link click
             */
            click : function(event) {
                assert(event.currentTarget.mlp.id);
                assert(this.active_id);

                var nlid = event.currentTarget.mlp.id;
                if (this.active_id != nlid)
                    this.activate(nlid);

                // This event should bubble.
                return true;
            }
        }
    );


    /**
     * class audio_mood_manager
     *
     * This instantiates audio_mood objects and
     * dispatches relevant events to them.
     */
    var audio_mood_manager_class = function(player) {
        this.init(player);
    };

    $.extend(
        audio_mood_manager_class.prototype,
        activation_manager_abstract_class,
        /** @lends {audio_mood_manager_class.prototype} */
        {
            /**
             * audio_mood_manager::init
             */
            init : function(player) {
                this.bucket = { };

                $('#audio li.mood').each($.proxy(function(i, li) {
                    var m = new audio_mood_class($(li), player);

                    this.bucket[m.id] = m;

                    m.$header.bind('click', $.proxy(this.click, this));
                }, this));
            },

            /**
             * audio_mood_manager::click
             * event-handler for click
             */
            click : function(event) {
                var mood = event.currentTarget.mlp;

                if (!this.active_id || (this.active_id != mood.id))
                    this.activate(mood.id);
            }
        }
    );


    /**
     * class video_manager_class
     *
     * This simply instantiates video_clip objects.
     */
    var video_manager_class = function(player) {
        this.$      = $('#video');
        this.node   = this.$.get(0);
        this.id     = this.$.attr('id');
        this.player = player;

        this.$.find('.video').each($.proxy(function(i, li) {
            new video_clip_class(new video_button_class($(li)), this.player);
        }, this));
    };


    /**
     * abstract class media_player
     */
    var media_player_abstract_class = {
        $play_space : null,
        clip        : null,
        html5       : false,
        $media      : null,
        is_paused   : false,
        is_active   : false,
        volume      : 0.6,
        $error_msg  : null,

        SPACE_ID : '',
        HTML5_ID : '',
        FLASH_ID : '',

        /**
         * media_player::init
         */
        init : function() {
            _debug('media_player::init: ABSTRACT METHOD');
        },

        /**
         * media_player::init_article_handler
         */
        init_article_handler : function(article_id, event_ns) {
            var handlers = { };

            handlers['article-activating.'   + event_ns] = $.proxy(this.activate, this);
            handlers['article-deactivating.' + event_ns] = $.proxy(this.deactivate, this);

            article_manager.bucket[article_id].$.bind(handlers);
        },

        /**
         * media_player::init_play_space
         */
        init_play_space : function() {
            this.$play_space =
                $(document.createElement('div'))
                    .addClass('play-space')
                    .attr('id', this.SPACE_ID)
                    .css('display', 'none');
        },

        /**
         * media_player::init_error_msg
         */
        init_error_msg : function() {
            this.$error_msg =
                $(document.createElement('p'))
                    .addClass('message')
                    .text('Sorry! Something went wrong when trying to play this video.');
        },

        /**
        /* media_player::show
         */
        show : function() {
            this.$play_space.show();
        },

        /**
         * media_player::hide
         */
        hide : function() {
            this.$play_space.hide();
        },

        /**
         * media_player::play
         */
        play : function() {
            if (this.$media)
                this.$media.get(0).play();

            this.is_paused = false;
        },

        /**
         * media_player::pause
         */
        pause : function() {
            if (this.$media)
                this.$media.get(0).pause();

            this.is_paused = true;
        },

        /**
         * media_player::activate
         * event-handler for article-activating
         */
        activate : function() {
            //_debug('media_player::activate: INVOKED');
            this.is_active = true;
            this.is_paused = false;

            return false;
        },

        /**
         * media_player::deactivate
         * event-handler for article-deactivating
         */
        deactivate : function(event_ns) {
            //_debug('media_player::deactivate');
            this.is_active = false;
            this.is_paused = true;

            if (!this.clip || !this.$media) return;

            var m = this.$media.get(0);

            if (m.readyState == m.HAVE_ENOUGH_DATA) {
                //_debug('media_player::deactivate: pausing immediately');
                m.pause();
            }

            return false;
        },

        /**
         * media_player::spawn_prep
         */
        spawn_prep : function(old_clip, new_clip) {
            _debug('media_player::spawn_prep: ABSTRACT METHOD');
        },

        /**
         * media_player::prep_play_space
         */
        prep_play_space : function(new_clip) {
            _debug('media_player::spawn_prep_play_space: ABSTRACT METHOD');
        },

        /**
         * media_player::handle_clip_change
         */
        handle_clip_change : function(new_clip) {
            _debug('media_player::handle_clip_change: ABSTRACT METHOD');
        },

        /**
         * media_player::spawn
         */
        spawn : function(new_clip) {
            //_debug('media_player::spawn: INVOKED');

            if (this.clip)
                this.clip.set_state(media_states.INACTIVE);

            this.handle_clip_change(new_clip);

            this.unload();
            this.$play_space.css('display', 'none');

            this.clip = new_clip;
            this.clip.set_state(media_states.ACTIVE);

            this.prep_play_space();

            if (this.html5)
                this.spawn_html();
            else
                this.spawn_flash();
        },

        /**
         * media_player::create_html_element
         */
        create_html_element : function() {
            this.$media =
                $(document.createElement(this.ELEMENT))
                    .attr({
                        title      : this.clip.button.title,
                        controls   : 'controls',
                        id         : this.HTML5_ID,
                        autobuffer : 'autobuffer'
                    })
                    .bind({
                        'canplaythrough.mp' : $.proxy(function(event) {
                            this.show();
                            if (this.is_paused)
                                this.clip.set_state(media_states.PAUSED);
                            else
                                this.play();
                        }, this),
                        'loadstart.mp' : $.proxy(function(event) {
                            this.clip.set_state(media_states.LOADING);
                        }, this),
                        'play.mp' : $.proxy(function(event) {
                            this.clip.set_state(media_states.PLAYING);
                        }, this),
                        'pause.mp' : $.proxy(function(event) {
                            this.clip.set_state(media_states.PAUSED);
                        }, this),
                        'ended.mp' : $.proxy(function(event) {
                            this.clip.set_state(media_states.PAUSED);
                            this.hide();
                        }, this)
                    })
                    .bind('dataunavailable.mp empty.mp error.mp', $.proxy(function(event) {
                        this.trash_media();
                        this.$media = null;

                        this.clip.set_state(media_states.ERROR);
                        this.$play_space
                            .append(this.$error_msg)
                            .append(this.clip.button.$fallback)
                            .show();
                    }, this));

            for (ext in this.clip.files) {
                //_debug('appending: ' + this.clip.files[ext].url + '; ' + this.clip.files[ext].mime_type);
                this.$media
                    .append(
                        $(document.createElement('source'))
                            .attr({
                                src  : this.clip.files[ext].url,
                                type : this.clip.files[ext].mime_type
                            })
                    );
            }

            this.$media
                .append(this.clip.button.$fallback)

            this.$media.get(0).volume = this.volume;
        },

        /**
         * media_player::amend_html_element
         */
        amend_html_element : function() {
            _debug('media_player::amend_html_element: ABSTRACT METHOD');
        },

        /**
         * media_player::spawn_html
         */
        spawn_html : function() {
            this.create_html_element();
            this.amend_html_element();
            this.$play_space.prepend(this.$media);
        },

        /**
         * media_player::embed_flash
         */
        embed_flash : function() {
            _debug('media_player::embed_flash: ABSTRACT METHOD');
        },

        /**
         * media_player::spawn_flash
         */
        spawn_flash : function() {
            _debug('media_player::spawn_flash: INVOKED');
            this.clip.set_state(media_states.PAUSED);

            this.$play_space
                .prepend(
                    $(document.createElement('div'))
                        .attr('id', this.FLASH_ID)
                        .append(this.clip.button.$fallback)
                )
                .show();

            this.embed_flash();
        },

        /**
         * media_player::unload
         */
        unload : function() {
            //_debug('media_player::unload: INVOKED');

            this.hide();

            if (this.clip) {
                this.clip.set_state(media_states.INACTIVE);
                this.clip = null;
            }

            this.$error_msg.detach();
            this.$play_space.empty().detach();

            if (this.$media) {
                this.$media.hide().detach();
                this.trash_media();
                this.$media = null;
            }
        },

        /**
         * media_player::trash_media
         */
        trash_media : function() {
            if (!this.$media) return;

            this.$media
                .detach()
                .unbind('canplaythrough dataunavailable empty ended error loadstart pause play progress volumechange');

            var m = this.$media.get(0);
            m.volume = 0;

            if ((m.readyState == m.HAVE_ENOUGH_DATA) || m.error) {
                this.$media.remove();
            }
            else {
                this.$media.bind('canplaythrough dataunavailable empty error', $.proxy(function(event) {
                    //_debug('media_player::trash_media: trashing media (callback)');
                    this.remove();
                    return false;
                }, this.$media));
            }
        },
    };


    /**
     * class audio_player
     */
    var audio_player_class = function() {
        this.init();
    };

    $.extend(
        audio_player_class.prototype,
        media_player_abstract_class,
        /** @lends audio_player_class.prototype */
        {
            ARTIST    : 'Mocha Lab Productions',
            SPACE_ID  : 'audio-player',
            HTML5_ID  : 'audio-html-player',
            FLASH_ID  : 'audio-flash-player',
            ELEMENT   : 'audio',
            FLASH_SWF : 'http://cloud.foomg.com/common/flash/audio-player/2.0/player.swf',

            /**
             * audio_player::init
             */
            init : function() {
                this.html5 = (
                    Modernizr.audio.ogg == 'probably'
                        ||
                    Modernizr.audio.m4a == 'probably'
                        ||
                    Modernizr.audio.mp3 == 'probably'
                );

                this.init_article_handler('audio', 'ap');
                this.init_play_space();
                this.init_error_msg();

                if (!this.html5) {
                    AudioPlayer.setup(this.FLASH_SWF, {
                        bg                : '999999',
                        border            : 'b9b9b9',
                        initialvolume     : 60,
                        leftbg            : 'f5f5f5',
                        lefticon          : '505050',
                        loader            : '65cbfe',
                        rightbg           : 'b9b9b9',
                        rightbghover      : '505050',
                        righticon         : '505050',
                        righticonhover    : 'b9b9b9',
                        track             : 'f5f5f5',
                        transparentpagebg : 'yes',
                        text              : '505050',
                        track             : 'f5f5f5',
                        tracker           : 'b9b9b9',
                        voltrack          : 'b9b9b9',
                        volslider         : '505050',
                        width             : 290,
                    });
                }
            },

            /**
             * audio_player::handle_mood_nav
             * event-handler for audio_mood:mood-deactivating
             */
            handle_mood_nav : function() {
                //_debug('audio_player::handle_mood_nav: INVOKED');

                audio_mood_manager.get_current().$.unbind('mood-deactivating');
                this.hide();

                if (this.clip)
                    this.clip.set_state(media_states.INACTIVE);

                this.unload();
            },

            /**
             * audio_player::handle_clip_change
             */
            handle_clip_change : function(new_clip) {
                //_debug('audio_player::handle_clip_change: INVOKED');

                if (!this.clip || (this.clip.parent_mood != new_clip.parent_mood)) {
                    audio_mood_manager.get_current().$
                        .bind('mood-deactivating.ap', $.proxy(this.handle_mood_nav, this));
                }
            },

            /**
             * audio_player::prep_play_space
             */
            prep_play_space : function() {
                //_debug('audio_player::prep_play_space');
                this.clip.parent_mood.$.append(this.$play_space);
            },

            /**
             * audio_player::amend_html_element
             */
            amend_html_element : function() { },

            /**
             * audio_player::embed_flash
             */
            embed_flash : function() {
                AudioPlayer.embed(this.FLASH_ID, {
                    soundFile : this.clip.files.mp3.url,
                    titles    : this.clip.button.title,
                    artists   : this.ARTIST,
                    autostart : this.is_paused ? 'no' : 'yes'
                });
            }
        }
    );


    var video_player_class = function() {
        this.init();
    };

    $.extend(
        video_player_class.prototype,
        media_player_abstract_class,
        /** @lends {video_player_class.prototype} */
        {
            $parent     : null,
            $play_space : null,

            SPACE_ID  : 'video-player',
            HTML5_ID  : 'video-html-player',
            FLASH_ID  : 'video-flash-player',
            ELEMENT   : 'video',
            FLASH_SWF : 'http://cloud.foomg.com/common/flash/jw-media-player/5.1/player.swf',

            /**
             * video_player::init
             */
            init : function() {
                this.html5    = ( Modernizr.video.ogg == 'probably' || Modernizr.video.h264 == 'probably' );

                this.init_article_handler('video', 'vp');
                this.init_play_space();
                this.init_error_msg();

                this.$parent = $('#video');
            },

            /**
             * video_player::handle_clip_change
             */
            handle_clip_change : function(old_clip, new_clip) { },

            /**
             * video_player::prep_play_space
             */
            prep_play_space : function() {
                this.$parent.append(this.$play_space);
            },

            /**
             * video_player::amend_html_element
             */
            amend_html_element : function() {
                this.$media.attr('poster', this.clip.poster.url);
            },

            /**
             * video_player::embed_flash
             */
            embed_flash : function() {
                _debug('video_player::embed_flash: INVOKED');
                _debug('width: ' + this.clip.poster.width + '; height: ' + this.clip.poster.height);
                swfobject.embedSWF(
                    this.FLASH_SWF, this.FLASH_ID, this.clip.poster.width, this.clip.poster.height, '9', false,
                    {
                        'image'     : this.clip.poster.url,
                        'file'      : this.clip.files.m4v.url,
                        'volume'    : '60',
                        'autostart' : this.is_paused ? 'false' : 'true'
                    }
                );
            }
        }
    );


    /**
     * class article
     *
     * This represents an article (div.article) element.
     * It only manages showing and hiding itself.
     *
     */
    var article_class = function($article) {
        this.init($article);
    };

    $.extend(
        article_class.prototype,
        /** @lends {article_class.prototype} */
        {
            $    : null,
            node : null,
            id   : '',

            /**
             * article::init
             */
            init : function($article) {
                this.$        = $article;
                this.node     = this.$.get(0);
                this.node.mlp = this;
                this.id       = this.$.attr('id');

                this.$
                    .find('h2').remove().end()
                    .find('.top-link').remove();
            },

            /**
             * article::activate
             */
            activate : function() {
                this.$.show();
                this.$.trigger('article-activating');
            },

            /**
             * article::deactivate
             */
            deactivate : function() {
                this.$.hide();
                this.$.trigger('article-deactivating');
            },
        }
    );


    /**
     * class nav_link
     *
     * This represents a navigation (#nav a) element.
     * It only handles changing its appearance on clicks.
     *
     */
    var nav_link_class = function($link) {
        this.init($link);
    };

    $.extend(
        nav_link_class.prototype,
        /** @lends {nav_link_class.prototype} */
        {
            $          : null,
            node       : null,
            id         : '',
            $link      : null,
            item       : null,
            title      : '',
            article_id : '',

            /**
             * nav_link::init
             */
            init : function($item) {
                this.$          = $item;
                this.node       = this.$.get(0);
                this.node.mlp   = this;
                this.id         = this.$.attr('id');
                this.$link      = this.$.find('a:first');
                this.href       = this.$link.attr('href');
                this.title      = this.$link.text();
                this.article_id = this.id.replace(/-link$/, '');

                transform_button_rect(this.$link);
            },

            /**
             * nav_link::activate
             */
            activate : function() {
                document.location = this.href;
                this.$.addClass('active');

                $(document).find('head title:first').replaceWith(
                    $(document.createElement('title')).text(this.title + ' » Mocha Lab Productions')
                );
            },

            /**
             * nav_link::deactivate
             */
            deactivate : function() {
                this.$.removeClass('active');
            }
        }
    );


    /**
     * class audio_mood
     *
     * This represents an audio section (.mood).
     * It only handles hiding and showing its list
     * of clips and styling itself.
     *
     */
    var audio_mood_class = function($mood, player) {
        this.init($mood, player);
    };

    $.extend(
        audio_mood_class.prototype,
        /** @lends {audio_mood_class.prototype} */
        {
            $       : null,
            node    : null,
            id      : '',
            $header : null,

            /**
             * audio_mood::init
             */
            init : function($mood, player) {
                this.$        = $mood;
                this.node     = this.$.get(0);
                this.node.mlp = this;
                this.id       = this.$.attr('id');
                this.$header  = this.$.find('.header:first h3:first');

                // Give the header a reference to this object so it can be
                // referenced easily in event handlers.
                this.$header.get(0).mlp = this;

                this.$header
                    .append($(document.createElement('span')).addClass('select'))
                    .attr('title', 'Listen to ' + this.$header.text().toLowerCase() + ' audio samples.');

                this.$.find('.clip-list .clip').each($.proxy(function(i, li) {
                    new audio_clip_class(new audio_button_class($(li)), this, player);
                }, this));
            },

            /**
             * audio_mood::activate
             */
            activate : function() {
                this.$.addClass('active');
            },

            /**
             * audio_mood::deactivate
             */
            deactivate : function() {
                this.$.removeClass('active');
                this.$.trigger('mood-deactivating');
            }
        }
    );


    /**
     * class video_poster
     *
     * This represents the metadata for a video's poster image.
     */
    var video_poster_class = function($img) {
        this.init($img);
    };

    $.extend(
        video_poster_class.prototype,
        /** @lends {video_poster_class.prototype} */
        {
            url    : '',
            height : '',
            width  : '',

            /**
             * video_poster::init
             */
            init : function($img) {
                this.url    = $img.attr('src');
                this.height = $img.attr('height') || $img.css('height');
                this.width  = $img.attr('width')  || $img.css('width');
            }
        }
    );

    /**
     * class media_file
     *
     * This represents the metadata for a media file.
     */
    var media_file_class = function(url, mime_type) {
        this.init(url, mime_type);
    };

    $.extend(
        media_file_class.prototype,
        /** @lends {media_file_class.prototype} */
        {
            url       : '',
            mime_type : '',

            /**
             * media_file::init
             */
            init : function(url, mime_type) {
                this.url       = url;
                this.mime_type = mime_type;
            }
        }
    );


    /**
     * @lends audio_clip_class.prototype
     * @lends video_clip_class.prototype
     */
    var media_clip_abstract_class = {
        button : null,
        player : null,
        state  : media_states.INACTIVE,
        files  : null,

        /**
         * media_clip::init_button
         */
        init_button : function(button) {
            //_debug('media_clip::init_button: INVOKED');
            this.button = button;
            this.button.$.bind('click', $.proxy(this.click, this));
        },

        /**
         * media_clip::init_files
         */
        init_files : function(list, selector) {
            //_debug('media_clip::init_button: INVOKED');
            this.files = { };

            for (var ext in list) {
                //_debug('media_clip::init_files: selector: ' + selector + '.' + ext + ':first');
                //_debug(this.button.$.find(selector + '.' + ext + ':first'));
                this.files[ext] = new media_file_class(this.button.$.find(selector + '.' + ext + ':first').attr('href'), list[ext]);
            }
        },

        /**
         * media_clip::set_state
         */
        set_state : function(state) {
            this.state = state;

            switch (state) {
                case media_states.ACTIVE :
                    //_debug('media_clip[' + this.button.id + ']::set_state: state is now: ACTIVE');
                    this.button.activate();
                    break;
                case media_states.LOADING :
                    //_debug('media_clip[' + this.button.id + ']::set_state: state is now: LOADING');
                    this.button.show_loading();
                    break;
                case media_states.PLAYING :
                    //_debug('media_clip[' + this.button.id + ']::set_state: state is now: PLAYING');
                    this.button.show_playing();
                    break;
                case media_states.PAUSED :
                    //_debug('media_clip[' + this.button.id + ']::set_state: state is now: PAUSED');
                    this.button.show_paused();
                    break;
                case media_states.INACTIVE :
                    //_debug('media_clip[' + this.button.id + ']::set_state: state is now: INACTIVE');
                    if (this.state != media_states.ERROR)
                        this.button.deactivate();
                    break;
                case media_states.ERROR :
                    //_debug('media_clip[' + this.button.id + ']::set_state: state is now: ERROR');
                    this.button.show_error();
                    break;
                default :
                    _debug('media_clip[' + this.button.id + ']::set_state: state is now: UNDEFINED');
            }
        },

        /**
         * media_clip::click
         * event-handler for this.button:click
         *
         * 'this' is this object.
         */
        click : function(event) {
            // The player is currently loading/playing this clip; change my state.
            if (this.player.clip == this) {
                switch (this.state) {

                    case media_states.PLAYING :
                        //_debug('media_clip::click: pausing');
                        this.player.pause();
                        this.set_state(media_states.PAUSED);
                        break;

                    case media_states.PAUSED :
                        //_debug('media_clip::click: playing');
                        this.player.play();
                        this.set_state(media_states.PLAYING);
                        break;

                    default :
                        _debug('media_clip::click: ' + this.button.id + ': clicked on a non-clickable state: ' + this.state);
                }
            }
            // Tell the player to load and play this clip.
            else {
                this.player.spawn(this, this);
            }
        }
    };


    /**
     * class audio_clip
     *
     */
    var audio_clip_class = function(button, parent_mood, player) {
        this.init(button, parent_mood, player);
    };

    $.extend(
        audio_clip_class.prototype,
        media_clip_abstract_class,
        /** @lends {audio_clip_class.prototype} */
        {
            parent_mood    : null,

            /**
             * audio_clip::init
             */
            init : function(button, parent_mood, player) {
                this.parent_mood = parent_mood;
                this.player      = player;

                this.init_button(button);
                this.init_files({ oga : 'audio/ogg', m4a : 'audio/m4a', mp3 : 'audio/mpeg' }, 'a.audio-file');
                this.button.init_button();
            }
        }
    );


    /**
     * class video_clip
     *
     */
    var video_clip_class = function(button, player) {
        this.init(button, player);
    };

    $.extend(
        video_clip_class.prototype,
        media_clip_abstract_class,
        /** @lends {video_clip_class.prototype} */
        {
            poster : null,

            /**
             * video_clip::init
             */
            init : function(button, player) {
                this.player = player;

                this.init_button(button);
                this.init_files({ ogv : 'video/ogg', m4v : 'video/mp4' }, 'a.video-file');

                var $p = this.button.$.find('img:first');
                this.poster = new video_poster_class($p);
                $p.remove();

                this.button.init_button();
            }
        }
    );

    /** 
     * abstract class media_button
     *
     * @lends audio_button_class.prototype
     * @lends video_button_class.prototype
     */
    var media_button_abstract_class = {
        $         : null,
        node      : null,
        id        : '',
        num       : NaN,
        title     : '',
        href      : '',
        $fallback : null,
        $link     : null,
        $num      : null,
        $activity : null,

        /**
         * media_button::init_node
         */
        init_node : function($li) {
            //_debug('media_button::init_node:: INVOKED');
            this.$        = $li;
            this.node     = this.$.get(0);
            this.node.mlp = this;
            this.id       = this.$.attr('id');
        },

        /**
         * media_button::init_button
         */
        init_button : function() {
            //_debug('media_button::init_button::INVOKED');
            //_debug(this.$);
            this.$num      = $(document.createElement('span')).addClass('num').text(this.num);
            this.$activity = $(document.createElement('span')).addClass('activity');

            this.$link =
                $(document.createElement('a'))
                    //.attr('href',  this.href)
                    .attr('title', this.title)
                    .append(this.$num)
                    .append(this.$activity);

            this.$
                .empty()
                .addClass('button')
                .append(this.$link);
        },

        /**
         * media_button::activate
         */
        activate : function() {
            this.$link.addClass('active');
            this.$activity.show();
        },

        /**
         * media_button::deactivate
         */
        deactivate : function() {
            this.$link.removeClass('active');
            this.$activity.hide().removeClass('loading playing paused error');
        },

        /**
         * media_button::show_loading
         */
        show_loading : function() {
            this.$activity
                .removeClass('playing paused error')
                .addClass('loading');
        },

        /**
         * media_button::show_playing
         */
        show_playing : function() {
            this.$activity
                .removeClass('loading paused error')
                .addClass('playing');
        },

        /**
         * media_button::show_paused
         */
        show_paused : function() {
            this.$activity
                .removeClass('loading playing error')
                .addClass('paused');
        },

        /**
         * media_button::show_error
         */
        show_error : function() {
            this.$activity
                .removeClass('loading playing paused')
                .addClass('error');
        }
    };


    /**
     * class audio_button
     *
     * This represents an audio clip list item/button (li.clip).
     * It instantiates and displays a button and provides an
     * interface to control its appearance (through this.show_*).
     *
     */
    var audio_button_class = function($li) {
        this.init($li);
    };

    $.extend(
        audio_button_class.prototype,
        media_button_abstract_class,
        /** @lends {audio_button_class.prototype} */
        {
            mood : '',

            /**
             * audio_button::init
             */
            init : function($li) {
                this.init_node($li);

                this.mood = this.id.replace(/^mood-([^-]+)-\d+$/, "$1");
                this.num  = this.id.replace(/^mood-[^-]+-(\d+)$/, "$1");

                this.title =
                    this.mood.replace(/^(\w).*$/, "$1").toUpperCase() +
                        this.mood.replace(/^\w(\w+)$/, "$1") +
                        ' Sample #' + this.num;

                this.$fallback =
                    $(document.createElement('div'))
                        .addClass('no-js')
                        .append(this.$.find('ul:first')
                        .clone(false));
            },
        }
    );


    /**
     * class video_button
     *
     * This represents a video clip list item/button (li.clip).
     * It instantiates and displays a button and provides an
     * interface to control its appearance (through this.show_*).
     *
     */
    var video_button_class = function($li) {
        this.init($li);
    };

    $.extend(
        video_button_class.prototype,
        media_button_abstract_class,
        /** @lends {video_button_class.prototype} */
        {
            /**
             * video_button::init
             */
            init : function($li) {
                //_debug('video_button::init: INVOKED');
                this.init_node($li);

                this.num   = this.id.replace(/^video-(\d+)$/, "$1");
                this.title = "Video #" + this.num;

                this.$fallback =
                    $(document.createElement('div'))
                        .addClass('no-js')
                        .append(this.$.contents().clone(false));
            }
        }
    );


    $(function() {

        var aid = window.location.hash.replace(/^#/, '');
        if (!aid || (aid == 'top'))
            aid = 'about';

        article_manager    = new article_manager_class(aid);
        nav_link_manager   = new nav_link_manager_class(aid);
        audio_mood_manager = new audio_mood_manager_class(new audio_player_class());
        video_manager      = new video_manager_class(new video_player_class());

    });
})(jQuery);

