"use strict";

/* exported logger */

var Promise = require("bluebird");

var logger = require('./logger');

var request = require('request');

var HttpStatus = require('http-status-codes');

var Alias = require('./map/data/Alias');
var Annotation = require('./map/data/Annotation');
var Chemical = require('./map/data/Chemical');
var Comment = require('./map/data/Comment');
var Configuration = require('./Configuration');
var Drug = require('./map/data/Drug');
var ConfigurationType = require('./ConfigurationType');
var IdentifiedElement = require('./map/data/IdentifiedElement');
var InvalidCredentialsError = require('./InvalidCredentialsError');
var LayoutAlias = require('./map/data/LayoutAlias');
var LayoutData = require('./map/data/LayoutData');
var LayoutReaction = require('./map/data/LayoutReaction');
var MapModel = require('./map/data/MapModel');
var Mesh = require('./map/data/Mesh');
var MiRna = require('./map/data/MiRna');
var NetworkError = require('./NetworkError');
var Project = require('./map/data/Project');
var ProjectStatistics = require('./map/data/ProjectStatistics');
var Reaction = require('./map/data/Reaction');
var ReferenceGenome = require('./map/data/ReferenceGenome');
var SbmlFunction = require('./map/data/SbmlFunction');
var SbmlParameter = require('./map/data/SbmlParameter');
var SecurityError = require('./SecurityError');
var SessionData = require('./SessionData');
var User = require('./map/data/User');

var GuiConnector = require('./GuiConnector');

var ObjectWithListeners = require('./ObjectWithListeners');

/**
 * This object contains methods that will communicate with server.
 */
var ServerConnector = new ObjectWithListeners();
ServerConnector.init = function () {
  var self = this;

  self._configurationParam = [];
  self._projects = [];
  self._projectsById = [];

  self._users = [];
  self._usersByLogin = [];

  self._customMap = null;
  self._sessionData = undefined;
  self._configuration = undefined;
  self._loggedUser = undefined;
  self._serverBaseUrl = undefined;

  var i;
  var listeners = self.getListeners("onDataLoadStart");
  for (i = 0; i < listeners.length; i++) {
    self.removeListener("onDataLoadStart", listeners[i]);
  }

  listeners = self.getListeners("onDataLoadStop");
  for (i = 0; i < listeners.length; i++) {
    self.removeListener("onDataLoadStop", listeners[i]);
  }

};
ServerConnector.registerListenerType("onDataLoadStart");
ServerConnector.registerListenerType("onDataLoadStop");
ServerConnector.init();

ServerConnector.getMinOverlayColorInt = function () {
  var self = this;
  return self.getLoggedUser().then(function (user) {
    var userColor = user.getMinColor();
    return self.returnUserOrSystemColor(userColor, self.getConfigurationParam(ConfigurationType.MIN_COLOR_VAL));
  });
};

ServerConnector.returnUserOrSystemColor = function (userColor, systemPromisedColor) {
  return systemPromisedColor.then(function (systemColor) {
    var color = userColor;
    if (userColor === null || userColor === undefined || userColor === "") {
      color = systemColor;
    }
    color = parseInt(color, 16);
    /* jslint bitwise: true */
    color = (color & 0xFFFFFF);
    return color;
  });
};

ServerConnector.getSimpleOverlayColorInt = function () {
  var self = this;
  return self.getLoggedUser().then(function (user) {
    var userColor = user.getSimpleColor();
    return self.returnUserOrSystemColor(userColor, self.getConfigurationParam(ConfigurationType.SIMPLE_COLOR_VAL));
  });
};

ServerConnector.getMaxOverlayColorInt = function () {
  var self = this;
  return self.getLoggedUser().then(function (user) {
    var userColor = user.getMaxColor();
    return self.returnUserOrSystemColor(userColor, self.getConfigurationParam(ConfigurationType.MAX_COLOR_VAL));
  });
};

ServerConnector.getNeutralOverlayColorInt = function () {
  var self = this;
  return self.getLoggedUser().then(function (user) {
    var userColor = user.getNeutralColor();
    return self.returnUserOrSystemColor(userColor, self.getConfigurationParam(ConfigurationType.NEUTRAL_COLOR_VAL));
  });
};

ServerConnector.sendGetRequest = function (url, description) {
  return this.sendRequest({
    url: url,
    description: description,
    method: "GET"
  });
};

ServerConnector.sendRequest = function (params) {
  var self = this;
  if (arguments.length > 1) {
    return Promise.reject(new Error("Only two arguments are supported"));
  }

  if (self.getSessionData().getToken() === undefined) {
    self.getSessionData().setLogin(undefined);
    window.location.reload(false);
  }

  var description = params.url;
  if (params.description !== undefined) {
    description = params.description;
    params.description = undefined;
  }

  var content;
  return self.callListeners("onDataLoadStart", description).then(function () {
    return self._sendRequest(params);
  }).then(function (result) {
    content = result;
    return self.callListeners("onDataLoadStop", description);
  }, function (error) {
    return self.callListeners("onDataLoadStop", description).then(function () {
      return Promise.reject(error);
    });
  }).then(function () {
    return content;
  }, function (error) {
    if (error instanceof NetworkError) {
      if (error.statusCode === HttpStatus.FORBIDDEN && error.content.indexOf('"reason":"Invalid token"') >= 0) {
        self.getSessionData().setToken(undefined);
        if (self.getSessionData().getLogin() === "anonymous") {
          window.location.reload(false);
        } else {
          window.location.href = ServerConnector.getServerBaseUrl() + "login.xhtml?from=" + encodeURI(window.location.href);
        }
      }
    }
    return Promise.reject(error);
  });

};

ServerConnector._sendRequest = function (params) {
  return new Promise(function (resolve, reject) {
    request(params, function (error, response, body) {
      if (error) {
        reject(new NetworkError(error.message, {
          content: body,
          url: params.url
        }));
      } else if (response.statusCode !== 200) {
        reject(new NetworkError(params.url + " rejected with status code: " + response.statusCode, {
          content: body,
          url: params.url,
          statusCode: response.statusCode
        }));
      } else {
        // for some reason sometimes result is an object not a string
        if (typeof body === 'string' || body instanceof String) {
          resolve(body);
        } else {
          resolve(JSON.stringify(body));
        }
      }
    });
  });
};

