
/* Copyright 2014 Intel Corporation, All Rights Reserved.

 Licensed under the Apache License, Version 2.0 (the"License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied. See the License for the
 specific language governing permissions and limitations
 under the License.
 */

/* Namespace for core functionality related to DataTables. */
vsm.datatables = {
  update: function () {
    var $rows_to_update = $('tr.ajax-update');
    if ($rows_to_update.length) {
      var interval = $rows_to_update.attr('data-update-interval'),
        $table = $rows_to_update.closest('table'),
        decay_constant = $table.attr('decay_constant');

      // Do not update this row if the action column is expanded
      if ($rows_to_update.find('.actions_column .btn-group.open').length) {
        // Wait and try to update again in next interval instead
        setTimeout(vsm.datatables.update, interval);
        // Remove interval decay, since this will not hit server
        $table.removeAttr('decay_constant');
        return;
      }
      // Trigger the update handlers.
      $rows_to_update.each(function(index, row) {
        var $row = $(this),
          $table = $row.closest('table.datatable');
        horizon.ajax.queue({
          url: $row.attr('data-update-url'),
          error: function (jqXHR, textStatus, errorThrown) {
            switch (jqXHR.status) {
              // A 404 indicates the object is gone, and should be removed from the table
              case 404:
                // Update the footer count and reset to default empty row if needed
                var $footer, row_count, footer_text, colspan, template, params, $empty_row;

                // existing count minus one for the row we're removing
                row_count = vsm.datatables.update_footer_count($table, -1);

                if(row_count === 0) {
                  colspan = $table.find('th[colspan]').attr('colspan');
                  template = horizon.templates.compiled_templates["#empty_row_template"];
                  params = {
                      "colspan": colspan,
                      no_items_label: gettext("No items to display.")
                  };
                  empty_row = template.render(params);
                  $row.replaceWith(empty_row);
                } else {
                  $row.remove();
                }
                // Reset tablesorter's data cache.
                $table.trigger("update");
                break;
              default:
                horizon.utils.log(gettext("An error occurred while updating."));
                $row.removeClass("ajax-update");
                $row.find("i.ajax-updating").remove();
                break;
            }
          },
          success: function (data, textStatus, jqXHR) {
            var $new_row = $(data);

            if ($new_row.hasClass('status_unknown')) {
              var spinner_elm = $new_row.find("td.status_unknown:last");

              if ($new_row.find('.btn-action-required').length > 0) {
                spinner_elm.prepend(
                  $("<div />")
                    .addClass("action_required_img")
                    .append(
                      $("<img />")
                        .attr("src", "/static/dashboard/img/action_required.png")));
              } else {
                // Replacing spin.js here with an animated gif to reduce CPU
                spinner_elm.prepend(
                  $("<div />")
                    .addClass("loading_gif")
                    .append(
                      $("<img />")
                        .attr("src", "/static/dashboard/img/loading.gif")));
              }
            }

            // Only replace row if the html content has changed
            if($new_row.html() !== $row.html()) {
              if($row.find('.table-row-multi-select:checkbox').is(':checked')) {
                // Preserve the checkbox if it's already clicked
                $new_row.find('.table-row-multi-select:checkbox').prop('checked', true);
              }
              $row.replaceWith($new_row);
              // Reset tablesorter's data cache.
              $table.trigger("update");
              // Reset decay constant.
              $table.removeAttr('decay_constant');
            }
          },
          complete: function (jqXHR, textStatus) {
            // Revalidate the button check for the updated table
            vsm.datatables.validate_button();

            // Set interval decay to this table, and increase if it already exist
            if(decay_constant === undefined) {
              decay_constant = 1;
            } else {
              decay_constant++;
            }
            $table.attr('decay_constant', decay_constant);
            // Poll until there are no rows in an "unknown" state on the page.
            next_poll = interval * decay_constant * 2;
            // Limit the interval to 30 secs
            if(next_poll > 30 * 1000) { next_poll = 30 * 1000; }
            //setTimeout(vsm.datatables.update, 5000);
          }
        });
      });
      setTimeout(vsm.datatables.update, 4500);
    }
  },

  validate_button: function () {
    // Disable form button if checkbox are not checked
    $("form").each(function (i) {
      var checkboxes = $(this).find(".table-row-multi-select:checkbox");
      var action_buttons = $(this).find(".table_actions button.btn-danger");

      // Buttons should be enabled only if there are checked checkboxes
      action_buttons.toggleClass("disabled",
                                 !checkboxes.filter(":checked").length);
    });
  }
};

