// 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.

// Various pieces of miscellaneous base functionality.

// Move utils to global namespace.
Util.fill(window, Util);
// Register the util.js event connect function with Mold.
Mold.attachEvent = connect;

// Configure RSH
dhtmlHistory.create({
  toJSON: Util.writeJSON,
  fromJSON: Util.readJSON,
  blankURL: "/js/lib/blank.html"
});

// Used by components that have some state they want to preserve
// across refreshes.
var customRefresh = null;

function doDispatch(fragment) {
  customRefresh = null;
  dispatch(fragment);
}

function refresh() {
  if (customRefresh) customRefresh();
  else doDispatch(decodeURIComponent(dhtmlHistory.getCurrentHash()));
}

function goTo(hash) {
  dhtmlHistory.add(hash);
  doDispatch(hash);
}

// A cache for compiled templates.
var template = (function() {
  var loaded = Dictionary.create();

  return function template(name) {
    if (loaded.contains(name))
      return loaded.lookup(name);
    if (!templates.hasOwnProperty(name))
      throw new Error("No template '" + name + "' defined.");
    var mold = Mold.bake(templates[name]);
    loaded.store(name, mold);
    return mold;
  };
})();

// All templates in the instruction/ dir get registered as Mold
// instructions.
function defineMoldInstructions() {
  forEachIn(templates, function(name, val) {
    var match = name.match(/^instruction\/(.+)$/);
    if (match) Mold.define(match[1], template(name));
  });
}

function postJSON(service, args, success, failure) {
  fetchJSON(service, args, success, failure, true);
}

// Interface to the JSON interface (see /src/api.lisp). Calls the
// service denoted by the first argument with the second argument (an
// array) as arguments. success is called with the resulting value on
// success, failure with two arguments, the failure message and the
// XHR object, in case of failure. See the auth cookie in user.js for
// user authentication.
function fetchJSON(service, args, success, failure, usePost) {
  var query = queryString({p: writeJSON(args)}), params = null;
  // When the query is too long, we always use a post request, because
  // very long URLs can cause problems.
  if (!usePost && (query.length + service.length <= dhtmlHistory.isIE ? 2000 : 3900))
    service += "?" + query;
  else
    params = {method: "POST", body: query, headers: {"Content-Type": "application/x-www-form-urlencoded"}};

  httpRequest(service, params,
              // AServe has a tendency to return empty 200 responses on Lisp errors
              function(response) {
                if (response == "") failure("No response.");
                else success(readJSON(response));
              },
              function(message, xhr){failure(xhr.responseText || message, xhr);});
}

// Shorthand for showing a failure message.
function failMessage(prefix, message, disappear) {
  showMessage(prefix + " failed: " + message, disappear == null ? true : disappear, true);
}

// Simple way of posting data to the server. Shows an activity-pane
// while the request is loading.
function activityPost(api, args, text, finish, doRefresh) {
  postJSON(api, args, success, failure);
  var close = activityPane(text);
  function success(data) {
    if (finish) finish(data);
    close();
    if (doRefresh !== false) refresh();
  }
  function failure(message) {
    close();
    failMessage(text, message);
  }
}

// Maps s,o,p,g,i abbreviations to names. Used in several of the
// templates.
var fullName = {"s": "subject", "o": "object", "p": "predicate", "g": "graph", "i": "id"};
// Labels for the user roles.
var userRoles = {"site": "Site", "store": "Triple-store", "user": "Users"};

function capitalise(string) {
  return (/^\w/.test(string)) ? string.charAt(0).toUpperCase() + string.slice(1) : string;
}

// Used to insert extra results when clicking the 'more' links in
// result tables.
function addTBodyBefore(node) {
  var tbody = document.createElement("TBODY");
  node.parentNode.insertBefore(tbody, node);
  return tbody;
}

// Three different ways to set an element's opacity.
function setOpacity(node, opacity) {
  node.style.filter = "alpha(opacity=" + opacity + ")";
  node.style.opacity = String(opacity);
  node.style.MozOpacity = String(opacity);
}

// Convert a number to a string with a certain amount of digits after
// the decimal dots.
function printDecimal(num, positions, trailZeroes) {
  var whole = Math.floor(num), frac = num - whole, result = [whole];
  if (positions) {
    result.push(".");
    for (var i = 0; i < positions; i++) {
      var pos = (i == positions - 1 ? Math.round : Math.floor)(frac * 10);
      result.push(pos);
      frac = frac * 10 - pos;
    }
    if (!trailZeroes)
      while (result[result.length - 1] == "0" && result[result.length - 2] != ".") result.pop();
  }
  return result.join("");
}

function bigNumber(num) {
  num = String(num);
  var pieces = [];
  while (num.length > 3) {
    var end = num.length - 3;
    pieces.unshift(num.substr(end));
    num = num.substr(0, end);
  }
  pieces.unshift(num);
  return pieces.join(",");
}

// Go through a query result matrix looking for geospatial data.
function containsGeoData(queryResult) {
  for (var i = 0; i < queryResult.length; i++) {
    var row = queryResult[i];
    for (var j = 0; j < row.length; j++)
      if (row[j] && part(row[j]).geo()) return true;
  }
  return false;
}

// Put a Google map into a given node.
function showGMap(place, center, zoom) {
  var map = new GMap2(place);
  map.setCenter(center, zoom);
  map.addControl(new GSmallMapControl());
  map.enableScrollWheelZoom()
  return map;
}

// dbInfo holds the information we have about the database (result of
// fetching db/info service). Is null when no database is selected.
var dbInfo = null;
function setDBInfo(data) {
  dbInfo = data;
  if (data && user == null && NameSpaces) NameSpaces.set(data.namespaces);
  initLayout();
}
// Holds information about the current site (see site/info service).
var siteInfo = null;