ServerConnector.sendPostRequest = function (url, params) {
  return this.sendRequest({
    method: "POST",
    url: url,
    form: params
  });
};

ServerConnector.sendDeleteRequest = function (url, json) {
  return this.sendRequest({
    method: "DELETE",
    url: url,
    json: json
  });
};

ServerConnector.sendPatchRequest = function (url, json) {
  return this.sendRequest({
    method: "PATCH",
    url: url,
    json: json
  });
};

ServerConnector.getToken = function () {
  var self = this;

  var login = self.getSessionData(null).getLogin();
  var token = self.getSessionData(null).getToken();
  if (token === undefined || login === undefined) {
    return self.login();
  } else {
    // if the project is not initialized then check if we can download data
    // using current token
    if (self.getSessionData().getProject() === null) {
      return self.getConfiguration().then(function () {
        return token;
        // if there was an error accessing configuration it means our token is
        // invalid
      }, function () {
        return self.login();
      });
    } else {
      return Promise.resolve(token);
    }
  }
};

ServerConnector.getApiBaseUrl = function () {
  return this.getServerBaseUrl() + "/api/";
};

ServerConnector.getServerBaseUrl = function () {
  if (this._serverBaseUrl === undefined) {
    var url = "" + window.location.href;
    if (url.indexOf("?") >= 0) {
      url = url.substr(0, url.indexOf("?"));
    }
    if (!url.endsWith("/")) {
      url = url.substr(0, url.lastIndexOf("/") + 1);
    }
    this._serverBaseUrl = url;
  }
  return this._serverBaseUrl;
};

ServerConnector.createGetParams = function (params, prefix) {
  var sorted = [], key;

  for (key in params) {
    if (params.hasOwnProperty(key)) {
      sorted.push(key);
    }
  }
  sorted.sort();

  var result = "";
  for (var i = 0; i < sorted.length; i++) {
    key = sorted[i];
    var value = params[key];
    if (prefix !== undefined) {
      key = prefix + "." + key;
    }

    if (value instanceof google.maps.Point) {
      value = this.pointToString(value);
    } else if (Object.prototype.toString.call(value) === '[object Array]') {
      value = this.idsToString(value);
    } else if (typeof value === 'string' || value instanceof String || !isNaN(value)) {

    } else {
      result += this.createGetParams(value, key);
      value = undefined;
    }

    if (value !== undefined && value !== "") {
      result += key + "=" + value + "&";
    }
  }
  return result;
};

ServerConnector.getApiUrl = function (paramObj) {
  var type = paramObj.type;
  var params = this.createGetParams(paramObj.params);

  var result = paramObj.url;
  if (result === undefined) {
    result = this.getApiBaseUrl() + "/" + type;
  }
  if (params !== "") {
    result += "?" + params;
  }
  return result;
};

ServerConnector.getProjectsUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    type: "projects/",
    params: filterParams
  });
};

ServerConnector.getProjectUrl = function (queryParams, filterParams) {
  var id = this.getIdOrAsterisk(queryParams.projectId);
  return this.getApiUrl({
    url: this.getProjectsUrl(queryParams) + id + "/",
    params: filterParams
  });
};

ServerConnector.getProjectStatisticsUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "statistics/",
    params: filterParams
  });
};

ServerConnector.getPublicationsUrl = function (queryParams, filterParams) {
  filterParams.start = filterParams.start || 0;
  filterParams.length = filterParams.length || 10;

  return this.getApiUrl({
    url: this.getModelsUrl(queryParams) + "publications/",
    params: filterParams
  });
};
ServerConnector.getProjectLogsUrl = function (queryParams, filterParams) {
  filterParams.start = filterParams.start || 0;
  filterParams.length = filterParams.length || 10;

  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "logs/",
    params: filterParams
  });
};
ServerConnector.getMeshUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    type: "mesh/" + queryParams.id,
    params: filterParams
  });
};

ServerConnector.getSbmlFunctionUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getModelsUrl(queryParams) + "functions/" + queryParams.functionId,
    params: filterParams
  });
};

ServerConnector.getSbmlParameterUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getModelsUrl(queryParams) + "parameters/" + queryParams.parameterId,
    params: filterParams
  });
};

ServerConnector.getReferenceGenomeUrl = function (queryParams, filterParams) {
  var version = this.getIdOrAsterisk(queryParams.version);

  return this.getApiUrl({
    type: "genomics/taxonomies/" + queryParams.organism + "/genomeTypes/" + queryParams.type + "/versions/" + version
    + "/",
    params: filterParams
  });
};

ServerConnector.loginUrl = function () {
  return this.getApiUrl({
    type: "/doLogin"
  });
};

ServerConnector.logoutUrl = function () {
  return this.getApiUrl({
    type: "/doLogout"
  });
};

ServerConnector.getSuggestedQueryListUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getBioEntitiesUrl(queryParams) + "suggestedQueryList/",
    params: filterParams
  });
};
ServerConnector.getChemicalSuggestedQueryListUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "chemicals/suggestedQueryList",
    params: filterParams
  });
};
ServerConnector.getDrugSuggestedQueryListUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "drugs/suggestedQueryList",
    params: filterParams
  });
};
ServerConnector.getMiRnaSuggestedQueryListUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "miRnas/suggestedQueryList",
    params: filterParams
  });
};

ServerConnector.addCommentUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getCommentsUrl(queryParams)
  });
};

ServerConnector.addOverlayUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getOverlaysUrl(queryParams)
  });
};

ServerConnector.updateOverlayUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getOverlayByIdUrl(queryParams)
  });
};

ServerConnector.updateModelUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getModelsUrl(queryParams)
  });
};

ServerConnector.deleteOverlayUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getOverlayByIdUrl(queryParams)
  });
};

ServerConnector.deleteCommentUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "comments/" + queryParams.commentId + "/"
  });
};

ServerConnector.getOverlaysUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "overlays/",
    params: filterParams
  });
};

