
var g_jimi = null;

function ensure_jimi()
{
    if(g_jimi == null){
        g_jimi = new JimiPlayer();
        g_jimi.log = function() {}; // disable logging
    }
	return g_jimi;
}

function on_flash_log(text)
{
	g_jimi.log(text);
}

function on_jimi_play_complete()
{
	g_jimi.on_done();
}

function on_jimi_play_progress(position, duration)
{
	g_jimi.on_progress(position, duration);
}

function on_jimi_load_complete(success)
{
	g_jimi.on_load(success);
}

function on_jimi_load_progress(position, duration)
{
	g_jimi.on_load_progress(position, duration);
}

// Stops all Jimi players and hides all enhanced player stuff. Used by
// flash to stop web players once a flash player starts playing.
function stop_all()
{
  g_jimi.stop_sound();
  hide_other_progress();
}

function format_time(uSec)
{
	secs = Math.round(uSec / 1000);
	min = Math.floor(secs / 60);
	secs = secs % 60;
	if(secs < 10){
        secs = "0"+secs;
	}
	return min+":"+secs;
}

var JimiSingle = new Class.create();
JimiSingle.prototype = {
    
    initialize: function(path, idname, options)
    {
        ensure_jimi();
        
        this.path = path;
        this.set_options(options);
        this.playing = false;
        
        $("#"+idname)[0].play = function(){ this.play()}.bind(this);
        $("#"+idname)[0].toggle = function(){ this.toggle()}.bind(this);
        $("#"+idname)[0].stop = function(){ this.stop()}.bind(this);
    },

    set_path: function(new_path) {
      this.path = new_path;
    },
    
    set_options: function(options) {
        this.options = {
            id: null,
            playlist: null,
            on_start: null,
            on_end: null,
            on_progress: null,
            on_load_progress: null,
            on_loading: null,
            on_error:null,
            preset_duration: null
        };
        
        Object.extend(this.options, options || {});
        
        if (this.options.on_start != null) {
            this.on_start_func = new Function("total", this.options.on_start);
        }
        
        if (this.options.on_end != null) {
            this.on_end_func = new Function(this.options.on_end);
        }
        
        if (this.options.on_error != null) {
            this.on_error_func = new Function(this.options.on_error);
        }
        
        if (this.options.on_loading != null) {
            this.on_loading_func = new Function(this.options.on_loading);
        }

        if (this.options.on_progress != null) {
            this.on_progress_func = new Function("msecs", "total", this.options.on_progress);
        }

        if (this.options.on_load_progress != null) {
            this.on_load_progress_func = new Function("msecs", "total", this.options.on_load_progress);
        }
        
        if (this.options.preset_duration != null) {
            // Convert seconds to milliseconds
            this.preset_duration = this.options.preset_duration * 1000;
        }
    },
    
    play: function()
    {
        // Fire off the play event, ignore the result
        if (this.options.b_guid || this.options.c_guid) {
          $.post("/ev/play_start",
                 {b_guid: this.options.b_guid,
                  c_guid: this.options.c_guid},
                 function () {});
        }

        if(this.on_loading_func)
        {
            this.on_loading_func();
        }
        g_jimi.stop_sound();
        g_jimi.set_callback(this);
        g_jimi.play_sound(this.path);
    },

    toggle: function()
    {
        if(this.playing)
        {
            this.stop();
        }else{
            this.play();
        }
    },
    
    stop: function()
    {
        this.on_done();
        g_jimi.stop_sound();
    },
    
    seek: function(percentage)
    {
        g_jimi.seek_percent(percentage)
    },
    
    on_start: function()
    {
        this.playing = true;

        if(this.on_start_func)
        {
            g_jimi.log("now started");
            // duration is currently unavailable
            // dur = g_jimi.get_duration();
            dur = 0;
            this.on_start_func(dur);
        }
    },
    
    on_error: function()
    {
        this.playing = false;
        
        if(this.on_error_func)
        {
            this.on_error_func();
        }
    },
    
    on_done: function()
    {
        this.playing = false;

        if(this.on_end_func)
        {
            this.on_end_func();
        }
    },
    
	on_progress: function(progress, duration)
	{
        if(this.on_progress_func)
        {
            this.on_progress_func(progress, duration);
        }
	},
    
	on_load_progress: function(progress, duration)
	{
        if(this.on_load_progress_func)
        {
            this.on_load_progress_func(progress, duration);
        }
	}
}

