// Licensed to Cloudera, Inc. under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Cloudera, Inc. licenses this file
// to you 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.

var utils = {
  //take an element with mustache templates as content and re-render
  renderElement: function (element, data) {
    element.html(Mustache.render(element.html(), data));
  },
  renderElements: function (selector, data) {
    if (selector == null || typeof(selector) == "undefined")
      selector = '';
    $(selector).each(function () {
      utils._renderElement(this);
    });
  },
  renderPage: function (page_selector, data) {
    return utils.renderElements('.' + PAGE_TEMPLATE_PREFIX + page_selector, data);
  },
  setTitle: function (title) {
    $('.page-title').text(title);
    return this;
  },
  getTitle: function () {
    return $('.page-title').text();
  }
}


function hashToArray(hash) {
  var keys = Object.keys(hash);
  var output = [];
  for (var i = 0; i < keys.length; i++) {
    output.push({'key': keys[i], 'value': hash[keys[i]]});
  }
  return output;
}

function stringHashColor(str) {
  var r = 0, g = 0, b = 0, a = 0;
  for (var i = 0; i < str.length; i++) {
    var c = str.charCodeAt(i);
    a += c;
    r += Math.floor(Math.abs(Math.sin(c)) * a);
    g += Math.floor(Math.abs(Math.cos(c)) * a);
    b += Math.floor(Math.abs(Math.tan(c)) * a);
  }
  return 'rgb(' + (r % 190) + ',' + (g % 190) + ',' + (b % 190) + ')'; //always keep values under 180, to keep it darker
}

function scrollTo(posY) {
  $('html, body').animate({scrollTop: posY - 120}, 400);
}

function lockClickOrigin(func, origin) {
  return function (target, ev) {
    if (origin != ev.target)
      return function () {
      };
    return func(target, ev);
  };
}

function confirm(title, text, callback) {
  var modal = $('#confirm-modal');
  ko.cleanNode(modal[0]);
  modal.attr('data-bind', 'template: {name: "confirm_template"}');
  ko.applyBindings({
    title: title,
    text: text
  }, modal[0]);
  modal.find('.confirm-submit').click(callback);
  modal.modal('show');
}

function launchModal(modal, data) {
  var element = $('#' + modal);
  ko.cleanNode(element[0]);
  element.attr('data-bind', 'template: {name: "' + modal + '_template"}');
  ko.applyBindings(data, element[0]);
  element.is('.ajaxSubmit') ? element.submit(bindSubmit) : '';
  switch (modal) {
    case 'cell_edit_modal':
      if (data.mime().split('/')[0] == 'text') {
        var target = document.getElementById('ace_target_hidden');
        var mime = data.mime();
        var aceMode = 'ace/mode/text';
        switch (mime) {
          case 'text/json':
            aceMode = 'ace/mode/json';
            break;
          case 'text/xml':
            aceMode = 'ace/mode/xml';
            break;
        }
        var editor = ace.edit('ace_target');
        editor.setOptions({
          readOnly: $('#ace_target').is(':disabled')
        });
        editor.setTheme($.totalStorage('hue.ace.theme') || 'ace/theme/hue');
        editor.getSession().setMode(aceMode);
        editor.setValue($(target).val(), -1);

        data.ace = editor;

        var updateTimeout = 0;
        editor.on("change", function() {
          clearTimeout(updateTimeout);
          updateTimeout = setTimeout(function () {
            data.value(editor.getValue());
          }, 100);
        });

        data.showingCurrent.subscribe(function(showingCurrent) {
          editor.setOption('readOnly', !showingCurrent)
        });
      }
      app.focusModel(data.content);
      data.content.history.reload();

      if (data.content.parent) {
        var path = '/hbase/api/putUpload/"' + app.cluster() + '"/"' + app.views.tabledata.name() + '"/"' + data.content.parent.row + '"/"' + data.content.name + '"';
        var uploader = new qq.FileUploaderBasic({
          button: document.getElementById("file-upload-btn"),
          action: path,
          fileFieldLabel: 'hbase_file',
          multiple: false,
          onComplete: function (id, fileName, response) {
            data.content.reload(function() {
              data.value(data.content.value());
              data.mime(detectMimeType(data.value()))
            });
          }
        });
      }
      break;
    case 'new_row_modal':
      $('a.action_addColumnValue').click(function () {
        $(this).parent().find("ul").append("<li><input type=\"text\" name=\"column_values\" class=\"ignore\" placeholder = \"family:column_name\"/> <input type=\"text\" name=\"column_values\" class=\"ignore\" placeholder = \"cell_value\"/></li>")
      });
      break;
    case 'new_column_modal':
      var uploader = new qq.FileUploaderBasic({
        button: document.getElementById("column-upload-btn"),
        action: '',
        fileFieldLabel: 'hbase_file',
        multiple: false,
        onComplete: function (id, fileName, response) {
          if (response.status == null) {
            data.reload();
            element.modal('hide');
          } else {
            $(document).trigger("error", $(response.response).find('div.alert strong').text());
          }
        },
        onSubmit: function () {
          uploader._handler._options.action = '/hbase/api/putUpload/"' + app.cluster() + '"/"' + app.views.tabledata.name() + '"/' + prepForTransport(data.row) + '/"' + element.find('#new_column_name').val() + '"';
        }
      });
      break;
  }
  if (!element.hasClass('in'))
    element.modal('show');
  logGA(modal.slice(0, modal.indexOf('_modal') != -1 ? modal.indexOf('_modal') : modal.length));
}