ServerConnector.getCommentsUrl = function (queryParams, filterParams) {
  var modelId = this.getIdOrAsterisk(queryParams.modelId);
  var url = this.getProjectUrl(queryParams) + "comments/models/" + modelId + "/";
  if (queryParams.elementType !== undefined) {
    if (queryParams.elementType === "ALIAS") {
      url += "bioEntities/elements/" + queryParams.elementId;
    } else if (queryParams.elementType === "REACTION") {
      url += "bioEntities/reactions/" + queryParams.elementId;
    } else if (queryParams.elementType === "POINT") {
      url += "points/" + queryParams.coordinates;
    } else {
      throw new Error("Unknown element type: " + queryParams.elementType);
    }
  }
  return this.getApiUrl({
    url: url,
    params: filterParams
  });
};

ServerConnector.getOverlayByIdUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getOverlaysUrl(queryParams) + queryParams.overlayId + "/",
    params: filterParams
  });

};

ServerConnector.getOverlayElementsUrl = function (queryParams, filterParams) {

  return this.getApiUrl({
    url: this.getModelsUrl(queryParams) + "bioEntities/",
    params: filterParams
  });

};

ServerConnector.getFullOverlayElementUrl = function (queryParams, filterParams) {

  return this.getApiUrl({
    url: this.getAliasesUrl(queryParams) + queryParams.id + "/",
    params: filterParams
  });

};

ServerConnector.idsToString = function (ids) {
  var result = "";
  if (ids !== undefined) {
    ids.sort(function (a, b) {
      if (typeof a === "string") {
        return a.localeCompare(b);
      } else {
        return a - b;
      }
    });
    for (var i = 0; i < ids.length; i++) {
      if (result !== "") {
        if (ids[i - 1] !== ids[i]) {
          result = result + "," + ids[i];
        } // we ignore duplicates
      } else {
        result = ids[i];
      }
    }
  }
  return result;
};

ServerConnector.pointToString = function (point) {
  return point.x.toFixed(2) + "," + point.y.toFixed(2);
};

ServerConnector.getModelsUrl = function (queryParams) {
  var modelId = this.getIdOrAsterisk(queryParams.modelId);
  var overlayId = queryParams.overlayId;
  var url = this.getProjectUrl(queryParams);
  if (overlayId !== undefined) {
    url = this.getOverlayByIdUrl(queryParams);
  }

  return this.getApiUrl({
    url: url + "models/" + modelId + "/"
  });
};

ServerConnector.getBioEntitiesUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getModelsUrl(queryParams) + "bioEntities/"
  });
};

ServerConnector.getIdOrAsterisk = function (id) {
  if (id === undefined || id === "" || id === null) {
    return "*";
  } else {
    return id;
  }
};

ServerConnector.getReactionsUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getBioEntitiesUrl(queryParams) + "reactions/",
    params: filterParams
  });
};

ServerConnector.getAliasesUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getBioEntitiesUrl(queryParams) + "elements/",
    params: filterParams
  });
};

ServerConnector.getConfigurationUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    type: "configuration/",
    params: filterParams
  });
};

ServerConnector.getConfigurationOptionUrl = function (queryParams, filterParams) {
  var self = this;
  return self.getApiUrl({
    url: self.getConfigurationUrl() + "options/" + queryParams.type,
    params: filterParams
  });
};


ServerConnector.getSearchUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getModelsUrl(queryParams) + "bioEntities:search",
    params: filterParams
  });
};

ServerConnector.getSearchDrugsUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "drugs:search",
    params: filterParams
  });
};

ServerConnector.getSearchMiRnasUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "miRnas:search",
    params: filterParams
  });
};

ServerConnector.getSearchChemicalsUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "chemicals:search",
    params: filterParams
  });
};

ServerConnector.getOverlaySourceUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getOverlaysUrl(queryParams) + queryParams.overlayId + ":downloadSource",
    params: filterParams
  });
};

ServerConnector.getImageUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "models/" + queryParams.modelId + ":downloadImage",
    params: filterParams
  });
};

ServerConnector.getModelPartUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getProjectUrl(queryParams) + "models/" + queryParams.modelId + ":downloadModel",
    params: filterParams
  });
};

ServerConnector.getProjectSourceUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    type: "projects/" + queryParams.projectId + ":downloadSource",
    params: filterParams
  });
};


ServerConnector.getFilesUrl = function () {
  return this.getApiUrl({
    type: "files/"
  });
};

ServerConnector.getCreateFileUrl = function () {
  return this.getApiUrl({
    url: this.getFilesUrl()
  });
};
ServerConnector.getFileUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getFilesUrl() + "/" + queryParams.id
  });
};
ServerConnector.getUploadFileUrl = function (queryParams) {
  return this.getApiUrl({
    url: this.getFileUrl(queryParams) + ":uploadContent"
  });
};


ServerConnector.getUsersUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    type: "users/",
    params: filterParams
  });
};

ServerConnector.getUserUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getUsersUrl() + queryParams.login,
    params: filterParams
  });
};

ServerConnector.getUpdateUserPrivilegesUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getUserUrl(queryParams) + ":updatePrivileges",
    params: filterParams
  });
};
ServerConnector.getUpdateUserPreferencesUrl = function (queryParams, filterParams) {
  return this.getApiUrl({
    url: this.getUserUrl(queryParams) + ":updatePreferences",
    params: filterParams
  });
};

ServerConnector.getConfiguration = function () {
  var self = this;
  if (this._configuration === undefined) {
    return self.sendGetRequest(self.getConfigurationUrl()).then(function (content) {
      self._configuration = new Configuration(JSON.parse(content));
      return Promise.resolve(self._configuration);
    });
  } else {
    return Promise.resolve(self._configuration);
  }
};

ServerConnector.getConfigurationParam = function (paramId) {
  if (paramId === undefined) {
    return Promise.reject(new Error("Unknown param type"));
  }
  var self = this;
  return self.getConfiguration().then(function (configuration) {
    var option = configuration.getOption(paramId);
    if (option.getValue !== undefined) {
      return option.getValue();
    } else {
      return option;
    }
  });
};
ServerConnector.updateConfigurationOption = function (option) {
  var self = this;
  var queryParams = {
    type: option.getType()
  };
  var filterParams = {
    option: {
      type: option.getType(),
      value: option.getValue()
    }
  };
  return self.sendPatchRequest(self.getConfigurationOptionUrl(queryParams), filterParams);
};