var JimiTrack = Class.create();
Object.extend(JimiTrack.prototype, JimiSingle.prototype);

Object.extend(JimiTrack.prototype, {

	join_list: function(list, index)
	{
		this.index = index;
		this.list = list;
	},
	
    play: function(url)
    {
		this.list.play(this);
    }
});

var JimiTrackList = Class.create();
JimiTrackList.prototype = {
	initialize: function(output_node)
	{
        ensure_jimi();
        
		this.tracks = new Array;
	},

	add_track: function(track)
	{
		track.join_list(this, this.tracks.length);

		this.tracks.push(track);
	},
	
	play: function(track)
	{
		this.current_track = track;

		g_jimi.set_callback(this);
		g_jimi.play_sound(track.url);
	},

	stop: function()
	{
		g_jimi.stop_sound();
	},

	next_track: function()
	{
		return this.tracks[this.current_track.index + 1];
	},
	
	length: function()
	{
		return this.tracks.length;
	},
	
	track: function(index)
	{
		return this.tracks[index];
	},

	on_start: function()
	{
		this.current_track.set_playing();
	},

	on_error: function()
	{
		this.current_track.set_stopped();
	},

	on_done: function()
	{
		this.current_track.set_stopped();

		next = this.next_track();
		if (next != null)
		{
			this.play(next);
		}
	},

	on_progress: function(progress, duration)
	{
		this.current_track.on_progress(progress, duration);
	}
}
	

var JimiPlayer = Class.create();
JimiPlayer.prototype = {

	initialize: function(output_node)
	{
		var so = new SWFObject("/localflash/jimi.swf", "snap_player", "0", "0", "7", "#FFFFFF");
        so.useExpressInstall();        
		so.write("flashcontent");
		this.swf = document.getElementById("snap_player");
		this.instance = 0;
		this.callback = null;
        this.playing = false;
        this.log_list = new Array();
	},

	set_callback: function(callback_object)
	{
		this.callback = callback_object
	},

	on_progress: function(position, duration)
	{
        this.log("on progress");
        if(!this.playing)
        {
            this.log("not playing");
		    if (this.callback && this.callback.on_start)
		    {
            this.log("now playing");
                this.playing = true;
            
			    this.callback.on_start();
		    }
        }

		this.instance += 1;

        this.log("play " + this.instance + ": " + position + " of " + duration);
        
		if (this.callback && this.callback.on_progress)
		{
      if (this.callback.preset_duration && this.callback.preset_duration != null)
      {
        duration = this.callback.preset_duration;
      }
			this.callback.on_progress(position, duration);
		}
    },
    
	on_load: function(success)
	{
		if (success)
		{
      // Once we've loaded the audio, we don't want to use the preset duration
      // any more. We'll set that to null. Then the on_progress function will
      // use the audio's actual loaded duration.
      if (this.callback.preset_duration)
      {
        this.callback.preset_duration = null;
      }
      
      if (this.callback && this.callback.on_load)
			{
				this.callback.on_load();
			}
			this.log("Load Successful");
		}else{
			if (this.callback && this.callback.on_error)
			{
				this.callback.on_error();
			}
			this.log("Load Failed");
		}
	},

	on_load_progress: function(bytes, totalBytes)
	{
		this.instance += 1;
		
        this.log("load " + this.instance + ": " + bytes + " of " + totalBytes);
        
		if (this.callback && this.callback.on_load_progress)
		{
			this.callback.on_load_progress(position, duration);
		}
    },
    
	get_duration: function()
	{
		dur = this.swf.get_sound_duration();
        return dur;
	},

	on_done: function()
	{
    this.playing = false;
    
    this.reset();
        
		this.log("Playback is done");

		if (this.callback && this.callback.on_done)
		{
			this.callback.on_done();
		}
	},
	
	play_sound: function(url)
	{
		this.current_track = null;
		this.swf.play_sound(url);
	},
	
	pause_sound: function(url)
	{
		this.swf.pause_sound();
	},

	stop_sound: function()
	{
    if (this.playing)
    {
      this.reset();
    }
    
    this.playing = false;
    
    if (this.callback && this.callback.on_done)
		{
			this.callback.on_done();
		}
	},

  seek: function(position)
  {
      if(this.playing)
      {
          this.swf.set_sound_position(position);
      }
  },
  
  seek_percent: function(percentage)
  {
    this.seek(this.get_duration() * (percentage / 100));
  },
  
  reset: function()
  {
    this.swf.set_sound_position(0);
    this.swf.stop_sound();
  },
    
	log: function(text)
	{
        len = this.log_list.push("<div>"+text+"</div>");
        if(len > 1000)
        {
            this.log_list.shift();
        }

        var out_text = "";

        for(ii=0;ii<this.log_list.length;ii++)
        {
            out_text += this.log_list[ii];
        }

        $("#snap_log").html(out_text);
	}
}

