dojo.provide("hobit.MyHobit");

dojo.require("dojo.cookie");

dojo.addOnLoad(
  function() {
    /**
     * The MyHobit singleton instance
     */
    hobit.myHobit = new hobit.MyHobit();
    dojo.connect(window, "onload", function() {
      // initially disable stylesheet
      // (needs to be done after completely loaded in chrome)
      hobit.myHobit.enableStyleSheet(false);
    });
  }
);

/**
 * MyHobit class is used to manage events and infobooths selected by the user.
 * It's also responsible for displaying the my hobit dialog.
 * The class expects two globally defined associative arrays:
 *
 * - itemDefinitions: Stores item definitions. There is one key for each
 *   item type (event, infobooth, ...). The values are associative arrays with
 *   item ids as keys and item objects as values.
 * - orderedItemIds: Stores ordered item ids. There is one key for each
 *   item type (event, infobooth, ...). The values are arrays with ordered
 *   item ids.
 * - dates: Stores hobit dates, where the keys are the dates in the format
 *   yyyy-MM-dd and the values are the dates in the format dd.MM.yyyy
 */
dojo.declare("hobit.MyHobit", null, {

  /**
   * Constructor
   */
  constructor: function() {
    // create cookies
    var types = ['event', 'infobooth'];
    for (var i=0, count=types.length; i<count; i++) {
      dojo.cookie(this.getCookieName(types[i]), '{}');
    }

    // add over status and click to my hobit link
    var self = this;
    var myHobitLink = dojo.query('#myHobitLink');
    var myHobitLinkOver = dojo.query('#myHobitLinkOver');
    myHobitLink.connect("onmouseover", function() {
      dojo.style(this, "display", "none");
      myHobitLinkOver.style("display", "block");
    }).connect("onclick", function() {
      self.display();
    });
    myHobitLinkOver.connect("onmouseout", function() {
      dojo.style(this, "display", "none");
      myHobitLink.style("display", "block");
    }).connect("onclick", function() {
      self.display();
    });
    this.updateMyHobitLinkDisplay();
  },

  /**
   * Check if an item is selected
   * @param type Type of the item
   * @param id The id of the item
   * @param date The date of the item in the form yyyy-MM-dd (optional)
   * @return Boolean
   */
  containsItem: function(type, id, date) {
    var ids = dojo.fromJson(dojo.cookie(this.getCookieName(type)));
    var entry = ids[id];
    if (entry != undefined) {
      if (date != undefined) {
        // selected dates are stored as array value in entry.dates
        return (dojo.indexOf(entry.dates, date) != -1);
      }
      // no date given, existence of entry is sufficient
      return true;
    }
    return false;
  },

  /**
   * Add an item to the list of selected items
   * @param type Type of the item
   * @param id The id of the item
   * @param date The date of the item in the form yyyy-MM-dd (optional)
   */
  addItem: function(type, id, date) {
    var ids = dojo.fromJson(dojo.cookie(this.getCookieName(type)));
    var entry = ids[id];
    if (entry == undefined) {
      entry = {dates:[date]};
    }
    else if (dojo.indexOf(entry.dates, date) == -1) {
      entry.dates.push(date);
    }
    ids[id] = entry;
    // update cookie
    dojo.cookie(this.getCookieName(type), dojo.toJson(ids));
    // update ui
    this.updateAllDisplay();
    this.updateMyHobitLinkDisplay();
  },

  /**
   * Remove an item from the list of selected items
   * @param type Type of the item
   * @param id The id of the item
   * @param date The date of the item in the form yyyy-MM-dd (optional)
   */
  removeItem: function(type, id, date) {
    var ids = dojo.fromJson(dojo.cookie(this.getCookieName(type)));
    var entry = ids[id];
    if (entry == undefined) {
      return;
    }
    else if (dojo.indexOf(entry.dates, date) == -1) {
      return;
    }

    // update date list
    var newDates = [];
    var entryDates = entry.dates;
    for (var i=0, count=entryDates.length; i<count; i++) {
      var curDate = entryDates[i];
      if (curDate != date) {
        newDates.push(curDate);
      }
    }

    // update entry
    if (newDates.length > 0) {
      entry.dates = newDates;
      ids[id] = entry;
    }
    else {
      delete ids[id];
    }

    // update cookie
    dojo.cookie(this.getCookieName(type), dojo.toJson(ids));

    // update ui
    this.updateAllDisplay();
    this.updateMyHobitLinkDisplay();
  },

  /**
   * Get the selected items of a given type
   * @param type Type of the item
   * @return Array of items, where each item has a property selectedDates
   * that contains an array with the dates selected by the user in the form
   * yyyy-MM-dd
   */
  getItems: function(type) {
    var items = [];
    var definitions = itemDefinitions[type];
    var allIds = orderedItemIds[type];
    var selectedIds = dojo.fromJson(dojo.cookie(this.getCookieName(type)));
    for (var i=0, countI=allIds.length; i<countI; i++) {
      var curId = allIds[i];
      var entry = selectedIds[curId];
      if (entry != undefined) {
        var item = definitions[curId];
        item.selectedDates = entry.dates;
        items.push(item);
      }
    }
    return items;
  },

  /**
   * Get the number of selected events and infoBooths
   * @return Integer
   */
  getNumItems: function() {
    var count = 0;
    var types = ['event', 'infobooth'];
    for (var j=0, countJ=types.length; j<countJ; j++) {
      var type = types[j];
      var selectedIds = dojo.fromJson(dojo.cookie(this.getCookieName(type)));
      for (id in selectedIds) {
        count++;
      }
    }
    return count;
  },

  /**
   * Display the selected events and infoBooths in a list
   */
  display: function() {

    this.hideSentStatus();

    var eventTable = dojo.query('#myHobitEventList').shift();
    // delete all rows except the first (header)
    while (eventTable.rows.length > 1) {
      eventTable.deleteRow(eventTable.rows.length-1);
    }

    // get the selected events
    var events = this.getItems('event');

    // get conflicts in events
    var conflicts = this.getConflicts(events);

    // add a row for each event
    var warningPlaced = false;
    for (var i=0, count=events.length; i<count; i++) {
      var event = events[i];
      var row = eventTable.insertRow(eventTable.rows.length);

      var cell0 = row.insertCell(0);
      cell0.innerHTML = '<a href="javascript:hobit.myHobit.removeItem(\'event\', \''+event.id+'\', \''+event.dateKey+'\'); hobit.myHobit.display();" class="myhobit_remove">x</a>';

      var cell1 = row.insertCell(1);
      cell1.innerHTML = event.date;
      dojo.addClass(cell1, 'tcDate');

      var cell2 = row.insertCell(2);
      cell2.innerHTML = event.startTime+" - "+event.endTime;
      dojo.addClass(cell2, 'tcTime');

      var cell3 = row.insertCell(3);
      cell3.innerHTML = event.venue;
      dojo.addClass(cell3, 'tcVenue');

      var cell4 = row.insertCell(4);
      cell4.innerHTML = '<em>'+event.title+'</em>';
      dojo.addClass(cell4, 'thTitle');

      var cell5 = row.insertCell(5);
      cell5.innerHTML = event.organizer;
      dojo.addClass(cell5, 'tcOrganizer');

      if (dojo.indexOf(conflicts, event.id) != -1) {
        dojo.addClass(row, 'conflict');
        // place warning
        if (!warningPlaced) {
          this.showWarning('images/my_hobit_hint_events.png', row.lastChild);
          warningPlaced = true;
        }
      }
    }

    var infoBoothTable = dojo.query('#myHobitInfoBoothList').shift();
    // delete all rows except the first (header)
    while (infoBoothTable.rows.length > 1) {
      infoBoothTable.deleteRow(infoBoothTable.rows.length-1);
    }

    // get the selected info booths
    var infoBooths = this.getItems('infobooth');

    // add a row for each info booth
    for (var i=0, countI=infoBooths.length; i<countI; i++) {
      var infoBooth = infoBooths[i];
      var infoBoothDates = infoBooth.selectedDates.sort();
      for (var j=0, countJ=infoBoothDates.length; j<countJ; j++) {
        var date = infoBoothDates[j];
        var row = infoBoothTable.insertRow(infoBoothTable.rows.length);

        var cell0 = row.insertCell(0);
        cell0.innerHTML = '<a href="javascript:hobit.myHobit.removeItem(\'infobooth\', \''+infoBooth.id+'\', \''+date+'\'); hobit.myHobit.display();" class="myhobit_remove">x</a>';

        var cell1 = row.insertCell(1);
        cell1.innerHTML = dates[date];
        dojo.addClass(cell1, 'tcDate');

        var cell2 = row.insertCell(2);
        cell2.innerHTML = infoBooth.location+" "+infoBooth.number;
        dojo.addClass(cell2, 'tcLocation');

        var cell3 = row.insertCell(3);
        cell3.innerHTML = '<em>'+infoBooth.name+'</em>';
        dojo.addClass(cell3, 'tcName');

        var cell4 = row.insertCell(4);
        cell4.innerHTML = infoBooth.organizer;
        dojo.addClass(cell4, 'tcOrganizer');
      }
    }

    // show the dialog
    this.enableStyleSheet(true);
    $('#myHobitDlg').jqm({onHide:this.onClose}).jqmShow();
  },

  /**
   * Called, when the dialog is closed. The scope is the jqModal instance.
   */
  onClose: function(hash) {
    var myhobit = hobit.myHobit;
    myhobit.enableStyleSheet(false);

    // close the window (see jqModal docs)
    hash.o.remove();
    hash.w.hide();
  },

  /**
   * Print the MyHobit content
   */
  print: function() {
    // prevent execution, if there are any conflicts
    var events = this.getItems('event');
    var conflicts = this.getConflicts(events);
    if (conflicts.length > 0) {
      this.showWarning('images/my_hobit_hint_events_send.png');
      return;
    }

    window.print();
  },

  /**
   * Update the given item row display
   * @param type Type of the item
   * @param id The id of the item
   * @param date The date of the item in the format yyyy-MM-dd
   * @param selected Boolean indicating wether the event/infobooth is
   * selected by the user or not
   */
  updateDisplay: function(type, id, date, selected) {
    // create the query for the given item
    var query = '.'+type+'_'+id+'_'+date;
    var rows = dojo.query(query);
    for (var i=0, count=rows.length; i<count; i++) {
      var row = rows[i];
      if (selected) {
        dojo.addClass(row, 'myHobitSelected');
        dojo.query('.myhobit_add', row).style({display: 'none'});
        dojo.query('.myhobit_remove', row).style({display: 'inline'});
      }
      else {
        dojo.removeClass(row, 'myHobitSelected');
        dojo.query('.myhobit_add', row).style({display: 'inline'});
        dojo.query('.myhobit_remove', row).style({display: 'none'});
      }
    }
  },

  /**
   * Update all item row displays
   */
  updateAllDisplay: function() {
    var types = ['event', 'infobooth'];
    for (var j=0, countJ=types.length; j<countJ; j++) {
      var type = types[j];
      var allIds = orderedItemIds[type];
      var selectedIds = dojo.fromJson(dojo.cookie(this.getCookieName(type)));
      for (var i=0, countI=allIds.length; i<countI; i++) {
        var curId = allIds[i];
        var entry = selectedIds[curId];
        for (var date in dates) {
          var selected = (entry != undefined && dojo.indexOf(entry.dates, date) != -1);
          this.updateDisplay(type, curId, date, selected);
        }
      }
    }
  },

  /**
   * Send the selected events and infobooths to the given email address
   */
  sendEmail: function() {

    this.hideSentStatus();

    // prevent execution, if there are any conflicts
    var events = this.getItems('event');
    var conflicts = this.getConflicts(events);
    if (conflicts.length > 0) {
      this.showWarning('images/my_hobit_hint_events_send.png');
      return;
    }

    var email = dojo.byId('myHobitEmail').value;
    var eventObjs = [];
    var events = this.getItems('event');
    for (var i=0, count=events.length; i<count; i++) {
      var curEvent = events[i];
      eventObjs.push({id:curEvent.id, dates:curEvent.selectedDates});
    }
    var infoBoothObjs = [];
    var infoBooths = this.getItems('infobooth');
    for (var i=0, count=infoBooths.length; i<count; i++) {
      var curInfoBooth = infoBooths[i];
      infoBoothObjs.push({id:curInfoBooth.id, dates:curInfoBooth.selectedDates});
    }

    var data = {
      email:email,
      eventObjs:eventObjs,
      infoBoothObjs:infoBoothObjs
    };

    dojo.xhrPost({
      url: 'events.php?usr_action=sendEmail&response_format=JSON',
      handleAs: 'json',
      postData: dojo.toJson(data),
      load: function(data, ioArgs){
        if (data.success) {
          dojo.style('myHobitSuccess', 'display', 'block');
        }
        else {
          dojo.byId('myHobitError').innerHTML = data.errorMsg;
          dojo.style('myHobitError', 'display', 'block');
        }
      },
      error: function(error, ioArgs){
        dojo.byId('myHobitError').innerHTML = "Der Server konnte die Anfrage nicht bearbeiten.";
        dojo.style('myHobitError', 'display', 'block');
      }
    });
  },

  /**
   * Update the display of the myHobit link
   */
  updateMyHobitLinkDisplay: function() {
    var numItems = this.getNumItems();
    var countDisplays = dojo.query('.myHobitItemsCount');
    for (var i=0, count=countDisplays.length; i<count; i++) {
      var curDisplay = countDisplays[i];
      curDisplay.innerHTML = '&nbsp;';
      if (numItems > 0) {
        curDisplay.innerHTML = numItems;
      }
    }
  },

  /**
   * Get the name of the cookie that stores the selected ids of a given type
   * in an array
   * @param type The type name
   * @return String
   */
  getCookieName: function(type) {
    return 'myhobit_'+type+'_ids';
  },

  /**
   * Hide the status displays
   */
  hideSentStatus: function() {
    dojo.style('myHobitError', 'display', 'none');
    dojo.style('myHobitSuccess', 'display', 'none');
  },

  /**
   * Enable/disable the myhobit stylesheet (stylesheet with title 'myhobit')
   * @param enabled Boolean wether the stylesheet should be enabled or not
   */
  enableStyleSheet: function(enabled) {
    var styleSheets = document.styleSheets;
    for (var i=0, count=styleSheets.length; i<count; i++) {
      var curStyleSheet = styleSheets[i];
      if (curStyleSheet != undefined && curStyleSheet.title == 'myhobit') {
        curStyleSheet.disabled = !enabled;
      }
    }
  },

  /**
   * Calculate conflicts in the given list of events
   * @param events Array of event items
   * @return Array of ids of conflicting items
   */
  getConflicts: function(events) {
    // code from: http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1303732202
    var conflicts = [];
    var k=0;
    for(var i=1, count=events.length; i<count; i++) {
      if (events[i].dateKey == events[k].dateKey && events[i].startTime < events[k].endTime) {
        conflicts.push(events[i].id);
        conflicts.push(events[k].id);
      }
      if (events[i].dateKey != events[k].dateKey || events[i].endTime >= events[k].endTime) {
        k = i;
      }
    }
    return conflicts;
  },

  /**
   * Show the given warning image
   * @param image The filename of the image to show
   * @param parent The parent node to which the warning should be attached (if undefined,
   * the node with id myHobitWarning will be used if it exists)
   */
  showWarning: function(image, parent) {
    var id = 'myHobitWarning';
    if (parent) {
      var nodeStr = '<div id="'+id+'"><img src="'+image+'" width="236" height="162" alt="Achtung! Du hast Veranstaltungen gewählt, die zur gleichen Zeit stattfinden." /></div>';
      dojo.place(nodeStr, parent, 'last');
      // show a hidden warning, that will only be visible when printing
      var nodeStr = '<div id="myHobitWarningPrint"><img src="images/my_hobit_hint_events_print.png" width="236" height="162" /></div>';
      dojo.place(nodeStr, parent, 'last');
    }
    else {
      dojo.query('#'+id+' img').forEach(function(node, index, arr){
        node.setAttribute('src', image);
      });
    }
  }
});

