// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * Converts a number in bytes to a string in megabytes split by comma into
 * three digit block.
 * @param {number} bytes The number in bytes.
 * @return {string} Formatted string in megabytes.
 */
function ToMegaByteString(bytes) {
  var mb = Math.floor(bytes / (1 << 20));
  return mb.toString().replace(
      /\d+?(?=(\d{3})+$)/g,  // Digit sequence (\d+) followed (?=) by 3n digits.
      function(three_digit_block) { return three_digit_block + ','; }
  );
}

/**
 * Updates the Drive related Preferences section.
 * @param {Array} preferences List of dictionaries describing preferences.
 */
function updateDriveRelatedPreferences(preferences) {
  var ul = $('drive-related-preferences');
  updateKeyValueList(ul, preferences);
}

/**
 * Updates the Connection Status section.
 * @param {Object} connStatus Dictionary containing connection status.
 */
function updateConnectionStatus(connStatus) {
  $('connection-status').textContent = connStatus['status'];
  $('has-refresh-token').textContent = connStatus['has-refresh-token'];
  $('has-access-token').textContent = connStatus['has-access-token'];
}

/**
 * Updates the Path Configurations section.
 * @param {Array} paths List of dictionaries describing paths.
 */
function updatePathConfigurations(paths) {
  var ul = $('path-configurations');
  updateKeyValueList(ul, paths);
}

/**
 * Updates the GCache Contents section.
 * @param {Array} gcacheContents List of dictionaries describing metadata
 * of files and directories under the GCache directory.
 * @param {Object} gcacheSummary Dictionary of summary of GCache.
 */
function updateGCacheContents(gcacheContents, gcacheSummary) {
  var tbody = $('gcache-contents');
  for (var i = 0; i < gcacheContents.length; i++) {
    var entry = gcacheContents[i];
    var tr = document.createElement('tr');

    // Add some suffix based on the type.
    var path = entry.path;
    if (entry.is_directory)
      path += '/';
    else if (entry.is_symbolic_link)
      path += '@';

    tr.appendChild(createElementFromText('td', path));
    tr.appendChild(createElementFromText('td', entry.size));
    tr.appendChild(createElementFromText('td', entry.last_modified));
    tr.appendChild(createElementFromText('td', entry.permission));
    tbody.appendChild(tr);
  }

  $('gcache-summary-total-size').textContent =
      ToMegaByteString(gcacheSummary['total_size']);
}

/**
 * Updates the File System Contents section. The function is called from the
 * C++ side repeatedly with contents of a directory.
 * @param {string} directoryContentsAsText Pre-formatted string representation
 * of contents a directory in the file system.
 */
function updateFileSystemContents(directoryContentsAsText) {
  var div = $('file-system-contents');
  div.appendChild(createElementFromText('pre', directoryContentsAsText));
}

/**
 * Updates the Cache Contents section.
 * @param {Object} cacheEntry Dictionary describing a cache entry.
 * The function is called from the C++ side repeatedly.
 */
function updateCacheContents(cacheEntry) {
  var tr = document.createElement('tr');
  tr.appendChild(createElementFromText('td', cacheEntry.local_id));
  tr.appendChild(createElementFromText('td', cacheEntry.md5));
  tr.appendChild(createElementFromText('td', cacheEntry.is_present));
  tr.appendChild(createElementFromText('td', cacheEntry.is_pinned));
  tr.appendChild(createElementFromText('td', cacheEntry.is_dirty));

  $('cache-contents').appendChild(tr);
}

/**
 * Updates the Local Storage summary.
 * @param {Object} localStorageSummary Dictionary describing the status of local
 * stogage.
 */
function updateLocalStorageUsage(localStorageSummary) {
  var freeSpaceInMB = ToMegaByteString(localStorageSummary.free_space);
  $('local-storage-freespace').innerText = freeSpaceInMB;
}

/**
 * Updates the summary about in-flight operations.
 * @param {Array} inFlightOperations List of dictionaries describing the status
 * of in-flight operations.
 */
function updateInFlightOperations(inFlightOperations) {
  var container = $('in-flight-operations-contents');

  // Reset the table. Remove children in reverse order. Otherwides each
  // existingNodes[i] changes as a side effect of removeChild.
  var existingNodes = container.childNodes;
  for (var i = existingNodes.length - 1; i >= 0; i--) {
    var node = existingNodes[i];
    if (node.className == 'in-flight-operation')
      container.removeChild(node);
  }

  // Add in-flight operations.
  for (var i = 0; i < inFlightOperations.length; i++) {
    var operation = inFlightOperations[i];
    var tr = document.createElement('tr');
    tr.className = 'in-flight-operation';
    tr.appendChild(createElementFromText('td', operation.id));
    tr.appendChild(createElementFromText('td', operation.type));
    tr.appendChild(createElementFromText('td', operation.file_path));
    tr.appendChild(createElementFromText('td', operation.state));
    var progress = operation.progress_current + '/' + operation.progress_total;
    if (operation.progress_total > 0) {
      var percent = operation.progress_current / operation.progress_total * 100;
      progress += ' (' + Math.round(percent) + '%)';
    }
    tr.appendChild(createElementFromText('td', progress));

    container.appendChild(tr);
  }
}