// skip_to_click() skips to a certain position in the audio when you click
// somewhere inside the status bar.
//
// TODO: Fix implementation so that clicking on progress/duration numbers in
// Firefox skips.
function skip_to_click(e) {
  var posx = 0;
  if (!e) var e = window.event;
  if (e.pageX || e.pageY) 	{
    posx = e.pageX;
  }
  else if (e.clientX || e.clientY) 	{
    posx = e.clientX + document.body.scrollLeft
      + document.documentElement.scrollLeft;
  }
  var elem = $('.playing_status:visible .status_bar');
  var dom_elem = elem.get(0);
  var coords = ui_findPos(dom_elem);
  var x_inside_elem = posx - coords[0];
  var percentage = (x_inside_elem / elem.width()) * 100;
  
  track.seek(percentage);
}

// update_player_progress updates the provided id's progress and duration
// as the audio loads/plays.
//
// Parameters:
//            id - HTML id of the table row containing the player tag for the
//                 audio you want to update.
//            msecs - how far into the audio you are (in milliseconds)
//            total - total duration of the audio (in milliseconds)
//
// Usage:
//            Inside the player_tag HTML helper, set the on_progress option
//            to be something like:
// update_player_progress(\'message_entry_' + message.id.to_s + '\',msecs,total)
//
// IMPORTANT: In usage above, do not change "msecs" or "total" parameter names.
//            Player.js requires that these parameter names remain the same as
//            above.
function update_player_progress(id, msecs, total)
{
    
  var duration_string = format_time(total);
  var progress_string = format_time(msecs);
  
  var percentage = Math.round((msecs / total) * 100);
  
  if (!document.getElementById('status_' + id))
  {
    hide_other_progress();
    
    var new_html = '<tr id="status_' + id + '" class="playing_status"><td colspan="2">';
    new_html += '<div class="status_bar">';
    new_html += '<div class="status_played" style="width: ' + percentage + '%"></div>';
    new_html += '<div class="status_duration"><span class="progress_string">0:00</span> / <span class="duration_string">' + duration_string + '</span></div>';
    new_html += '</div>';
    new_html += '</td></tr>';
    show_progress(id);
    $('#' + id).after(new_html);
    
    var el = $('#status_' + id + ' .status_bar').get(0);
    el.onclick = skip_to_click;
    if (el.captureEvents) el.captureEvents(Event.CLICK);
  }
  else if ($('#status_' + id + ':hidden').length == 1)
  {
    // We execute this block if we've opened a piece of audio, closed it (by
    // opening a different piece of audio on the same page), and then opened it
    // again.
    hide_other_progress();
    
    show_progress(id);
    $('#status_' + id).show();
  }
  else
  {
    $('#status_' + id + ' .duration_string').text(duration_string);
    
    $('#status_' + id + ' .status_played').css('width', percentage + '%');
    $('#status_' + id + ' .progress_string').text(progress_string);
  }
}