ServerConnector.getModels = function (projectId) {
  var queryParams = {};
  var filterParams = {};
  var self = this;
  return self.getProjectId(projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getModelsUrl(queryParams, filterParams));
  }).then(function (content) {
    var models = [];
    var parsedJson = JSON.parse(content);
    for (var i = 0; i < parsedJson.length; i++) {
      models.push(new MapModel(parsedJson[i]));
    }
    return models;
  });
};

ServerConnector.getProject = function (projectId) {
  var queryParams = {};
  var filterParams = {};
  var project;
  var self = this;
  return self.getProjectId(projectId).then(function (result) {
    projectId = result;
    queryParams.projectId = result;
    return self.sendGetRequest(self.getProjectUrl(queryParams, filterParams));
  }).then(function (content) {
    var downloadedProject = new Project(content);
    if (self._projectsById[projectId] !== undefined) {
      self._projectsById[projectId].update(downloadedProject);
    } else {
      self._projectsById[projectId] = downloadedProject;
    }
    project = self._projectsById[projectId];
    return self.getModels(projectId);
  }).then(function (models) {
    project.setModel(models[0]);
    return self.getLoggedUser();
  }).then(function (user) {
    return self.getOverlays({
      projectId: projectId,
      creator: user.getLogin(),
      publicOverlay: false
    });
  }).then(function (overlays) {
    if (project.getModel() !== undefined) {
      project.getModel().addLayouts(overlays);
    } else {
      if (overlays.length > 0) {
        logger.warn("Cannot add overlays to the project: " + project.getProjectId());
      }
    }
    return project;
  }).then(null, function (error) {
    if ((error instanceof NetworkError)) {
      switch (error.statusCode) {
        case HttpStatus.NOT_FOUND:
          return null;
        case HttpStatus.FORBIDDEN:
          return Promise.reject(new SecurityError("Access denied."));
        default:
          return Promise.reject(error);
      }
    } else {
      return Promise.reject(error);
    }
  });
};

ServerConnector.updateProject = function (project) {
  var self = this;
  var queryParams = {
    projectId: project.getProjectId()
  };
  var filterParams = {
    project: {
      name: project.getName(),
      version: project.getVersion(),
      notifyEmail: project.getNotifyEmail(),
      organism: self.serialize(project.getOrganism()),
      disease: self.serialize(project.getDisease())
    }
  };
  return self.sendPatchRequest(self.getProjectUrl(queryParams), filterParams).then(function (content) {
    var downloadedProject = new Project(content);
    project.update(downloadedProject);
    return project;
  }).then(null, function (error) {
    if ((error instanceof NetworkError)) {
      switch (error.statusCode) {
        case HttpStatus.FORBIDDEN:
          return Promise.reject(new SecurityError("Access denied."));
        default:
          return Promise.reject(error);
      }
    } else {
      return Promise.reject(error);
    }
  });
};

ServerConnector.removeProject = function (projectId) {
  var self = this;
  var queryParams = {
    projectId: projectId
  };
  return self.sendDeleteRequest(self.getProjectUrl(queryParams)).then(function (content) {
    var project = new Project(content);
    if (self._projectsById[project.getProjectId()] !== undefined) {
      self._projectsById[project.getProjectId()].update(project);
    } else {
      self._projectsById[project.getProjectId()] = project;
    }
    return self._projectsById[project.getProjectId()];
  }).then(null, function (error) {
    if ((error instanceof NetworkError)) {
      switch (error.statusCode) {
        case HttpStatus.FORBIDDEN:
          return Promise.reject(new SecurityError("Access denied."));
        default:
          return Promise.reject(error);
      }
    } else {
      return Promise.reject(error);
    }
  });
};

ServerConnector.addProject = function (options) {
  var self = this;
  var queryParams = {
    projectId: options.projectId
  };
  return self.sendPostRequest(self.getProjectUrl(queryParams), options).then(function (content) {
    var project = new Project(content);
    if (self._projectsById[project.getProjectId()] !== undefined) {
      self._projectsById[project.getProjectId()].update(project);
    } else {
      self._projectsById[project.getProjectId()] = project;
    }
    return project;
  }).then(null, function (error) {
    if ((error instanceof NetworkError)) {
      switch (error.statusCode) {
        case HttpStatus.FORBIDDEN:
          return Promise.reject(new SecurityError("Access denied."));
        default:
          return Promise.reject(error);
      }
    } else {
      return Promise.reject(error);
    }
  });
};

ServerConnector.serialize = function (object) {
  var result = {};
  if (object instanceof Annotation) {
    result.type = object.getType();
    result.resource = object.getResource();
  } else if (object === undefined) {
    result = undefined;
  } else {
    throw new Error("Unhandled object type: " + (typeof object));
  }
  return result;
};

ServerConnector.getProjects = function (reload) {
  var self = this;
  if (self._projects.length > 0 && !reload) {
    return Promise.resolve(self._projects);
  } else {
    return self.sendGetRequest(self.getProjectsUrl()).then(function (content) {
      var parsedData = JSON.parse(content);
      self._projects.length = 0;
      for (var i = 0; i < parsedData.length; i++) {
        var project = new Project(JSON.stringify(parsedData[i]));
        if (self._projectsById[project.getProjectId()] !== undefined) {
          self._projectsById[project.getProjectId()].update(project);
        } else {
          self._projectsById[project.getProjectId()] = project;
        }
        self._projects.push(self._projectsById[project.getProjectId()]);
      }
      return self._projects;
    });
  }
};

ServerConnector.getProjectStatistics = function (projectId) {
  var queryParams = {};
  var filterParams = {};
  var self = this;
  var content;
  return self.getProjectId(projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getProjectStatisticsUrl(queryParams, filterParams));
  }).then(function (result) {
    content = JSON.parse(result);
    return self.getConfiguration();
  }).then(function (configuration) {
    return new ProjectStatistics(content, configuration);
  });
};