/**
 * Updates the summary about about resource.
 * @param {Object} aboutResource Dictionary describing about resource.
 */
function updateAboutResource(aboutResource) {
  var quotaTotalInMb = ToMegaByteString(aboutResource['account-quota-total']);
  var quotaUsedInMb = ToMegaByteString(aboutResource['account-quota-used']);

  $('account-quota-info').textContent =
      quotaUsedInMb + ' / ' + quotaTotalInMb + ' (MB)';
  $('account-largest-changestamp-remote').textContent =
      aboutResource['account-largest-changestamp-remote'];
  $('root-resource-id').textContent = aboutResource['root-resource-id'];
}

/**
 * Updates the summary about app list.
 * @param {Object} appList Dictionary describing app list.
 */
function updateAppList(appList) {
  $('app-list-etag').textContent = appList['etag'];

  var itemContainer = $('app-list-items');
  for (var i = 0; i < appList['items'].length; i++) {
    var app = appList['items'][i];
    var tr = document.createElement('tr');
    tr.className = 'installed-app';
    tr.appendChild(createElementFromText('td', app.name));
    tr.appendChild(createElementFromText('td', app.application_id));
    tr.appendChild(createElementFromText('td', app.object_type));
    tr.appendChild(createElementFromText('td', app.supports_create));

    itemContainer.appendChild(tr);
  }
}

/**
 * Updates the local cache information about account metadata.
 * @param {Object} localMetadata Dictionary describing account metadata.
 */
function updateLocalMetadata(localMetadata) {
  var changestamp = localMetadata['account-largest-changestamp-local'];

  $('account-largest-changestamp-local').textContent =
      changestamp.toString() +
      (changestamp > 0 ? ' (loaded)' : ' (not loaded)') +
      (localMetadata['account-metadata-refreshing'] ? ' (refreshing)' : '');
}

/**
 * Updates the summary about delta update status.
 * @param {Object} deltaUpdateStatus Dictionary describing delta update status.
 */
function updateDeltaUpdateStatus(deltaUpdateStatus) {
  $('push-notification-enabled').textContent =
        deltaUpdateStatus['push-notification-enabled'];
  $('last-update-check-time').textContent =
        deltaUpdateStatus['last-update-check-time'];
  $('last-update-check-error').textContent =
        deltaUpdateStatus['last-update-check-error'];
}

/**
 * Updates the event log section.
 * @param {Array} log Array of events.
 */
function updateEventLog(log) {
  var ul = $('event-log');
  updateKeyValueList(ul, log);
}

/**
 * Creates an element named |elementName| containing the content |text|.
 * @param {string} elementName Name of the new element to be created.
 * @param {string} text Text to be contained in the new element.
 * @return {HTMLElement} The newly created HTML element.
 */
function createElementFromText(elementName, text) {
  var element = document.createElement(elementName);
  element.appendChild(document.createTextNode(text));
  return element;
}

/**
 * Updates <ul> element with the given key-value list.
 * @param {HTMLElement} ul <ul> element to be modified.
 * @param {Array} list List of dictionaries containing 'key', 'value' (optional)
 * and 'class' (optional). For each element <li> element with specified class is
 * created.
 */
function updateKeyValueList(ul, list) {
  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var text = item.key;
    if (item.value != '')
      text += ': ' + item.value;

    var li = createElementFromText('li', text);
    if (item.class)
      li.classList.add(item.class);
    ul.appendChild(li);
  }
}

/**
 * Updates the text next to the 'reset' button to update the status.
 * @param {boolean} success whether or not resetting has succeeded.
 */
function updateResetStatus(success) {
  $('reset-status-text').textContent = (success ? 'success' : 'failed');
}

document.addEventListener('DOMContentLoaded', function() {
  chrome.send('pageLoaded');

  // Update the table of contents.
  var toc = $('toc');
  var sections = document.getElementsByTagName('h2');
  for (var i = 0; i < sections.length; i++) {
    var section = sections[i];
    var a = createElementFromText('a', section.textContent);
    a.href = '#' + section.id;
    var li = document.createElement('li');
    li.appendChild(a);
    toc.appendChild(li);
  }

  $('button-clear-access-token').addEventListener('click', function() {
    chrome.send('clearAccessToken');
  });

  $('button-clear-refresh-token').addEventListener('click', function() {
    chrome.send('clearRefreshToken');
  });

  $('button-reset-drive-filesystem').addEventListener('click', function() {
    $('reset-status-text').textContent = 'resetting...';
    chrome.send('resetDriveFileSystem');
  });

  $('button-show-file-entries').addEventListener('click', function() {
    var button = $('button-show-file-entries');
    button.parentNode.removeChild(button);
    chrome.send('listFileEntries');
  });

  window.setInterval(function() {
    chrome.send('periodicUpdate');
  }, 1000);
});
