text: /*global Dropbox */
  define([
      "jquery",
      "underscore",
      "constants",
      "core",
      "utils",
      "storage",
      "logger",
      "settings",
      "eventMgr",
      "classes/AsyncTask",
  ], function($, _, constants, core, utils, storage, logger, settings, eventMgr, AsyncTask) {
  
      var client;
      var authenticated = false;
  
      var dropboxHelper = {};
      
      // Listen to offline status changes
      var isOffline = false;
      eventMgr.addListener("onOfflineChanged", function(isOfflineParam) {
          isOffline = isOfflineParam;
      });
  
      // Try to connect dropbox by downloading client.js
      function connect(task) {
          task.onRun(function() {
              if(isOffline === true) {
                  client = undefined;
                  task.error(new Error("Operation not available in offline mode.|stopPublish"));
                  return;
              }
              if(client !== undefined) {
                  task.chain();
                  return;
              }
              $.ajax({
                  url: "libs/dropbox.min.js",
                  dataType: "script",
                  timeout: constants.AJAX_TIMEOUT
              }).done(function() {
                  client = new Dropbox.Client({
                      key: settings.dropboxFullAccess === true ? constants.DROPBOX_APP_KEY : constants.DROPBOX_RESTRICTED_APP_KEY,
                      secret: settings.dropboxFullAccess === true ? constants.DROPBOX_APP_SECRET : constants.DROPBOX_RESTRICTED_APP_SECRET
                  });
                  client.authDriver(new Dropbox.AuthDriver.Popup({
                      receiverUrl: constants.BASE_URL + "html/dropbox-oauth-receiver.html",
                      rememberUser: true
                  }));
                  task.chain();
              }).fail(function(jqXHR) {
                  var error = {
                      status: jqXHR.status,
                      responseText: jqXHR.statusText
                  };
                  handleError(error, task);
              });
          });
      }
  
      // Try to authenticate with Oauth
      function authenticate(task) {
          task.onRun(function() {
              if(authenticated === true) {
                  task.chain();
                  return;
              }
              var immediate = true;
              function oauthRedirect() {
                  utils.redirectConfirm('You are being redirected to <strong>Dropbox</strong> authorization page.', function() {
                      task.chain(localAuthenticate);
                  }, function() {
                      task.error(new Error('Operation canceled.'));
                  });
              }
              function localAuthenticate() {
                  if(immediate === false) {
                      // If not immediate we add time for user to enter his
                      // credentials
                      task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT;
                  }
                  else {
                      client.reset();
                  }
                  client.authenticate({
                      interactive: !immediate
                  }, function(error, client) {
                      // Success
                      if(client.isAuthenticated() === true) {
                          authenticated = true;
                          task.chain();
                          return;
                      }
                      // If immediate did not work retry without immediate flag
                      if(immediate === true) {
                          immediate = false;
                          task.chain(oauthRedirect);
                          return;
                      }
                      // Error
                      task.error(new Error("Access to Dropbox account is not authorized."));
                  });
              }
              task.chain(localAuthenticate);
          });
      }
  
      dropboxHelper.upload = function(path, content, callback) {
          var result;
          var task = new AsyncTask();
          connect(task);
          authenticate(task);
          task.onRun(function() {
              client.writeFile(path, content, function(error, stat) {
                  if(!error) {
                      result = stat;
                      task.chain();
                      return;
                  }
                  // Handle error
                  if(error.status === 400) {
                      error = 'Could not upload document into path "' + path + '".';
                  }
                  handleError(error, task);
              });
          });
          task.onSuccess(function() {
              callback(undefined, result);
          });
          task.onError(function(error) {
              callback(error);
          });
          task.enqueue();
      };
  
      dropboxHelper.checkChanges = function(lastChangeId, callback) {
          var changes = [];
          var newChangeId = lastChangeId || 0;
          var task = new AsyncTask();
          connect(task);
          authenticate(task);
          task.onRun(function() {
              function retrievePageOfChanges() {
                  client.pullChanges(newChangeId, function(error, pullChanges) {
                      if(error) {
                          handleError(error, task);
                          return;
                      }
                      // Retrieve success
                      newChangeId = pullChanges.cursor();
                      if(pullChanges.changes !== undefined) {
                          changes = changes.concat(pullChanges.changes);
                      }
                      if(pullChanges.shouldPullAgain) {
                          task.chain(retrievePageOfChanges);
                      }
                      else {
                          task.chain();
                      }
                  });
              }
              task.chain(retrievePageOfChanges);
          });
          task.onSuccess(function() {
              callback(undefined, changes, newChangeId);
          });
          task.onError(function(error) {
              callback(error);
          });
          task.enqueue();
      };
  
      dropboxHelper.downloadMetadata = function(paths, callback) {
          var result = [];
          var task = new AsyncTask();
          connect(task);
          authenticate(task);
          task.onRun(function() {
              function recursiveDownloadMetadata() {
                  if(paths.length === 0) {
                      task.chain();
                      return;
                  }
                  var path = paths[0];
                  client.stat(path, function(error, stat) {
                      if(stat) {
                          result.push(stat);
                          paths.shift();
                          task.chain(recursiveDownloadMetadata);
                          return;
                      }
                      handleError(error, task);
                  });
              }
              task.chain(recursiveDownloadMetadata);
          });
          task.onSuccess(function() {
              callback(undefined, result);
          });
          task.onError(function(error) {
              callback(error);
          });
          task.enqueue();
      };
  
      dropboxHelper.downloadContent = function(objects, callback) {
          var result = [];
          var task = new AsyncTask();
          connect(task);
          authenticate(task);
          task.onRun(function() {
              function recursiveDownloadContent() {
                  if(objects.length === 0) {
                      task.chain();
                      return;
                  }
                  var object = objects[0];
                  result.push(object);
                  var file;
                  // object may be a file
                  if(object.isFile === true) {
                      file = object;
                  }
                  // object may be a change
                  else if(object.wasRemoved !== undefined) {
                      file = object.stat;
                  }
                  if(!file) {
                      objects.shift();
                      task.chain(recursiveDownloadContent);
                      return;
                  }
                  client.readFile(file.path, function(error, data) {
                      if(_.isString(data)) {
                          file.content = data;
                          objects.shift();
                          task.chain(recursiveDownloadContent);
                          return;
                      }
                      handleError(error, task);
                  });
              }
              task.chain(recursiveDownloadContent);
          });
          task.onSuccess(function() {
              callback(undefined, result);
          });
          task.onError(function(error) {
              callback(error);
          });
          task.enqueue();
      };
  
      function handleError(error, task) {
          var errorMsg = true;
          if(error) {
              logger.error(error);
              // Try to analyze the error
              if(typeof error === "string") {
                  errorMsg = error;
              }
              else {
                  errorMsg = "Dropbox error (" + error.status + ": " + error.responseText + ").";
  
                  if(error.status === 401 || error.status === 403) {
                      authenticated = false;
                      errorMsg = "Access to Dropbox account is not authorized.";
                      task.retry(new Error(errorMsg), 1);
                      return;
                  }
                  else if(error.status === 400 && error.responseText.indexOf("oauth_nonce") !== -1) {
                      // A bug I guess...
                      _.each(_.keys(storage), function(key) {
                          // We have to remove the Oauth cache from the
                          // storage
                          if(key.indexOf("dropbox-auth") === 0) {
                              storage.removeItem(key);
                          }
                      });
                      authenticated = false;
                      task.retry(new Error(errorMsg), 1);
                      return;
                  }
                  else if(error.status <= 0) {
                      client = undefined;
                      authenticated = false;
                      core.setOffline();
                      errorMsg = "|stopPublish";
                  }
              }
          }
          task.error(new Error(errorMsg));
      }
  
      var pickerLoaded = false;
      function loadPicker(task) {
          task.onRun(function() {
              if(pickerLoaded === true) {
                  task.chain();
                  return;
              }
              function chooserRedirect() {
                  utils.redirectConfirm('You are being redirected to <strong>Dropbox Chooser</strong> page.', function() {
                      task.chain();
                  }, function() {
                      task.error(new Error('Operation canceled.'));
                  });
              }
              $.ajax({
                  url: "https://www.dropbox.com/static/api/1/dropbox.js",
                  dataType: "script",
                  timeout: constants.AJAX_TIMEOUT
              }).done(function() {
                  pickerLoaded = true;
                  task.chain(chooserRedirect);
              }).fail(function(jqXHR) {
                  var error = {
                      status: jqXHR.status,
                      responseText: jqXHR.statusText
                  };
                  handleError(error, task);
              });
          });
      }
  
      dropboxHelper.picker = function(callback) {
          var paths = [];
          var task = new AsyncTask();
          // Add some time for user to choose his files
          task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT;
          connect(task);
          loadPicker(task);
          task.onRun(function() {
              var options = {};
              options.multiselect = true;
              options.linkType = "direct";
              options.success = function(files) {
                  for ( var i = 0; i < files.length; i++) {
                      var path = files[i].link;
                      path = path.replace(/.*\/view\/[^\/]*/, "");
                      paths.push(decodeURI(path));
                  }
                  task.chain();
              };
              options.cancel = function() {
                  task.chain();
              };
              Dropbox.choose(options);
          });
          task.onSuccess(function() {
              callback(undefined, paths);
          });
          task.onError(function(error) {
              callback(error);
          });
          task.enqueue();
      };
  
      return dropboxHelper;
  });
  