ServerConnector.getLoggedUser = function () {
  var self = this;
  if (self._loggedUser !== undefined) {
    return Promise.resolve(self._loggedUser);
  } else {
    return self.getUser(self.getSessionData().getLogin()).then(function (user) {
      self._loggedUser = user;
      return self._loggedUser;
    });
  }
};

ServerConnector.getUser = function (login) {
  var self = this;
  var queryParams = {
    login: login
  };
  var filterParams = {};

  return self.sendGetRequest(self.getUserUrl(queryParams, filterParams)).then(function (content) {
    var obj = JSON.parse(content);
    var user = new User(obj);
    if (self._usersByLogin[user.getLogin()] !== undefined) {
      self._usersByLogin[user.getLogin()].update(user);
    } else {
      self._usersByLogin[user.getLogin()] = user;
    }
    return self._usersByLogin[user.getLogin()];
  }).then(null, function (error) {
    return self.processNetworkError(error);
  });
};

ServerConnector.updateUser = function (user) {
  var self = this;
  var queryParams = {
    login: user.getLogin()
  };
  var filterParams = {
    user: {
      name: user.getName(),
      surname: user.getSurname(),
      password: user.getPassword(),
      email: user.getEmail()
    }
  };
  return self.sendPatchRequest(self.getUserUrl(queryParams), filterParams).then(function () {
    return self.getConfiguration();
  }).then(function (configuration) {
    return self.updateUserPrivileges({user: user, privileges: user.privilegesToExport(configuration)});
  });

};
ServerConnector.addUser = function (user) {
  var self = this;
  var queryParams = {
    login: user.getLogin()
  };
  var filterParams = {
    login: user.getLogin(),
    name: user.getName(),
    surname: user.getSurname(),
    password: user.getPassword(),
    email: user.getEmail()
  };
  return self.sendPostRequest(self.getUserUrl(queryParams), filterParams).then(function () {
    return self.getConfiguration();
  }).then(function (configuration) {
    return self.updateUserPrivileges({user: user, privileges: user.privilegesToExport(configuration)});
  });

};

ServerConnector.removeUser = function (login) {
  var self = this;
  var queryParams = {
    login: login
  };
  return self.sendDeleteRequest(self.getUserUrl(queryParams));

};

ServerConnector.updateUserPrivileges = function (params) {
  var self = this;
  var queryParams = {
    login: params.user.getLogin()
  };

  return self.sendPatchRequest(self.getUpdateUserPrivilegesUrl(queryParams), {
    privileges: params.privileges
  }).then(function (content) {
    var obj = JSON.parse(content);
    var user = new User(obj);
    if (self._usersByLogin[user.getLogin()] !== undefined) {
      self._usersByLogin[user.getLogin()].update(user);
    } else {
      self._usersByLogin[user.getLogin()] = user;
    }
    return self._usersByLogin[user.getLogin()];
  }).then(null, function (error) {
    return self.processNetworkError(error);
  });
};

ServerConnector.processNetworkError = function (error) {
  if ((error instanceof NetworkError)) {
    switch (error.statusCode) {
      case HttpStatus.NOT_FOUND:
        return null;
      case HttpStatus.FORBIDDEN:
        return Promise.reject(new SecurityError("Access denied."));
      default:
        return Promise.reject(error);
    }
  } else {
    return Promise.reject(error);
  }
};


ServerConnector.updateUserPreferences = function (params) {
  var self = this;
  var queryParams = {
    login: params.user.getLogin()
  };

  return self.sendPatchRequest(self.getUpdateUserPreferencesUrl(queryParams), {
    preferences: params.preferences.toExport()
  }).then(function (content) {
    var obj = JSON.parse(content);
    var user = new User(obj);
    if (self._usersByLogin[user.getLogin()] !== undefined) {
      self._usersByLogin[user.getLogin()].update(user);
    } else {
      self._usersByLogin[user.getLogin()] = user;
    }
    return self._usersByLogin[user.getLogin()];
  });
};

ServerConnector.getUsers = function (forceRefresh) {
  var self = this;

  if (self._users.length > 0 && !forceRefresh) {
    return Promise.resolve(self._users);
  } else {
    return self.sendGetRequest(self.getUsersUrl()).then(function (content) {
      var parsedData = JSON.parse(content);
      self._users.length = 0;
      for (var i = 0; i < parsedData.length; i++) {
        var user = new User(parsedData[i]);
        if (self._usersByLogin[user.getLogin()] !== undefined) {
          self._usersByLogin[user.getLogin()].update(user);
        } else {
          self._usersByLogin[user.getLogin()] = user;
        }
        self._users.push(self._usersByLogin[user.getLogin()]);
      }
      return self._users;
    }).then(null, function (error) {
      return self.processNetworkError(error);
    });
  }

};

ServerConnector.getOverlays = function (params) {
  var self = this;
  if (params === undefined) {
    params = {};
  }
  var queryParams = {};
  var filterParams = {
    creator: params.creator,
    publicOverlay: params.publicOverlay
  };
  return new Promise(function (resolve, reject) {
    self.getProjectId(params.projectId).then(function (result) {
      queryParams.projectId = result;
      return self.sendGetRequest(self.getOverlaysUrl(queryParams, filterParams));
    }).then(function (content) {
      var arr = JSON.parse(content);
      var result = [];
      for (var i = 0; i < arr.length; i++) {
        var overlay = new LayoutData(arr[i]);
        result.push(overlay);
      }
      resolve(result);
    }, reject);
  });
};

ServerConnector.getOverlayElements = function (overlayId, projectId) {
  var self = this;
  if (overlayId === undefined) {
    throw new Error("Layout id must be defined");
  }
  var queryParams = {
    overlayId: overlayId,
    modelId: "*"
  };
  var filterParams = {};
  return self.getProjectId(projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getOverlayElementsUrl(queryParams, filterParams));
  }).then(function (content) {
    var arr = JSON.parse(content);
    var result = [];
    for (var i = 0; i < arr.length; i++) {
      var element = arr[i];
      if (element.type === "REACTION") {
        result.push(new LayoutReaction(element.overlayContent));
      } else if (element.type === "ALIAS") {
        result.push(new LayoutAlias(element.overlayContent));
      } else {
        throw new Error("Unknown element type: " + element.type);
      }
    }
    return result;
  });
};

