// copyright (c) 2006-2008 Franz Inc, Oakland, CA - All rights reserved.
//
// The software, data and information contained herein are proprietary
// to, and comprise valuable trade secrets of, Franz, Inc.  They are
// given in confidence by Franz, Inc. pursuant to a written license
// agreement, and may be stored and used only in accordance with the terms
// of such license.
//
// Restricted Rights Legend
// ------------------------
// Use, duplication, and disclosure of the software, data and information
// contained herein by any agency, department or entity of the U.S.
// Government are subject to restrictions of Restricted Rights for
// Commercial Software developed at private expense as specified in
// DOD FAR Supplement 52.227-7013 (c) (1) (ii), as applicable.

// Managing the logged in, and various pieces of interface
// functionality related to user and account management.

// Global state: the logged in user. Will contain an object as
// returned by the user/userdata service.
var user = null;
// Initialise the user at start-up time -- look at the auth cookie,
// and try to use it to authenticate.
function initUser(c) {
  if (getCookie("auth")) {
    fetchJSON("user/userdata", [], function(data) {
      setUser(data);
      c();
    }, function() {
      delCookie("auth");
      c();
    });
  }
  else {
    c();
  }
}

// Change the current user (null for logging out).
function setUser(userObj) {
  user = userObj;
  if ($("header-links")) Mold.cast($("header-links"), template("instruction/headerLinks"));
  if (window.NameSpaces) {
    if (user) NameSpaces.set(user.namespaces);
    else NameSpaces.set(dbInfo ? dbInfo.namespaces : []);
  }
}

// Set the auth cookie (see /src/user.lisp).
function setAuth(name, password, keep) {
  var hashed = SHA1(hostID + "/" + SHA1(name + "/" + password));
  setCookie("auth", name + ":" + hashed, keep ? null : true);
}

// When anonymous access is disallowed, we might need to load the DB
// info after logging in instead of at load time.
function refreshUser(c) {
  if (!dbInfo && !siteInfo.showStoreList) {
    loadDBInfo(function(){refresh(); c();});
  }
  else {
    refresh();
    c();
  }
}

function logout() {
  delCookie("auth");
  setUser(null);
  refresh();
}

// Show a login dialog, try to use the given credential to fetch user
// information.
function loginDialog() {
  if ($("login-name")) return;
  var close = customDialog("user/login", {ok: tryLogin, cancel: function(){close();}}, false, false);

  function tryLogin(name, password, keep) {
    setAuth(name, password, keep);
    fetchJSON("user/userdata", [], success, failure);
    var loading = $("login-loading");
    loading.style.visibility = "";

    function success(data) {
      close();
      setUser(data);
      refreshUser(function(){showMessage("Login successful.", 2, false);});
    }
    function failure(message, xhr) {
      loading.style.visibility = "hidden";
      if (xhr && xhr.status == 401)
        showMessage("Username or password incorrect.", 2, true);
      else
        showMessage("There was a problem logging in: " + message, true, true);
      delCookie("auth");
      setUser(null);
    }
  }
}

// Dialog for user creation.
function newUser() {
  if ($("user-name")) return;
  var close = customDialog("user/new", {ok: submit, cancel: function(){close();}}, false, false);

  function submit(name, password1, password2) {
    var hash = SHA1(name + "/" + password1);
    if (!password1 || !name)
      showMessage("You'll have to give a password and a name.", 2, true);
    else if (password1 != password2)
      showMessage("Passwords do not match.", 2, true);
    else {
      postJSON("user/create", [name, hash], success, failure);
      var loading = $("user-loading");
      loading.style.visibility = "";
    }

    function success(data) {
      setUser(data);
      setAuth(name, password1);
      close();
      refreshUser(function() {showMessage("Account created.", 3, false);});
    }
    function failure(message) {
      loading.style.visibility = "hidden";
      showMessage("Could not create account: " + message, true, true);
    }
  }
}

function deleteMyUser() {
  showDialog("Really delete your account?", del, null, false, false);

  function del() {
    var close = customDialog("user/delete", user.name);
    postJSON("user/delete", [user.name], success, failure);
    function success() {
      close();
      logout();
      showMessage("Account deleted.", 3, false);
    }
    function failure(message) {
      close();
      failMessage("Deleting account", message, false);
    }
  }
}

// Test whether the current user has a certain role. Used mostly in
// templates to determine which controls to show.
function userRole(role) {
  return user && member(role, user.roles);
}

// Does the current use have database write permission?
function userWrite() {
  return userRole("store") && dbInfo && !dbInfo.readOnly;
}

// Display function for #namespaces
function userNamespaces() {
  if (!user) { goTo(""); return; }

  function nsEq(a) {return function(b){return a.abbrev == b.abbrev && a.uri == b.uri;};}

  var own = [], standard = map(clone, dbInfo.namespaces);
  forEach(user.namespaces, function(ns) {
    var st = findIf(nsEq(ns), standard);
    if (st) st.used = true;
    else own.push(ns);
  });

  Mold.cast(getPage(), template("user/namespaces"), {own: own, standard: standard});
}

function deleteUserNamespace(abbrev) {
  showDialog("Delete namespace '" + abbrev + "'?", doDelete, null);
  function doDelete() {
    activityPost("user/namespace/remove", [abbrev], "Removing namespace", function(data) {
      setUser(data);
      refresh();
    });
  }
}

function storeUserNamespace(abbrev, uri, c) {
  activityPost("user/namespace/add", [abbrev, uri], "Adding namespace", function(data) {
    setUser(data);
    refresh();
    c && c();
  });
}

function selectUserNamespace(ns, selected) {
  if (selected)
    storeUserNamespace(ns.abbrev, ns.uri);
  else
    deleteUserNamespace(ns.abbrev);
}

function addUserNamespace(c) {
  if ($("namespace-abbrev")) return;
  var close = customDialog("store/namespace", {ok: store, cancel: function(){close();}}, false, false);
  function store(abbrev, uri) {
    close();
    var match = uri.match(/^<(.*)>$/);
    if (match) uri = match[1];
    storeUserNamespace(abbrev, uri, c);
  }
}

function userList() {
  if (!userRole("user")) { goTo(""); return; }

  fetchJSON("user/list", [], success, failure);
  Mold.cast(getPage(), template("message/loading"));
  function success(users) {
    Mold.cast(getPage(), template("user/list"), users);
  }
  function failure(message) {
    clearPage();
    failMessage("Loading user data", message);
  }
}

function deleteUser(name) {
  if (name == user.name) {
    deleteMyUser();
    return;
  }

  showDialog("Really delete " + name + "?", del, null, false, false);

  function del() {
    postJSON("user/delete", [name], refresh, failure);
    var loading = $("userlist-loading");
    loading.style.visibility = "";
    function failure(message) {
      loading.style.visibility = "hidden";
      failMessage("Deleting user", message);
    }
  }
}

function updateRole(user, role, state) {
  if (state == member(role, user.roles)) return;
  user.roles = state ? user.roles.concat([role]) : remove(role, user.roles);

  postJSON("user/setroles", [user.name, user.roles], success, failure);
  var loading = $("userlist-loading");
  loading.style.visibility = "";
  function success() {
    loading.style.visibility = "hidden";
  }
  function failure(message) {
    refresh();
    failMessage("Updating permissions", message);
  }
}
