(function($){

$.mn = { session: { }, seenEvents: { }, intervalId: null, intervalDuration: 0 };

$.mn.events = {
  UserEnteringEvent: function(up) { },
  UserLeavingEvent: function(up) { },
  WorkspaceConfiguringEvent: function(up) {
    $('#workspace').animate({ 
      backgroundColor  : up.backgroundColor,
      width            : up.width,
      height           : up.height
    }).css({
      backgroundImage      : 'url(' + up.backgroundImage + ')',
      backgroundRepeat     : up.backgroundRepeat,
      backgroundAttachment : up.backgroundAttachment,
      backgroundPosition   : up.backgroundPosition
    });
  },
  NoteCreatingEvent: function(up) {
    var newMetaNote = $('#factory div.note')
      .clone()
      .attr('id', 'note_' + up.noteId)
      .css({
        display         : 'none',
        width           : up.width + 'px',
        height          : up.height + 'px',
        top             : up.y + 'px',
        left            : up.x + 'px',
        zIndex          : up.zIndex,
        backgroundColor : up.backgroundColor,
        color           : up.color,
        opacity         : up.opacity
      });
    $('h1.subject, h1.footer', newMetaNote).css({
      backgroundColor : up.headerBackgroundColor,
      color           : up.color
    });
    $('h1.subject', newMetaNote).html(up.title).css({ backgroundColor: up.headerBackgroundColor, color: up.headerColor });
    $('div.body', newMetaNote).html(up.body);
    $('h1.footer a', newMetaNote).html(up.user).attr({ href: '/' + up.user });
    $('h1.footer span.date', newMetaNote).html(up.createdAt).css({ backgroundColor: up.headerBackgroundColor, color: up.headerColor });
    $('#workspace').append(newMetaNote);
    newMetaNote.fadeIn();
    var s = $.mn.session
    if (s.authorizedToEdit) {
      newMetaNote.mn({ wasJustCreated: true });
      if (typeof(s.id) == 'string' && s.id == up.sessionId) {
        $('h1.subject', newMetaNote).dblclick();
      }
    }
  },
  NoteMovingEvent: function(up) {
    var target   = $(up.noteId);
    var position = target.position();
    if ((position.top != up.y) || (position.left != up.x)) {
      $(up.noteId).animate({ top: up.y, left: up.x });
    }
  },
  NoteResizingEvent: function(up) {
    var target = $(up.noteId);
    var width  = target.width();
    var height = target.height();
    if ((width != up.width) || (height != up.height)) {
      target.animate({ width: up.width, height: up.height });
    }
  },
  NoteDeletingEvent: function(up) {
    $(up.noteId).fadeOut('normal', function() {
      $(up.noteId).remove();
    });
  },
  NoteColoringEvent: function(up) {
    var note = $.data($(up.noteId)[0], 'self');
    note.backgroundColor = up.backgroundColor;
    $(up.noteId).animate({ 
      backgroundColor : up.backgroundColor,
      color           : up.color,
      opacity         : up.opacity
    });
    var headers = up.noteId + ' h1.subject, ' + up.noteId + ' h1.footer';
    var headerContent = up.noteId + ' h1.subject, ' + up.noteId + ' h1.footer span.date';
    $(headers).animate({
      backgroundColor : up.headerBackgroundColor
    });
    $(headerContent).animate({
      color           : up.headerColor
    });
  },
  NoteRaisingEvent: function(up) {
    var i;
    for (i = 0; i < up.notes.length; i++) {
      var n = up.notes[i];
      $(n.noteId).css({ zIndex: n.zIndex });
    }
    $(up.noteId).css({ zIndex: up.zIndex });
  },
  NoteFinishEditingEvent: function(up) {
    $(up.noteId + ' div.body').html(up.body);
  },
  NoteFinishEditingSubjectEvent: function(up) {
    $(up.noteId + ' h1.subject').html(up.title);
  },
  NoteShadingEvent: function(up) {
    var note = $.data($(up.noteId)[0], 'self');
    note.shade();
  }
};

$.mn.notify = function(ev) {
  if (!$.browser.opera && $.mn.session.useToaster) {
    var notification = ev.notification ? ev.notification : ev.eventType;
    $('#workspace').toaster({ name: '', data: notification, position: 3 });
  }
};

$.mn.replayEvent = function(ev) {
  if (!window.opera) {
    // TODO - remove when toaster is fixed to work in opera
    // TODO - filter out certain trivial events
    $.mn.notify(ev);
  }
  if (!$.mn.seenEvents[ev.eventId]) {
    if ($.mn.events[ev.eventType]) {
      $.mn.events[ev.eventType](ev);
      $.mn.seenEvents[ev.eventId] = true;
      $.mn.session.workspaceLastEventId = ev.eventId;
    }
  }
};

$.mn.updateWorkspace = function() {
  var session = $.mn.session;
  $.ajax({
    type     : 'GET',
    dataType : 'json',
    url      : session.eventURL,
    data     : { eventId: session.workspaceLastEventId },
    success  : function(response) {
      var i;
      var events = response.events;
      var ev, theEnd;
      for (i = 0; i < events.length; i++) {
        ev = events[i];
        if (!ev) { continue }
        if (ev.eventId == response.eventId) { continue; }
        $.mn.replayEvent(ev);
      }
    }
  });
};

$.mn.startUpdatingEvery = function(ms) {
  var session = $.mn.session;
  session.intervalDuration = ms;
  if (session.intervalId) {
    window.clearInterval(session.intervalId);
  }
  session.intervalId = window.setInterval($.mn.updateWorkspace, ms);
};

$.mn.stopUpdating = function() {
  var session = $.mn.session;
  if (session.intervalId) {
    window.clearInterval(session.intervalId);
  }
  session.intervalId = null;
};

$.mn.registerSuccessfulEvent = function(response) {
  var session = $.mn.session;
  if ((response.eventId - session.workspaceLastEventId) >= 1) {
    // console.log('please update workspace')
    $.mn.updateWorkspace();
  } else {
    // console.log('supposedly do not need update')
    $.mn.replayEvent(response);
  }
};

// This is a generalized event generation function!!!  use this!!!
$.mn.generate = function(type, params) {
  var session = $.mn.session;
  if (!params) { params = {}; }
  params['eventType'] = type;
  params['sessionId'] = session.id;
  $.ajax({
    type     : 'POST',
    dataType : 'json',
    url      : session.eventURL,
    data     : params,
    success  : function(response) { 
      if (response.success) {
        $.mn.registerSuccessfulEvent(response);
      } else {
        // TODO - report the error somehow
        var message = type + " didn't work out.";
        if (!window.opera) {
          $('#workspace').toaster({ name: 'ERROR', data: message, position: 3 });
        } else {
          alert(message);
        }
      }
    }
  });
};

$.mn.Note = function(options) {
  $.extend(this, options);
  this.becomeEditable();
  this.becomeHighlightable();
  this.becomeResizable();
  this.becomeMenuEnabled();
};
$.mn.Note.prototype = {
  jquery: function() {
    return $('#note_' + this.noteId);
  },
  shade: function(state) {
    var $j = this.jquery();
    var el = $j[0];
    var rs;
    var needToShade = (state != null)
      ? state
      : !($.data(el, 'originalHeight'));
    if (needToShade) {
      $('h1.footer, img.menu, div.ui-resizable-se', $j).hide();
      $.data(el, 'originalHeight', $j.height());
      $j.css({ height: 18 });
      // $('.ui-resizable-handle', $j).hide();
      rs = $.data(el, 'ui-resizable');
      rs.disable();
      $('img.menu', $j).removeClass('enabled');
    } else {
      var height = $.data(el, 'originalHeight');
      if (height) {
        $j.css({ height: height });
        $('h1.footer, img.menu, div.ui-resizable-se', $j).show();
        // $('.ui-resizable-handle', $j).show();
        rs = $.data(el, 'ui-resizable');
        rs.enable();
        $.data(el, 'originalHeight', false);
        $('img.menu', $j).addClass('enabled');
      }
    }
  },
  resize: function(width, height) {
  },
  move: function(x, y) {
  },
  becomeEditable: function() {
    var self = this;
    var $j = this.jquery();
    var noteId = this.noteId;
    var baseURL = $.mn.session.eventURL.replace(/\/@events/, '');
    var noteTitlePath = baseURL + '/Note-' + noteId + '.title';
    var noteBodyPath  = baseURL + '/Note-' + noteId + '.body';
    $('h1.subject', $j).editable(
      function(value, settings) {
        // console.log(["hello", this, value, settings]);
        $.mn.generate('NoteFinishEditingSubjectEvent', { noteId: noteId, title: value });
        if (self.wasJustCreated) {
          $('div.body', $j).dblclick();
          self.wasJustCreated = false;
        }
        return "Saving...";
      },
      {
        loadurl     : noteTitlePath,
        placeholder : 'Double-click to Edit',
        tooltip     : 'Double-click to Edit',
        indicator   : '<img src="/static/images/spinner.gif" />',
        onblur      : 'submit',
        event       : 'dblclick',
        width       : function(s){ return $(s).width() - 16; },
        select      : true
      }
    );
    $('div.body', $j).editable(
      function(value, settings) {
        // console.log(["hello", this, value, settings]);
        $.mn.generate('NoteFinishEditingEvent', { noteId: noteId, body: value });
        return "<p>Saving...</p>";
      },
      {
        loadurl     : noteBodyPath,
        type        : 'textarea',
        placeholder : '<p>Double-click to Edit</p>',
        tooltip     : 'Double-click to edit.  Hit TAB to save.',
        indicator   : '<img src="/static/images/spinner.gif" />',
        onblur      : 'submit',
        event       : 'dblclick',
        width       : function(s){ return $(s).width()  - 24; },
        height      : function(s){ return $(s).height() - 24; },
        select      : true
      }
    );
  },
  becomeHighlightable: function() {
    var $j = this.jquery();
    $('img.shader').mouseover(function(ev){ this.src = '/static/images/windowshade-on.gif'; });
    $('img.shader').mouseout(function(ev){ this.src = '/static/images/windowshade.gif'; });
    $('img.closer').mouseover(function(ev){ this.src = '/static/images/close-on.gif'; });
    $('img.closer').mouseout(function(ev){ this.src = '/static/images/close.gif'; });
    $('img.menu').mouseover(function(ev){ this.src = '/static/images/menu-on.gif'; });
    $('img.menu').mouseout(function(ev){ this.src = '/static/images/menu.gif'; });
    $('div.ui-resizable-se').mouseover(function(ev){ $(this).css({ backgroundImage: 'url(/static/images/resize-on.gif)' }); });
    $('div.ui-resizable-se').mouseout(function(ev){ $(this).css({ backgroundImage: 'url(/static/images/resize.gif)' }); });
    $j.hover(
      function(ev){ 
        var note = $.data($j[0], 'self');
        var oc = note.backgroundColor;
        $('img.control.enabled', $(this)).show();
        $(this).css({backgroundColor: '#fc0' }).animate({ backgroundColor: oc }, 'slow') 
      }, 
      function(ev){
        $('img.control', $(this)).hide();
      }
    );
  },
  becomeResizable: function() {
    var $j = this.jquery();
    $j.resizable({
      autohide: true,
      stop: function(ev) {
        var noteId = parseInt(this.id.replace(/note_/, ''));
        var width = $(this).width();
        var height = $(this).height();
        $.mn.generate('NoteResizingEvent', { noteId: noteId, width: width, height: height });
      }
    });
    $('.ui-resizable-se', $j).css({ bottom: 2, right: 2 });
  },
  becomeMenuEnabled: function() {
    var $j = this.jquery();
    $('img.menu', $j).click(function(ev){
      var noteId = parseInt($j.attr('id').replace(/note_/, ''));
      $.mn.contextualMenu.toggle(noteId);
    });
  }
};

$.fn.extend({
  mn: function(options) {

    // TODO - wrap this whole thing in a this.each()
    // TODO - figure out noteId once
    this.each(function(n, el){
      var noteId = parseInt(el.id.replace(/note_/, ''));
      var noteOptions = $.extend({ noteId: noteId }, options);
      var note = new $.mn.Note(noteOptions);
      if (note.jquery().hasClass('need_to_shade')) {
        note.shade();
        note.jquery().removeClass('need_to_shade');
      }
      note.backgroundColor = $(el).css('backgroundColor'); 
      $.data(el, 'self', note);
    });

    // iAm.raisable()
    // TODO - Make this not conflict w/ double-click to edit.
    this.mousedown(function(ev){
      // var noteId = parseInt(this.id.replace(/note_/, ''));
      // $.mn.generate('NoteRaisingEvent', { noteId: noteId });
      if (!$.mn.session.highestZ) {
        $.mn.session.highestZ = 1000 + $('div.note').length;
      }
      $(this).css({ zIndex: $.mn.session.highestZ++ });
    });

    // iAm.deletable()
    $('img.closer', this).click(function(ev){
      if (confirm("Are you sure?")) {
        var noteId  = parseInt(this.parentNode.id.replace(/note_/, ''));
        $.mn.generate('NoteDeletingEvent', { noteId: noteId });
      }
    });
    $('img.shader', this).click(function(ev){
      var note = $.data(this.parentNode, 'self');
      var noteId = note.noteId;
      $.mn.generate('NoteShadingEvent', { noteId: noteId });
    });

    this.draggable({
      handle: 'h1.subject, h1.footer',
      drag: function(ev) {
        var noteId   = parseInt(this.id.replace(/note_/, ''));
        if ($.mn.contextualMenu.shown(noteId)) {
          $.mn.contextualMenu.move(noteId);
        }
      },
      stop: function(ev) {
        var position = $(this).position();
        var noteId   = parseInt(this.id.replace(/note_/, ''));
        if ($.mn.contextualMenu.shown(noteId)) {
          $.mn.contextualMenu.move(noteId);
        }
        $.mn.generate('NoteMovingEvent', { noteId: noteId, x: position.left, y: position.top });
      }
    });

    return this;
  }
});

})(jQuery);