ServerConnector.getFullOverlayElement = function (params) {
  var self = this;

  var queryParams = {
    overlayId: params.overlay.getId(),
    modelId: params.element.getModelId(),
    id: params.element.getId()
  };
  var filterParams = {};

  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getFullOverlayElementUrl(queryParams, filterParams));
  }).then(function (content) {
    var element = JSON.parse(content);
    var result = null;
    if (element.type === "REACTION") {
      result = new LayoutReaction(element.overlayContent);
    } else if (element.type === "ALIAS") {
      result = new LayoutAlias(element.overlayContent);
    } else {
      throw new Error("Unknown element type: " + element.type);
    }
    return result;
  });
};

ServerConnector.getProjectId = function (projectId) {
  var self = this;
  if (projectId !== undefined && projectId !== null && projectId !== "") {
    return Promise.resolve(projectId);
  } else if (GuiConnector.getParams['id'] !== undefined) {
    return Promise.resolve(GuiConnector.getParams['id']);
  } else {
    return self.getConfigurationParam(ConfigurationType.DEFAULT_MAP);
  }
};

ServerConnector.getLogoImg = function () {
  return this.getConfigurationParam(ConfigurationType.LOGO_IMG);
};

ServerConnector.getLogoLink = function () {
  return this.getConfigurationParam(ConfigurationType.LOGO_LINK);
};

ServerConnector.getMaxSearchDistance = function () {
  return this.getConfigurationParam(ConfigurationType.SEARCH_DISTANCE);
};

ServerConnector.getOverlayById = function (overlayId, projectId) {
  var self = this;
  var queryParams = {
    overlayId: overlayId
  };
  var filterParams = {};
  return self.getProjectId(projectId).then(function (data) {
    queryParams.projectId = data;
    return self.sendGetRequest(self.getOverlayByIdUrl(queryParams, filterParams));
  }).then(function (content) {
    return new LayoutData(JSON.parse(content));
  });
};

ServerConnector.getReactions = function (params) {
  var self = this;
  var queryParams = {
    modelId: params.modelId
  };
  if (params.ids === undefined) {
    params.ids = [];
  }
  if (params.participantId === undefined) {
    params.participantId = [];
  }
  var filterParams = {
    id: params.ids,
    columns: params.columns,
    participantId: params.participantId
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    if (filterParams.id.length > 100 || filterParams.participantId.length > 100) {
      return self.sendPostRequest(self.getReactionsUrl(queryParams), filterParams);
    } else {
      return self.sendGetRequest(self.getReactionsUrl(queryParams, filterParams));
    }

  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new Reaction(array[i]));
    }
    return result;
  });
};

ServerConnector.getAliases = function (params) {
  var self = this;
  var queryParams = {
    modelId: params.modelId
  };
  if (params.ids === undefined) {
    params.ids = [];
  }
  if (params.includedCompartmentIds === undefined) {
    params.includedCompartmentIds = [];
  }
  if (params.excludedCompartmentIds === undefined) {
    params.excludedCompartmentIds = [];
  }
  var filterParams = {
    id: params.ids,
    columns: params.columns,
    type: params.type,
    excludedCompartmentIds: params.excludedCompartmentIds,
    includedCompartmentIds: params.includedCompartmentIds

  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    if (filterParams.id.length > 100) {
      return self.sendPostRequest(self.getAliasesUrl(queryParams), filterParams);
    } else {
      return self.sendGetRequest(self.getAliasesUrl(queryParams, filterParams));
    }
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new Alias(array[i]));
    }
    return result;
  });
};

ServerConnector.getLightComments = function (params) {
  params.columns = ["id", "elementId", "modelId", "type", "icon", "removed", "pinned"];
  return this.getComments(params);
};

ServerConnector.getComments = function (params) {
  var self = this;
  var queryParams = {
    elementId: params.elementId,
    elementType: params.elementType,
    coordinates: params.coordinates
  };
  var filterParams = {
    columns: params.columns
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getCommentsUrl(queryParams, filterParams));
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new Comment(array[i]));
    }
    return result;
  });
};

ServerConnector.getSessionData = function (project) {
  if (this._sessionData === undefined) {
    this._sessionData = new SessionData(project);
  }
  if (project !== undefined && this._sessionData.getProject() === null) {
    this._sessionData.setProject(project);
  }
  return this._sessionData;
};

ServerConnector.getClosestElementsByCoordinates = function (params) {
  var self = this;
  var queryParams = {
    modelId: params.modelId
  };
  var filterParams = {
    coordinates: params.coordinates,
    count: params.count
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchUrl(queryParams, filterParams));
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new IdentifiedElement(array[i]));
    }
    return result;
  });
};

ServerConnector.login = function (login, password) {
  var self = this;
  var params = {};
  if (login !== undefined && login !== "") {
    params.login = login;
    params.password = password;
  } else {
    params.login = "anonymous";
  }
  self.getSessionData().setToken("");
  return self.sendPostRequest(self.loginUrl(), params).then(function () {
    self.getSessionData().setLogin(params.login);
    return Promise.resolve(self.getSessionData().getToken());
  }, function (error) {
    if (error instanceof NetworkError && error.statusCode === HttpStatus.FORBIDDEN) {
      throw new InvalidCredentialsError("Invalid credentials");
    } else {
      throw error;
    }
  });
};

ServerConnector.logout = function () {
  var self = this;
  return self.sendGetRequest(self.logoutUrl()).then(function () {
    self.getSessionData().setToken(undefined);
    self.getSessionData().setLogin(undefined);
    window.location.reload(false);
  });
};