/* Generates a confirmation modal dialog for the given action. */
vsm.datatables.confirm = function (action) {
  var $action = $(action),
    $modal_parent = $(action).closest('.modal'),
    name_array = [],
    closest_table_id, action_string, name_string,
    title, body, modal, form;
  if($action.hasClass("disabled")) {
    return;
  }
  action_string = $action.text();
  name_string = "";
  // Add the display name defined by table.get_object_display(datum)
  closest_table_id = $(action).closest("table").attr("id");
  // Check if data-display attribute is available
  if ($("#"+closest_table_id+" tr[data-display]").length > 0) {
    if($(action).closest("div").hasClass("table_actions")) {
      // One or more checkboxes selected
      $("#"+closest_table_id+" tr[data-display]").has(".table-row-multi-select:checkbox:checked").each(function() {
        name_array.push(" \"" + $(this).attr("data-display") + "\"");
      });
      name_array.join(", ");
      name_string = name_array.toString();
    } else {
      // If no checkbox is selected
      name_string = " \"" + $(action).closest("tr").attr("data-display") + "\"";
    }
    name_string = interpolate(gettext("You have selected %s. "), [name_string]);
  }
  title = interpolate(gettext("Confirm %s"), [action_string]);
  body = name_string + gettext("Please confirm your selection. This action cannot be undone.");
  modal = horizon.modals.create(title, body, action_string);
  modal.modal();
  if($modal_parent.length) {
    var child_backdrop = modal.next('.modal-backdrop');
    // re-arrange z-index for these stacking modal
    child_backdrop.css('z-index', $modal_parent.css('z-index')+10);
    modal.css('z-index', child_backdrop.css('z-index')+10);
  }
  modal.find('.btn-primary').click(function (evt) {
    form = $action.closest('form');
    form.append("<input type='hidden' name='" + $action.attr('name') + "' value='" + $action.attr('value') + "'/>");
    form.submit();
    modal.modal('hide');
    horizon.modals.modal_spinner(gettext("Working"));
    return false;
  });
  return modal;
};

$.tablesorter.addParser({
  // set a unique id
  id: 'sizeSorter',
  is: function(s) {
    // Not an auto-detected parser
    return false;
  },
  // compare int values
  format: function(s) {
    var sizes = {
      BYTE: 0,
      B: 0,
      KB: 1,
      MB: 2,
      GB: 3,
      TB: 4,
      PB: 5
    };
    var regex = /([\d\.,]+)\s*(byte|B|KB|MB|GB|TB|PB)+/i;
    var match = s.match(regex);
    if (match && match.length === 3){
      return parseFloat(match[1]) *
        Math.pow(1024, sizes[match[2].toUpperCase()]);
    }
    return parseInt(s, 10);
  },
  type: 'numeric'
});

$.tablesorter.addParser({
  // set a unique id
  id: 'timesinceSorter',
  is: function(s) {
    // Not an auto-detected parser
    return false;
  },
  // compare int values
  format: function(s, table, cell, cellIndex) {
    return $(cell).find('span').data('seconds');
  },
  type: 'numeric'
});

vsm.datatables.disable_buttons = function() {
  $("table .table_actions").on("click", ".btn.disabled", function(event){
    event.preventDefault();
    event.stopPropagation();
  });
};

