jQuery.fn.setupExtras = function(setup, options) {
  for(extra in setup) {
    var self = this;
    if(setup[extra] instanceof Array) {
      for(var i=0; i<setup[extra].length; i++)
        setup[extra][i].call(self, options);
    } else {
      setup[extra].call(self, options);
    }
  }
};

jQuery(function($) {
  
  var $$, defaults, selectors, util;
  
  $$ = function(param) {
    var id = $.data($(param)[0]);
    return $.cache[id];
  };
  
  selectors = {
    PLAYER: 'div.trackplayer',
    PROGRESS_PARENT: 'ul.progress',
    LOAD_PROGRESS: 'ul li.load',
    PLAY_PROGRESS: 'ul li.play',
    PLAY_BUTTON: 'a.trackplaypause',
    START_TIME: 'li.start',
    END_TIME: 'li.end'
  };
  
  defaults = {
    SWF_PATH: "/megaphone/flash/build/megaphone.swf",
    SWF_ID: 'sound_swf',
    selectors: selectors
  };
  
  util = {
    formatSeconds: function(seconds) {
      var minutes, seconds;
      
      minutes = Math.floor(seconds/60).toString();
      seconds = (seconds % 60).toString();
      
      if (seconds.length == 1) {
        seconds = '0' + seconds;
      }
      return minutes + ':' + seconds;
    },
    formatPercent: function(percent) {
      return (Math.round(percent * 1000) / 10).toString();
    },
    route: function(id, event, memo) {
      $('#' + id).trigger(event, [memo]);
    }
  };
  
  $.fn.megaphone = function(overrides) {

    overrides = overrides || {};
    
    var setupDefault = {
      setup: $.fn.megaphone.base
    };
    
    var options = $.extend(true, {}, setupDefault, defaults, overrides);

    if ($('#' + options.SWF_ID).length == 0) {
      $('body').append('<div id="' + options.SWF_ID + '"></div>');      
      setTimeout(function() {
        swfobject.embedSWF(options.SWF_PATH, options.SWF_ID, "1", "1", "9.0.0", null, {}, {wmode:'transparent'});
      }, 300);
    }

    this.setupExtras(options.setup, options);

    this.each(function() {
      $(this).trigger('initialize.megaphone');
    });

    return this;

  };

  $.fn.megaphone.instances = [];
  
  var trigger = function(options, event, data) {
    $('#' + options.SWF_ID)[0].trigger(event, data);
  };
  
  $.fn.megaphone.base = {
    initialize: [function(options) {
      this.bind('initialize.megaphone', function() {
        if(!$(this).attr('id')) {
          $(this).attr('id', 'megaphone_element_' + $.fn.megaphone.instances.length.toString());
        }
        $.fn.megaphone.instances.push(this);
        $(this).addClass('stopped');
      });
    }],

    loading: [
      function(options) {
        this.bind('loading.megaphone', function(event, memo) {
          $(this).removeClass('stopped').addClass('loading');
        });
      }
    ],

    loadComplete: [
      function(options) {
        this.bind('loadComplete.megaphone', function(event, memo) {
          $(this).removeClass('loading');
        });
      }
    ],

    playing: [
      function(options) {
        this.bind('playing.megaphone', function(event, memo) {
          $(this).addClass('playing').removeClass('paused stopped');
        });
      }
    ],

    paused: [function(options) {
      this.bind('paused.megaphone', function(event, memo) {
        $(this).removeClass('playing').addClass('paused');
      });
    }],

    stopped: [
      function(options) {
        this.bind('stopped.megaphone', function(event, memo) {
          $(this).addClass('stopped').removeClass('playing paused loading');
        });
      }
    ],
    
    playComplete: [
      function(options) {
        this.bind('playComplete.megaphone', function(event, memo) {
          $(this).addClass('stopped').removeClass('playing paused loading');
        });
      }
    ]
  };

  $.fn.megaphone.util = util;
  $.fn.megaphone.selectors = selectors;
  $.fn.megaphone.defaults = defaults;

  $.fn.megaphone.single = $.extend({}, $.fn.megaphone.base);

  $.fn.megaphone.single.loading.push(function(options) {
    this.bind('loading.megaphone', function(event, memo) {
      $(options.selectors.LOAD_PROGRESS, this).css('width', util.formatPercent(memo.loadPercent) + '%');
      $(options.selectors.START_TIME, this).text(util.formatSeconds(memo.playSeconds));
      $(options.selectors.END_TIME, this).text(util.formatSeconds(memo.totalSeconds));
    });
  });

  $.fn.megaphone.single.playButton = [
    function(options) {
      this.bind('initialize.megaphone', function() {
        var path, id;
        path = $(this).attr('data-mp3-path');
        id = $(this).attr('id');

        $(options.selectors.PLAY_BUTTON, this).click(function(event) {
          var player = $(this).closest(options.selectors.PLAYER);
          if($(player).hasClass('playing')) {
            trigger(options, 'pause.megaphone', {id: id, path: path});
          } else {
            trigger(options, 'pauseAll.megaphone');
            trigger(options, 'play.megaphone', {id: id, path: path});
          }
          event.target.blur();
          return false;
        });
      });
    }
  ];

  $.fn.megaphone.single.playComplete.push(function(options) {
    this.bind('playComplete.megaphone', function(event, memo) {
      $(options.selectors.PLAY_PROGRESS + ',' + options.selectors.LOAD_PROGRESS, this).css('width', '0%');
    });
  });

  $.fn.megaphone.single.stopped.push(function(options) {
    this.bind('stopped.megaphone', function(event, memo) {
      $(options.selectors.PLAY_PROGRESS + ',' + options.selectors.LOAD_PROGRESS, this).css('width', '0%');
    });
  });

  $.fn.megaphone.single.playing.push(function(options) {
    this.bind('playing.megaphone', function(event, memo) {
      $(options.selectors.PLAY_PROGRESS, this).css('width', util.formatPercent(memo.playPercent) + '%');
      $(options.selectors.START_TIME, this).text(util.formatSeconds(memo.playSeconds));
      $(options.selectors.END_TIME, this).text(util.formatSeconds(memo.totalSeconds));
    });
  });

  $.fn.megaphone.single.loadComplete.push(function(options) {
    this.bind('loadComplete.megaphone', function(event, memo) {
      $(options.selectors.LOAD_PROGRESS, this).css('width', util.formatPercent(memo.loadPercent) + '%');
      $(options.selectors.START_TIME, this).text(util.formatSeconds(memo.playSeconds));
      $(options.selectors.END_TIME, this).text(util.formatSeconds(memo.totalSeconds));
    });
  });

  $.fn.megaphone.single.progressClick = [
    function(options) {
      this.each(function(index, element) {
        var method = function(event) {
          var id, progress, offset, width, ratio, percent;
        
          id = $(this).closest(options.selectors.PLAYER).attr('id');
          progress = $(this).closest(options.selectors.PROGRESS_PARENT);
          offset = progress.offset();
          width = progress.width();
          ratio = ((event.pageX - offset.left) / width);
          percent = Math.round(ratio * 1000) / 10;

          trigger(options, 'play.megaphone', {id: id, percent: percent});

          return false;
        };
        
        $(options.selectors.PROGRESS_PARENT, this).click(method);        
      });
    }
  ];

  $.fn.megaphone.multi = $.extend({}, $.fn.megaphone.base);
  
  $.fn.megaphone.multi.progressClick = $.fn.megaphone.single.progressClick;

  $.fn.megaphone.multi.trackClick = [
    function(options) {
      this.each(function() {
        $(this).click(function(event) {
          var path, id, player;
          player = $(this).closest(options.selectors.PLAYER);
          path = $(player).attr('data-mp3-path');
          id = $(player).attr('id');
          if($(player).hasClass('playing')) {
            trigger(options, 'stopAll.megaphone');
            // trigger(options, 'stop.megaphone', {id: id, path: path});
          } else {
            trigger(options, 'stopAll.megaphone');
            trigger(options, 'play.megaphone', {id: id, path: path});
          }
          event.target.blur();
          return false;
        });
      });
    }
  ];
  
  $.fn.megaphone.multi.trackDisplay = [
    function(options) {
      this.each(function() {
        var module = this;
        var songTitle = $(this).closest(options.selectors.TRACK_LIST).find('div.song-title');
        var title = $('td.td-title a', module).text();
        $(options.selectors.PLAY_BUTTON, this).click(function(event) {
          songTitle.text(title);
        });
        $(this).bind('stopped.megaphone', function(event) {
          if (songTitle.text() == title) {
            songTitle.text('');
          }
        });
      });
    }
  ];
  
  $.fn.megaphone.multi.playing.push(function(options) {
    this.bind('playing.megaphone', function(event, memo) {
    });
  });

  $.fn.megaphone.multi.loading.push(function(options) {
    this.bind('loading.megaphone', function(event, memo) {
      var progress = $(this).closest(options.selectors.TRACK_LIST).find(options.selectors.PROGRESS_PARENT);
      $(options.selectors.LOAD_PROGRESS, progress).css('width', util.formatPercent(memo.loadPercent) + '%');
    });
  });
  
  $.fn.megaphone.multi.loadComplete.push(function(options) {
    this.bind('loadComplete.megaphone', function(event, memo) {
      var progress = $(this).closest(options.selectors.TRACK_LIST).find(options.selectors.PROGRESS_PARENT);
      $(options.selectors.LOAD_PROGRESS, progress).css('width', util.formatPercent(memo.loadPercent) + '%');
    });
  });

  $.fn.megaphone.multi.stopped.push(function(options) {
    this.bind('stopped.megaphone', function(event, memo) {
      var progress = $(this).closest(options.selectors.TRACK_LIST).find(options.selectors.PROGRESS_PARENT);
      $(options.selectors.LOAD_PROGRESS, progress).css('width', '0%');
      $(options.selectors.PLAY_PROGRESS, progress).css('width', '0%');
      $(this).closest(options.selectors.TRACK_LIST).find(options.selectors.TIME_REMAINING).text('');
    });
  });

  $.fn.megaphone.multi.playing.push(function(options) {
    this.bind('playing.megaphone', function(event, memo) {
      var progress = $(this).closest(options.selectors.TRACK_LIST).find(options.selectors.PROGRESS_PARENT);
      $(options.selectors.PLAY_PROGRESS, progress).css('width', util.formatPercent(memo.playPercent) + '%');
      var secondsRemaining = memo.totalSeconds - memo.playSeconds;
      var time = $(this).closest(options.selectors.TRACK_LIST).find(options.selectors.TIME_REMAINING);
      time.text('-' + util.formatSeconds(secondsRemaining));
    });
  });

});