ServerConnector.getElementsByQuery = function (params) {
  var self = this;
  var queryParams = {
    modelId: params.modelId
  };
  var filterParams = {
    query: params.query,
    perfectMatch: params.perfectMatch
  };

  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.getConfigurationParam(ConfigurationType.SEARCH_RESULT_NUMBER);
  }).then(function (defaultCount) {
    if (params.count !== undefined) {
      filterParams.count = params.count;
    } else {
      filterParams.count = defaultCount;
    }
    return self.sendGetRequest(self.getSearchUrl(queryParams, filterParams));
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new IdentifiedElement(array[i]));
    }
    return result;
  });
};

ServerConnector.getDrugsByQuery = function (params) {
  var self = this;
  var queryParams = {};
  var filterParams = {
    query: params.query
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchDrugsUrl(queryParams, filterParams));
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new Drug(array[i]));
    }
    return result;
  });
};

ServerConnector.getMiRnasByQuery = function (params) {
  var self = this;
  var queryParams = {};
  var filterParams = {
    query: params.query
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchMiRnasUrl(queryParams, filterParams));
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new MiRna(array[i]));
    }
    return result;
  });
};

ServerConnector.getChemicalsByQuery = function (params) {
  var self = this;
  var queryParams = {};
  var filterParams = {
    query: params.query
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchChemicalsUrl(queryParams, filterParams));
  }).then(function (content) {
    var array = JSON.parse(content);
    var result = [];
    for (var i = 0; i < array.length; i++) {
      result.push(new Chemical(array[i]));
    }
    return result;
  });
};

ServerConnector.getOverlaySourceDownloadUrl = function (params) {
  var self = this;
  var queryParams = {
    overlayId: params.overlayId
  };
  var filterParams = {};
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.getOverlaySourceUrl(queryParams, filterParams);
  });
};

ServerConnector.getImageDownloadUrl = function (params) {
  var self = this;
  var queryParams = {
    projectId: params.projectId,
    modelId: params.modelId
  };
  var filterParams = {
    token: params.token,
    polygonString: params.polygonString,
    handlerClass: params.handlerClass,
    backgroundOverlayId: params.backgroundOverlayId,
    zoomLevel: params.zoomLevel,
    overlayIds: this.idsToString(params.overlayIds)
  };

  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.getImageUrl(queryParams, filterParams);
  });
};

ServerConnector.getModelDownloadUrl = function (params) {
  var self = this;
  var queryParams = {
    projectId: params.projectId,
    modelId: params.modelId
  };
  var filterParams = {
    token: params.token,
    polygonString: params.polygonString,
    handlerClass: params.handlerClass,
    backgroundOverlayId: params.backgroundOverlayId,
    zoomLevel: params.zoomLevel,
    overlayIds: this.idsToString(params.overlayIds)
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.getModelPartUrl(queryParams, filterParams);
  });
};

ServerConnector.getImageConverters = function () {
  var self = this;
  return self.getConfiguration().then(function (configuration) {
    return configuration.getImageConverters();
  });
};
ServerConnector.getModelConverters = function () {
  var self = this;
  return self.getConfiguration().then(function (configuration) {
    return configuration.getModelConverters();
  });
};

ServerConnector.getProjectSourceDownloadUrl = function (params) {
  if (params === undefined) {
    params = {};
  }
  var queryParams = {};
  var filterParams = {};
  var self = this;
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.getProjectSourceUrl(queryParams, filterParams);
  });
};

ServerConnector.getDrugNamesByTarget = function (params) {
  var self = this;
  var queryParams = {};
  var filterParams = {
    columns: ["name"],
    target: params.target.getType() + ":" + params.target.getId()
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchDrugsUrl(queryParams, filterParams));
  }).then(function (content) {
    var result = [];
    var object = JSON.parse(content);
    for (var i = 0; i < object.length; i++) {
      result.push(object[i].name);
    }
    return result;
  });
};

ServerConnector.getMiRnaNamesByTarget = function (params) {
  var self = this;
  var queryParams = {};
  var filterParams = {
    columns: ["name"],
    target: params.target.getType() + ":" + params.target.getId()
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchMiRnasUrl(queryParams, filterParams));
  }).then(function (content) {
    var result = [];
    var object = JSON.parse(content);
    for (var i = 0; i < object.length; i++) {
      result.push(object[i].name);
    }
    return result;
  });
};

ServerConnector.getChemicalNamesByTarget = function (params) {
  var self = this;
  var queryParams = {};
  var filterParams = {
    columns: ["name"],
    target: params.target.getType() + ":" + params.target.getId()
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getSearchChemicalsUrl(queryParams, filterParams));
  }).then(function (content) {
    var result = [];
    var object = JSON.parse(content);
    for (var i = 0; i < object.length; i++) {
      result.push(object[i].name);
    }
    return result;
  });
};

ServerConnector.addComment = function (params) {
  var self = this;
  var queryParams = {
    elementId: params.elementId,
    elementType: params.elementType,
    coordinates: self.pointToString(params.coordinates),
    modelId: params.modelId
  };
  var filterParams = params;
  delete filterParams.elementId;
  delete filterParams.elementType;
  if (queryParams.elementType === "POINT") {
    delete filterParams.coordinates;
  } else {
    filterParams.coordinates = self.pointToString(params.coordinates);
  }
  delete filterParams.modelId;
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendPostRequest(self.addCommentUrl(queryParams), filterParams);
  }).then(function (content) {
    var response = JSON.parse(content);
    return new Comment(response);
  });
};

ServerConnector.addOverlay = function (params) {
  var overlay = params.overlay;
  if (!(overlay instanceof LayoutData)) {
    throw new Error("Invalid overlay: " + overlay);
  }
  var self = this;
  var queryParams = {};
  var data = {
    name: overlay.getName(),
    description: overlay.getDescription(),
    content: overlay.getContent(),
    filename: overlay.getFilename(),
    fileId: params.fileId
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendPostRequest(self.addOverlayUrl(queryParams), data);
  }).then(function (content) {
    return new LayoutData(JSON.parse(content));
  });
};