vsm.datatables.update_footer_count = function (el, modifier) {
  var $el = $(el),
    $browser, $footer, row_count, footer_text_template, footer_text;
  if (!modifier) {
    modifier = 0;
  }
  // code paths for table or browser footers...
  $browser = $el.closest("#browser_wrapper");
  if ($browser.length) {
    $footer = $browser.find('.tfoot span.content_table_count');
  }
  else {
    $footer = $el.find('tfoot span.table_count');
  }
  row_count = $el.find('tbody tr:visible').length + modifier - $el.find('.empty').length;
  //footer_text_template = ngettext("Displaying %s item", "Displaying %s items", row_count);
  footer_text = "Displaying "+row_count+' items'; //interpolate(footer_text_template, [row_count]);
  $footer.text(footer_text);
  return row_count;
};

vsm.datatables.add_no_results_row = function (table) {
  // Add a "no results" row if there are no results.
  template = horizon.templates.compiled_templates["#empty_row_template"];
  if (!table.find("tbody tr:visible").length && typeof(template) !== "undefined") {
    colspan = table.find("th[colspan]").attr('colspan');
    params = {
        "colspan": colspan,
        no_items_label: gettext("No items to display.")
    };
    table.find("tbody").append(template.render(params));
  }
};

vsm.datatables.remove_no_results_row = function (table) {
  table.find("tr.empty").remove();
};

/*
 * Fixes the striping of the table after filtering results.
 **/
vsm.datatables.fix_row_striping = function (table) {
  table.trigger('applyWidgetId', ['zebra']);
};

vsm.datatables.set_table_sorting = function (parent) {
    // ubind horizon sorting <
    $(".header").unbind("click");
    $(".header").unbind("mousedown");
    // ubind horizon sorting >

// Function to initialize the tablesorter plugin strictly on sortable columns.
  $(parent).find("table.datatable").each(function () {
    var $table = $(this),
      header_options = {};
    // Disable if not sortable or has <= 1 item
    if ($table.find('tbody tr').not('.empty').length > 1){
      $table.find("thead th[class!='table_header']").each(function (i, val) {
        $th = $(this);
        if (!$th.hasClass('sortable')) {
          header_options[i] = {sorter: false};
        } else if ($th.data('type') === 'size'){
          header_options[i] = {sorter: 'sizeSorter'};
        } else if ($th.data('type') === 'ip'){
          header_options[i] = {sorter: 'ipAddress'};
        } else if ($th.data('type') === 'timesince'){
          header_options[i] = {sorter: 'timesinceSorter'};
        }
      });
      $table.tablesorter({
        headers: header_options,
        widgets: ['zebra'],
        selectorHeaders: "thead th[class!='table_header']",
        cancelSelection: false
      });
    }
  });
};

vsm.datatables.add_table_checkboxes = function(parent) {
  $(parent).find('table thead .multi_select_column').each(function(index, thead) {
    if (!$(thead).find('.table-row-multi-select:checkbox').length &&
      $(thead).parents('table').find('tbody .table-row-multi-select:checkbox').length) {
      $(thead).append('<input type="checkbox" class="table-row-multi-select">');
    }
  });
};

vsm.datatables.set_table_query_filter = function (parent) {
  $(parent).find('table').each(function (index, elm) {
    var input = $($(elm).find('div.table_search.client input')),
        table_selector;
    if (input.length > 0) {
      // Disable server-side searcing if we have client-side searching since
      // (for now) the client-side is actually superior. Server-side filtering
      // remains as a noscript fallback.
      // TODO(gabriel): figure out an overall strategy for making server-side
      // filtering the preferred functional method.
      input.on('keypress', function (evt) {
        if (evt.keyCode === 13) {
          return false;
        }
      });
      input.next('button.btn-search').on('click keypress', function (evt) {
        return false;
      });

      // Enable the client-side searching.
      table_selector = 'table#' + $(elm).attr('id');
      input.quicksearch(table_selector + ' tbody tr', {
        'delay': 300,
        'loader': 'span.loading',
        'bind': 'keyup click',
        'show': this.show,
        'hide': this.hide,
        onBefore: function () {
          var table = $(table_selector);
          vsm.datatables.remove_no_results_row(table);
        },
        onAfter: function () {
          var template, table, colspan, params;
          table = $(table_selector);
          vsm.datatables.update_footer_count(table);
          vsm.datatables.add_no_results_row(table);
          vsm.datatables.fix_row_striping(table);
        },
        prepareQuery: function (val) {
          return new RegExp(val, "i");
        },
        testQuery: function (query, txt, _row) {
          return query.test($(_row).find('td:not(.hidden):not(.actions_column)').text());
        }
      });
    }
  });
};