function update_player_progress_using_divs(id, msecs, total)
{
    
  var duration_string = format_time(total);
  var progress_string = format_time(msecs);
  
  var percentage = Math.round((msecs / total) * 100);
  
  if (!document.getElementById('status_' + id))
  {
    hide_other_progress_using_divs();
    
    var new_html = '<div id="status_' + id + '" class="playing_status">';
    new_html += '<div class="status_bar">';
    new_html += '<div class="status_played" style="width: ' + percentage + '%"></div>';
    new_html += '<div class="status_duration"><span class="progress_string">0:00</span> / <span class="duration_string">' + duration_string + '</span></div>';
    new_html += '</div>';
    new_html += '</div>';
    show_progress_using_divs(id);
    $('#' + id).after(new_html);
    
    var el = $('#status_' + id + ' .status_bar').get(0);
    el.onclick = skip_to_click;
    if (el.captureEvents) el.captureEvents(Event.CLICK);
  }
  else if ($('#status_' + id + ':hidden').length == 1)
  {
    // We execute this block if we've opened a piece of audio, closed it (by
    // opening a different piece of audio on the same page), and then opened it
    // again.
    hide_other_progress_using_divs();
    
    show_progress_using_divs(id);
    $('#status_' + id).show();
  }
  else
  {
    $('#status_' + id + ' .duration_string').text(duration_string);
    
    $('#status_' + id + ' .status_played').css('width', percentage + '%');
    $('#status_' + id + ' .progress_string').text(progress_string);
  }
}

function show_progress(id)
{
  $('#' + id).addClass('playing_list_item');
  $('#' + id + ' .play').get(0).rowSpan = 2;
  $('#' + id + ' .profile').get(0).rowSpan = 2;
  
  // Image stuff
  var profile_img = $('#' + id + ' .profile img');
  var old_img_url = profile_img.get(0).src;
  var new_img_url = old_img_url.replace(/tiny/, 'small');
  
  profile_img.css('width', '90px');
  profile_img.get(0).src = '/images/loading_circle_90x60.gif'; // Set the src to the loading image
  
  // We have to use a setTimeout call here, because if we just set the image
  // src immediately, then the call above will not take place and we won't
  // display a loading image at all.
  setTimeout("update_profile_image('" + id + "', '" + new_img_url + "');", 50);
  
  $('#' + id + ' .profile').addClass('reg');
}

function show_progress_using_divs(id)
{
  $('#' + id).addClass('playing_list_item');
}

function update_profile_image(id, new_img_url)
{
  $('#' + id + ' .profile img').get(0).src = new_img_url;
}

// Hides all progress indicators on the page. This method gets called before
// the clicked audio progress indicator is shown, so the audio you just clicked
// doesn't get its indicator hidden.
function hide_other_progress()
{
  $('.playing_status:visible').each(function(i)
  {
    var id = this.id;
    id = id.substring(7); //Remove the "status_" part of the string
    
    $('#' + id).removeClass('playing_list_item');
    $('#' + id + ' .play').get(0).rowSpan = 1;
    $('#' + id + ' .profile').get(0).rowSpan = 1;
    $('#' + id + ' .profile').removeClass('reg');
    $('#' + id + ' .profile img').css('width', '30px');
    $(this).hide();
  });
}

function hide_other_progress_using_divs()
{
  $('.playing_status:visible').each(function(i)
  {
    var id = this.id;
    id = id.substring(7); //Remove the "status_" part of the string
    
    $('#' + id).removeClass('playing_list_item');
    $(this).hide();
  });
}

// The following three functions are used for JavaScript hooks to tie into
// the flash players.
//
// Example: On a blog post detail page, Liteboard needs to stop playing once
// you play a reply. Simply call "stopMedia('Liteboard');" ("Liteboard" is the
// name attribute of the <embed> tag) and this happens.
function thisMovie(movieName) {
    var isIE = navigator.appName.indexOf("Microsoft") != -1;
    var movieElem = (isIE) ? window[movieName] : document[movieName];
    if (movieElem == 'undefined')
    {
      return false;
    }
    
    return movieElem;
}

function stopMedia(movieName) {
  var movie = thisMovie(movieName);
  if (movie)
  {
    movie.stop_playback();
  }
}             

function startMedia(movieName) {
  var movie = thisMovie(movieName);
  if (movie)
  {
    movie.start_playback();
  }
}