ServerConnector.updateOverlay = function (overlay) {
  var self = this;
  var queryParams = {
    overlayId: overlay.getId()
  };
  var filterParams = {
    overlay: {
      name: overlay.getName(),
      description: overlay.getDescription(),
      creator: overlay.getCreator(),
      publicOverlay: overlay.getPublicOverlay(),
      defaultOverlay: overlay.isDefaultOverlay()
    }
  };
  return self.sendPatchRequest(self.updateOverlayUrl(queryParams), filterParams);
};

ServerConnector.updateModel = function (params) {
  var self = this;
  var model = params.model;
  var queryParams = {
    projectId: params.projectId,
    modelId: model.getId()
  };
  var filterParams = {
    model: {
      id: model.getId(),
      defaultCenterX: model.getDefaultCenterX(),
      defaultCenterY: model.getDefaultCenterY(),
      defaultZoomLevel: model.getDefaultZoomLevel()
    }
  };
  return self.sendPatchRequest(self.updateModelUrl(queryParams), filterParams);
};


ServerConnector.removeOverlay = function (params) {
  var self = this;
  var queryParams = {
    overlayId: params.overlayId
  };
  var filterParams = {};
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendDeleteRequest(self.deleteOverlayUrl(queryParams), filterParams);
  });
};

ServerConnector.removeComment = function (params) {
  var self = this;
  var queryParams = {
    commentId: params.commentId
  };
  var filterParams = {
    reason: params.reason
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendDeleteRequest(self.deleteCommentUrl(queryParams), filterParams);
  });
};

ServerConnector.getSuggestedQueryList = function (projectId) {
  var self = this;
  return self.getProjectId(projectId).then(function (result) {
    projectId = result;
    return self.sendGetRequest(self.getSuggestedQueryListUrl({
      projectId: projectId
    }));
  }).then(function (content) {
    return JSON.parse(content);
  });
};

ServerConnector.getChemicalSuggestedQueryList = function (projectId) {
  var self = this;
  return self.getProjectId(projectId).then(function (result) {
    projectId = result;
    return self.sendGetRequest(self.getChemicalSuggestedQueryListUrl({
      projectId: projectId
    }));
  }).then(function (content) {
    return JSON.parse(content);
  });
};
ServerConnector.getMiRnaSuggestedQueryList = function (projectId) {
  var self = this;
  return self.getProjectId(projectId).then(function (result) {
    projectId = result;
    return self.sendGetRequest(self.getMiRnaSuggestedQueryListUrl({
      projectId: projectId
    }));
  }).then(function (content) {
    return JSON.parse(content);
  });
};
ServerConnector.getDrugSuggestedQueryList = function (projectId) {
  var self = this;
  return self.getProjectId(projectId).then(function (result) {
    projectId = result;
    return self.sendGetRequest(self.getDrugSuggestedQueryListUrl({
      projectId: projectId
    }));
  }).then(function (content) {
    return JSON.parse(content);
  });
};

ServerConnector.getOverlayTypes = function () {
  var self = this;
  return self.getConfiguration().then(function (configuration) {
    return configuration.getOverlayTypes();
  });
};

ServerConnector.getPublications = function (params) {
  var self = this;
  if (params === undefined) {
    params = {};
  }

  var queryParams = {};
  var filterParams = {
    start: params.start,
    length: params.length,
    sortColumn: params.sortColumn,
    sortOrder: params.sortOrder,
    search: params.search
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getPublicationsUrl(queryParams, filterParams));
  }).then(function (content) {
    return JSON.parse(content);
  });
};

ServerConnector.getReferenceGenome = function (params) {
  var self = this;
  var filterParams = {};
  return self.sendGetRequest(self.getReferenceGenomeUrl(params, filterParams)).then(function (content) {
    return new ReferenceGenome(JSON.parse(content));
  });
};

ServerConnector.uploadFile = function (params) {
  var CHUNK_SIZE = 65535;
  var self = this;
  var data = new Uint8Array(params.content);
  var filterParams = {
    filename: params.filename,
    length: data.length
  };

  return self.sendPostRequest(self.getCreateFileUrl(), filterParams).then(function (response) {
    var uploadedLength = 0;
    var createPromise = function (resultFileJson) {
      var resultFile = JSON.parse(resultFileJson);
      if (uploadedLength >= data.length) {
        return Promise.resolve(resultFile);
      } else {
        var chunk = data.slice(uploadedLength, uploadedLength + CHUNK_SIZE);
        var url = self.getUploadFileUrl({id: resultFile.id});
        return self.sendRequest({method: "POST", url: url, body: chunk}).then(function (resultFileJson) {
          uploadedLength += chunk.length;
          return createPromise(resultFileJson);
        })
      }
    };
    return createPromise(response);
  });
};


ServerConnector.getProjectLogs = function (params) {
  var self = this;
  if (params === undefined) {
    params = {};
  }

  var queryParams = {};
  var filterParams = {
    start: params.start,
    length: params.length,
    sortColumn: params.sortColumn,
    sortOrder: params.sortOrder,
    search: params.search,
    level: params.level
  };
  return self.getProjectId(params.projectId).then(function (result) {
    queryParams.projectId = result;
    return self.sendGetRequest(self.getProjectLogsUrl(queryParams, filterParams));
  }).then(function (content) {
    return JSON.parse(content);
  });
};

ServerConnector.getMesh = function (params) {
  var self = this;
  if (params === undefined) {
    params = {};
  }
  return self.sendGetRequest(self.getMeshUrl(params)).then(function (content) {
    return new Mesh(JSON.parse(content));
  });
};

ServerConnector.getSbmlFunction = function (params) {
  var self = this;
  if (params === undefined) {
    params = {};
  }
  return self.getProjectId(params.projectId).then(function (result) {
    params.projectId = result;
    return self.sendGetRequest(self.getSbmlFunctionUrl(params));
  }).then(function (content) {
    return new SbmlFunction(JSON.parse(content));
  });
};

ServerConnector.getSbmlParameter = function (params) {
  var self = this;
  if (params === undefined) {
    params = {};
  }
  return self.getProjectId(params.projectId).then(function (result) {
    params.projectId = result;
    return self.sendGetRequest(self.getSbmlParameterUrl(params));
  }).then(function (content) {
    return new SbmlParameter(JSON.parse(content));
  });
};

module.exports = ServerConnector;