vsm.datatables.set_table_fixed_filter = function (parent) {
  $(parent).find('table.datatable').each(function (index, elm) {
    $(elm).on('click', 'div.table_filter button', function(evt) {
      var table = $(elm);
      var category = $(this).val();
      evt.preventDefault();
      vsm.datatables.remove_no_results_row(table);
      table.find('tbody tr').hide();
      table.find('tbody tr.category-' + category).show();
      vsm.datatables.update_footer_count(table);
      vsm.datatables.add_no_results_row(table);
      vsm.datatables.fix_row_striping(table);
    });
    $(elm).find('div.table_filter button').each(function (i, button) {
      // Select the first non-empty category
      if ($(button).text().indexOf(' (0)') === -1) {
        $(button).addClass('active');
        $(button).trigger('click');
        return false;
      }
    });
  });
};

horizon.addInitFunction(function() {
  vsm.datatables.validate_button();
  vsm.datatables.disable_buttons();
  $('table.datatable').each(function (idx, el) {
    vsm.datatables.update_footer_count($(el), 0);
  });
  // Bind the "select all" checkbox action.
  $('div.table_wrapper, #modal_wrapper').on('click', 'table thead .multi_select_column .table-row-multi-select:checkbox', function(evt) {
    var $this = $(this),
      $table = $this.closest('table'),
      is_checked = $this.prop('checked'),
      checkboxes = $table.find('tbody .table-row-multi-select:visible:checkbox');
    checkboxes.prop('checked', is_checked);
  });
  // Change "select all" checkbox behaviour while any checkbox is checked/unchecked.
  $("div.table_wrapper, #modal_wrapper").on("click", 'table tbody .table-row-multi-select:checkbox', function (evt) {
    var $table = $(this).closest('table');
    var $multi_select_checkbox = $table.find('thead .multi_select_column .table-row-multi-select:checkbox');
    var any_unchecked = $table.find("tbody .table-row-multi-select:checkbox").not(":checked");
    $multi_select_checkbox.prop('checked', any_unchecked.length === 0);
  });
  // Enable dangerous buttons only if one or more checkbox is checked.
  $("div.table_wrapper, #modal_wrapper").on("click", '.table-row-multi-select:checkbox', function (evt) {
    var $form = $(this).closest("form");
    var any_checked = $form.find("tbody .table-row-multi-select:checkbox").is(":checked");
    if(any_checked) {
      $form.find(".table_actions button.btn-danger").removeClass("disabled");
    }else {
      $form.find(".table_actions button.btn-danger").addClass("disabled");
    }
  });

//  // Trigger run-once setup scripts for tables.
//  vsm.datatables.add_table_checkboxes($('body'));
  vsm.datatables.set_table_sorting($('body'));
//  vsm.datatables.set_table_query_filter($('body'));
//  vsm.datatables.set_table_fixed_filter($('body'));
//
//  // Also apply on tables in modal views.
//  horizon.modals.addModalInitFunction(vsm.datatables.add_table_checkboxes);
//  horizon.modals.addModalInitFunction(vsm.datatables.set_table_sorting);
//  horizon.modals.addModalInitFunction(vsm.datatables.set_table_query_filter);
//  horizon.modals.addModalInitFunction(vsm.datatables.set_table_fixed_filter);
//
//  // Also apply on tables in tabs views for lazy-loaded data.
//  //horizon.tabs.addTabLoadFunction(vsm.datatables.add_table_checkboxes);
//  //horizon.tabs.addTabLoadFunction(vsm.datatables.set_table_sorting);
//  //horizon.tabs.addTabLoadFunction(vsm.datatables.set_table_query_filter);
//  //horizon.tabs.addTabLoadFunction(vsm.datatables.set_table_fixed_filter);

  vsm.datatables.update();
});