function showFullEditor(cellContent) {
  var currentValue = ko.isObservable(cellContent.value) ? cellContent.value() : cellContent.value;
  launchModal('cell_edit_modal', {
    originalValue: currentValue,
    currentValue: ko.observable(currentValue),
    value: ko.observable(currentValue),
    showingCurrent: ko.observable(true),
    readOnly: ko.observable(false),
    timestamp: ko.observable(cellContent.timestamp),
    content: cellContent,
    mime: ko.observable(detectMimeType(currentValue)),

    updateAce: function() {
      if (this.ace) {
        this.ace.setValue(this.value(), -1);
      }
    },

    switchToHistorical: function(item) {
      if (this.showingCurrent()) {
        this.currentValue(this.value());
      }
      this.value(item.value);
      this.mime(detectMimeType(this.value()));
      this.updateAce();
      this.showingCurrent(false);
      this.timestamp(item.timestamp);
    },

    switchToCurrent: function() {
      if (!this.showingCurrent()) {
        this.value(this.currentValue());
        this.mime(detectMimeType(this.value()));
        this.showingCurrent(true);
        this.timestamp(this.content.timestamp);
        this.updateAce();
      }
    }
  });
}

function editCell($data) {
  if ($data.value().length > 146) {
    showFullEditor($data);
  } else {
    $data.editing(true);
  }
}

function parseXML(xml) {
  if (window.ActiveXObject) {
    var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.async = false;
    xmlDoc.loadXML(xml);
    return xmlDoc.xml;
  }
  if (window.DOMParser) {
    var xmlDoc = new DOMParser().parseFromString(xml, "text/xml");
    return new XMLSerializer().serializeToString(xmlDoc);
  }
  return xml;
}

function detectMimeType(data) {
  var MIME_TESTS = {
    'text/plain': function (data) {
      return !data;
    },
    'type/int': function (data) {
      return !isNaN(parseInt(data));
    },
    'text/json': function (data) {
      try {
        return JSON.parse(data);
      }
      catch (err) {
      }
    },
    'text/xml': function (data) {
      if(window.ActiveXObject) {
        return parseXML(data) != "";
      }
      return parseXML(data).indexOf('parsererror') == -1;
    }
  };
  var keys = Object.keys(MIME_TESTS);
  for (var i = 0; i < keys.length; i++) {
    if (MIME_TESTS[keys[i]](data))
      return keys[i];
  }
  //images
  var types = ['image/png', 'image/gif', 'image/jpg', 'application/pdf']
  var b64 = ['iVBORw', 'R0lG', '/9j/', 'JVBERi']
  try {
    var decoded = atob(data).toLowerCase().trim();
    for (var i = 0; i < types.length; i++) {
      var location = decoded.indexOf(types[i].split('/')[1]);
      if (location >= 0 && location < 10) //stupid guess
        return types[i];
    }
  }
  catch (error) {
  }
  for (var i = 0; i < types.length; i++) {
    if (data.indexOf(b64[i]) >= 0 && data.indexOf(b64[i]) <= 10)
      return types[i];
  }
  return 'type/null';
}

function convertTimestamp(timestamp) {
  var date = new Date(parseInt(timestamp));
  return date.toLocaleString();
}

function formatTimestamp(timestamp) {
  var date = new Date(parseInt(timestamp));
  return date.toUTCString();
}

function resetElements() {
  $(window).unbind('scroll');
  $(window).scroll(function (e) {
    $(".subnav.sticky").each(function () {
      var padder = $(this).data('padder'), top = $(this).position().top + (padder ? window.scrollY : 0);
      if (padder && top <= padder.position().top) {
        $(this).removeClass('subnav-fixed').data('padder').remove();
        $(this).removeData('padder');
      }
      else if (!padder && top <= window.scrollY + $('.navbar').outerHeight()) {
        $(this).addClass('subnav-fixed').data('padder', $('<div></div>').insertBefore($(this)).css('height', $(this).outerHeight()));
      }
    });
  });
  app.views.tabledata.showGrid(false);
};

function resetSearch() {
  app.views.tabledata.searchQuery('');
  app.search.cur_input('');
};

function prepForTransport(value) {
  value = value.replace(/\"/g, '\\\"').replace(/\//g, '\\/');
  if (isNaN(parseInt(value)) && value.trim() != '')
    value = '"' + value + '"';
  return encodeURIComponent(value);
};

function logGA(postfix) {
  if (postfix == null)
    postfix = "";
  if (typeof trackOnGA == 'function') {
    window.setTimeout(function () {
      trackOnGA('hbase/' + postfix);
    }, 10);
  }
};

function table_search(value) {
  routie(app.cluster() + '/' + app.views.tabledata.name() + '/query/' + value);
}

function getEditablePosition(contentEditable, trimWhitespaceNodes) {
  var el = contentEditable;
  if (window.getSelection().getRangeAt(0).startContainer == el) //raw reference for FF fix
    return 0;
  var index = window.getSelection().getRangeAt(0).startOffset; //ff
  var cur_node = window.getSelection().getRangeAt(0).startContainer; //ff
  while (cur_node != null && cur_node != el) {
    var cur_sib = cur_node.previousSibling || cur_node.previousElementSibling;
    while (cur_sib != null) {
      var val = $(cur_sib).text() || cur_sib.nodeValue;
      if (typeof val !== "undefined" && val != null) {
        index += trimWhitespaceNodes ? val.length : val.length;
      }
      cur_sib = cur_sib.previousSibling;
    }
    cur_node = cur_node.parentNode;
  }
  return index;
};

function setCursor(node, pos, trimWhitespaceNodes) {
  var sel = window.getSelection();
  var range = document.createRange();
  node = function selectNode(node) {
    var nodes = node.childNodes;
    if (pos > 0) {
      for (var i = 0; i < nodes.length; i++) {
        var val = trimWhitespaceNodes ? nodes[i].nodeValue.trim() : nodes[i].nodeValue;
        if (val) {
          if (val.length >= pos) {
            return nodes[i];
          } else {
            pos -= val.length;
          }
        }
        var n = selectNode(nodes[i]);
        if (n) return n;
      }
    }
    return false;
  }(node);
  try {
    range.setStart(node, pos);
    range.setEnd(node, pos);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    return range;
  } catch (err) {
  }
}

function pullFromRenderer(str, renderer) {
  try {
    return str.match(renderer.select)[0].match(renderer.tag)[0];
  } catch (e) {
    return "";
  }
}

window.selectIndex = null;
var fallback = typeof window.getSelection === "undefined";
ko.bindingHandlers.editableText = {
  init: function (element, valueAccessor, allBindingsAccessor) {
    $(element).on('keydown', function () {
      setTimeout(function () {
        var modelValue = valueAccessor();
        var elementValue = $(element).text();
        if (ko.isWriteableObservable(modelValue) && elementValue != modelValue()) {
          if (!fallback)
            window.selectIndex = getEditablePosition(element); //firefox does some tricky predictive stuff here
          modelValue(elementValue);
        }
        else { //handle non-observable one-way binding
          var allBindings = allBindingsAccessor();
          if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers'].htmlValue) allBindings['_ko_property_writers'].htmlValue(elementValue);
        }
      }, 1);
    });
  },
  update: function (element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor()) || "";
    if (value.trim() == "" && !app.search.focused()) {
      app.search.doBlur();
    } else {
      if (!fallback) {
        element.innerHTML = app.search.render(value, searchRenderers);
        if (window.selectIndex != null) {
          setCursor(element, window.selectIndex);
        }
      }
    }
  }
};