/*jshint strict: false */

////////////////////////////////////////////////////////////////////////////////
/// @brief querying and managing collections
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Achim Brandt
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var arangodb = require("@arangodb");
var actions = require("@arangodb/actions");
var cluster = require("@arangodb/cluster");

// -----------------------------------------------------------------------------
// --SECTION--                                                 private functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief return a prefixed URL
////////////////////////////////////////////////////////////////////////////////

function databasePrefix (req, url) {
  if (req.hasOwnProperty('compatibility') && req.compatibility < 10400) {
    // pre 1.4-style location response (e.g. /_api/collection/xyz)
    return url;
  }

  // 1.4-style location response (e.g. /_db/dbname/_api/collection/xyz)
  return "/_db/" + encodeURIComponent(arangodb.db._name()) + url;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief collection representation
////////////////////////////////////////////////////////////////////////////////

function collectionRepresentation (collection, showProperties, showCount, showFigures) {
  var result = {};

  result.id = collection._id;
  result.name = collection.name();
  result.isSystem = (result.name.charAt(0) === '_');

  if (showProperties) {
    var properties = collection.properties();

    result.doCompact     = properties.doCompact;
    result.isVolatile    = properties.isVolatile;
    result.journalSize   = properties.journalSize;
    result.keyOptions    = properties.keyOptions;
    result.waitForSync   = properties.waitForSync;
    result.indexBuckets  = properties.indexBuckets;

    if (cluster.isCoordinator()) {
      result.shardKeys = properties.shardKeys;
      result.numberOfShards = properties.numberOfShards;
    }
  }

  if (showCount) {
    result.count = collection.count();
  }

  if (showFigures) {
    var figures = collection.figures();

    if (figures) {
      result.figures = figures;
    }
  }

  result.status = collection.status();
  result.type = collection.type();

  return result;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief helper to parse arguments for creating collections
////////////////////////////////////////////////////////////////////////////////

function parseBodyForCreateCollection (req, res) {
  var body = actions.getJsonBody(req, res);

  if (body === undefined) {
    return { bodyIsEmpty: true };
  }

  var r = {};

  if (! body.hasOwnProperty("name")) {
    r.name = "";
  }
  else {
    r.name = body.name;
  }
  r.parameters = { waitForSync : false };
  r.type = arangodb.ArangoCollection.TYPE_DOCUMENT;

  if (body.hasOwnProperty("doCompact")) {
    r.parameters.doCompact = body.doCompact;
  }

  if (body.hasOwnProperty("isSystem")) {
    r.parameters.isSystem = (body.isSystem && r.name[0] === '_');
  }
  
  if (body.hasOwnProperty("id")) {
    r.parameters.id = body.id;
  }

  if (body.hasOwnProperty("isVolatile")) {
    r.parameters.isVolatile = body.isVolatile;
  }

  if (body.hasOwnProperty("journalSize")) {
    r.parameters.journalSize = body.journalSize;
  }

  if (body.hasOwnProperty("indexBuckets")) {
    r.parameters.indexBuckets = body.indexBuckets;
  }

  if (body.hasOwnProperty("keyOptions")) {
    r.parameters.keyOptions = body.keyOptions;
  }

  if (body.hasOwnProperty("type")) {
    r.type = body.type;
  }

  if (body.hasOwnProperty("waitForSync")) {
    r.parameters.waitForSync = body.waitForSync;
  }

  if (body.hasOwnProperty("shardKeys") && cluster.isCoordinator()) {
    r.parameters.shardKeys = body.shardKeys || { };
  }

  if (body.hasOwnProperty("numberOfShards") && cluster.isCoordinator()) {
    r.parameters.numberOfShards = body.numberOfShards || 0;
  }

  if (body.hasOwnProperty("distributeShardsLike") && cluster.isCoordinator()) {
    r.parameters.distributeShardsLike = body.distributeShardsLike || "";
  }

  return r;
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_post_api_collection
/// @brief creates a collection
///
/// @RESTHEADER{POST /_api/collection, Create collection}
///
/// @RESTBODYPARAM{name,string,required,string}
/// The name of the collection.
///
/// @RESTBODYPARAM{waitForSync,boolean,optional,}
/// If *true* then the data is synchronized to disk before returning from a
/// document create, update, replace or removal operation. (default: false)
///
/// @RESTBODYPARAM{doCompact,boolean,optional,}
/// whether or not the collection will be compacted (default is *true*)
///
/// @RESTBODYPARAM{journalSize,integer,optional,int64}
/// The maximal size of a journal or datafile in bytes. The value 
/// must be at least `1048576` (1 MiB). (The default is a configuration parameter)
///
/// @RESTBODYPARAM{isSystem,boolean,optional,}
/// If *true*, create a  system collection. In this case *collection-name*
/// should start with an underscore. End users should normally create non-system
/// collections only. API implementors may be required to create system
/// collections in very special occasions, but normally a regular collection will do.
/// (The default is *false*)
///
/// @RESTBODYPARAM{isVolatile,boolean,optional,}
/// If *true* then the collection data is kept in-memory only and not made persistent.
/// Unloading the collection will cause the collection data to be discarded. Stopping
/// or re-starting the server will also cause full loss of data in the
/// collection. Setting this option will make the resulting collection be
/// slightly faster than regular collections because ArangoDB does not
/// enforce any synchronization to disk and does not calculate any CRC
/// checksums for datafiles (as there are no datafiles). This option 
/// should therefore be used for cache-type collections only, and not 
/// for data that cannot be re-created otherwise.
/// (The default is *false*)
///
/// @RESTBODYPARAM{keyOptions,object,optional,JSF_post_api_collection_opts}
/// additional options for key generation. If specified, then *keyOptions*
/// should be a JSON array containing the following attributes:
///
/// @RESTSTRUCT{type,JSF_post_api_collection_opts,string,required,string}
/// specifies the type of the key generator. The currently available generators are
/// *traditional* and *autoincrement*.
///
/// @RESTSTRUCT{allowUserKeys,JSF_post_api_collection_opts,boolean,required,}
/// if set to *true*, then it is allowed to supply own key values in the
/// *_key* attribute of a document. If set to *false*, then the key generator
/// will solely be responsible for generating keys and supplying own key values
/// in the *_key* attribute of documents is considered an error.
///
/// @RESTSTRUCT{increment,JSF_post_api_collection_opts,integer,required,int64}
/// increment value for *autoincrement* key generator. Not used for other key
/// generator types.
///
/// @RESTSTRUCT{offset,JSF_post_api_collection_opts,integer,required,int64}
/// Initial offset value for *autoincrement* key generator.
/// Not used for other key generator types.
///
/// @RESTBODYPARAM{type,integer,optional,int64}
/// (The default is *2*): the type of the collection to create.
/// The following values for *type* are valid:
///
/// - *2*: document collection
/// - *3*: edges collection
///
/// @RESTBODYPARAM{indexBuckets,integer,optional,int64}
/// The: number of buckets into which indexes using a hash
/// table are split. The default is 16 and this number has to be a
/// power of 2 and less than or equal to 1024. 
/// 
/// For very large collections one should increase this to avoid long pauses 
/// when the hash table has to be initially built or resized, since buckets 
/// are resized individually and can be initially built in parallel. For 
/// example, 64 might be a sensible value for a collection with 100
/// 000 000 documents. Currently, only the edge index respects this
/// value, but other index types might follow in future ArangoDB versions. 
/// Changes (see below) are applied when the collection is loaded the next 
/// time.
///
/// @RESTBODYPARAM{numberOfShards,integer,optional,int64}
/// (The default is *1*): in a cluster, this value determines the
/// number of shards to create for the collection. In a single
/// server setup, this option is meaningless.
///
/// @RESTBODYPARAM{shardKeys,string,optional,string}
/// (The default is *[ "_key" ]*): in a cluster, this attribute determines
/// which document attributes are used to determine the target shard for documents.
/// Documents are sent to shards based on the values of their shard key attributes.
/// The values of all shard key attributes in a document are hashed,
/// and the hash value is used to determine the target shard.
/// **Note**: Values of shard key attributes cannot be changed once set.
///   This option is meaningless in a single server setup.
///
/// @RESTDESCRIPTION
/// Creates an new collection with a given name. The request must contain an
/// object with the following attributes.
///
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404* is returned.
///
///
/// @EXAMPLES
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionCreateCollection}
///     var url = "/_api/collection";
///     var body = {
///       name: "testCollectionBasics"
///     };
///
///     var response = logCurlRequest('POST', url, body);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     body = {
///       name: "testCollectionEdges",
///       type : 3
///     };
///
///     var response = logCurlRequest('POST', url, body);
///
///     assert(response.code === 200);
///     logJsonResponse(response);
///
///     db._flushCache();
///     db._drop("testCollectionBasics");
///     db._drop("testCollectionEdges");
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionCreateKeyopt}
///     var url = "/_api/collection";
///     var body = {
///       name: "testCollectionUsers",
///       keyOptions : {
///         type : "autoincrement",
///         increment : 5,
///         allowUserKeys : true
///       }
///     };
///
///     var response = logCurlRequest('POST', url, body);
///
///     assert(response.code === 200);
///     logJsonResponse(response);
///
///     db._flushCache();
///     db._drop("testCollectionUsers");
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function post_api_collection (req, res) {
  var r = parseBodyForCreateCollection(req, res);

  if (r.bodyIsEmpty) {
    return;   // error in JSON, is already reported
  }

  if (r.name === "") {
    actions.resultBad(req, res, arangodb.ERROR_ARANGO_ILLEGAL_NAME,
                      "name must be non-empty");
    return;
  }

  try {
    var collection;
    if (typeof(r.type) === "string") {
      if (r.type.toLowerCase() === "edge" || r.type === "3") {
        r.type = arangodb.ArangoCollection.TYPE_EDGE;
      }
    }
    if (r.type === arangodb.ArangoCollection.TYPE_EDGE) {
      collection = arangodb.db._createEdgeCollection(r.name, r.parameters);
    }
    else {
      collection = arangodb.db._createDocumentCollection(r.name, r.parameters);
    }

    var result = {};

    result.id = collection._id;
    result.name = collection.name();
    result.waitForSync = r.parameters.waitForSync || false;
    result.isVolatile = r.parameters.isVolatile || false;
    result.isSystem = r.parameters.isSystem || false;
    result.status = collection.status();
    result.type = collection.type();
    result.keyOptions = collection.keyOptions;

    if (cluster.isCoordinator()) {
      result.shardKeys = collection.shardKeys;
      result.numberOfShards = collection.numberOfShards;
      result.distributeShardsLike = collection.distributeShardsLike || "";
    }

    var headers = {
      location: databasePrefix(req, "/_api/collection/" + result.name)
    };

    actions.resultOk(req, res, actions.HTTP_OK, result, headers);
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_get_api_collections
/// @brief returns all collections
///
/// @RESTHEADER{GET /_api/collection,reads all collections}
///
/// @RESTQUERYPARAMETERS
///
/// @RESTQUERYPARAM{excludeSystem,boolean,optional}
/// Whether or not system collections should be excluded from the result.
///
/// @RESTDESCRIPTION
/// Returns an object with an attribute *collections* containing an
/// array of all collection descriptions. The same information is also
/// available in the *names* as an object with the collection names
/// as keys.
///
/// By providing the optional query parameter *excludeSystem* with a value of
/// *true*, all system collections will be excluded from the response.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200}
/// The list of collections
///
/// @EXAMPLES
///
/// Return information about all collections:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetAllCollections}
///     var url = "/_api/collection";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function get_api_collections (req, res) {
  var i;
  var list = [];
  var names = {};
  var excludeSystem;
  var collections = arangodb.db._collections();

  excludeSystem = false;
  if (req.parameters.hasOwnProperty('excludeSystem')) {
    var value = req.parameters.excludeSystem.toLowerCase();
    if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
      excludeSystem = true;
    }
  }

  for (i = 0;  i < collections.length;  ++i) {
    var collection = collections[i];
    var rep = collectionRepresentation(collection);

    // include system collections or exclude them?
    if (! excludeSystem || rep.name.substr(0, 1) !== '_') {
      list.push(rep);
      names[rep.name] = rep;
    }
  }

  var result = { collections : list, names : names };

  actions.resultOk(req, res, actions.HTTP_OK, result);
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSA_get_api_collection_name
/// @brief returns a collection
///
/// @RESTHEADER{GET /_api/collection/{collection-name}, Return information about a collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// The result is an object describing the collection with the following
/// attributes:
///
/// - *id*: The identifier of the collection.
///
/// - *name*: The name of the collection.
///
/// - *status*: The status of the collection as number.
///  - 1: new born collection
///  - 2: unloaded
///  - 3: loaded
///  - 4: in the process of being unloaded
///  - 5: deleted
///  - 6: loading
///
/// Every other status indicates a corrupted collection.
///
/// - *type*: The type of the collection as number.
///   - 2: document collection (normal case)
///   - 3: edges collection
///
/// - *isSystem*: If *true* then the collection is a system collection.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404* is
/// returned.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSA_get_api_collection_properties
/// @brief reads the properties of the specified collection
///
/// @RESTHEADER{GET /_api/collection/{collection-name}/properties, Read properties of a collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// In addition to the above, the result will always contain the
/// *waitForSync*, *doCompact*, *journalSize*, and *isVolatile* attributes.
/// This is achieved by forcing a load of the underlying collection.
///
/// - *waitForSync*: If *true* then creating, changing or removing
///   documents will wait until the data has been synchronized to disk.
///
/// - *doCompact*: Whether or not the collection will be compacted.
///
/// - *journalSize*: The maximal size setting for journals / datafiles
///   in bytes.
///
/// - *keyOptions*: JSON object which contains key generation options:
///   - *type*: specifies the type of the key generator. The currently
///     available generators are *traditional* and *autoincrement*.
///   - *allowUserKeys*: if set to *true*, then it is allowed to supply
///     own key values in the *_key* attribute of a document. If set to
///     *false*, then the key generator is solely responsible for
///     generating keys and supplying own key values in the *_key* attribute
///     of documents is considered an error.
///
/// - *isVolatile*: If *true* then the collection data will be
///   kept in memory only and ArangoDB will not write or sync the data
///   to disk.
///
/// In a cluster setup, the result will also contain the following attributes:
/// - *numberOfShards*: the number of shards of the collection.
///
/// - *shardKeys*: contains the names of document attributes that are used to
///   determine the target shard for documents.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// Using an identifier:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionIdentifier}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/"+ coll._id + "/properties";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///   ~ db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Using a name:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionName}
///     var cn = "products";
///     db._drop(cn);
///     db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/products/properties";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSA_get_api_collection_count
/// @brief Counts the documents in a collection
///
/// @RESTHEADER{GET /_api/collection/{collection-name}/count, Return number of documents in a collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// In addition to the above, the result also contains the number of documents.
/// **Note** that this will always load the collection into memory.
///
/// - *count*: The number of documents inside the collection.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// Requesting the number of documents:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionCount}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: true });
///     for(var i=0;i<100;i++) {
///        coll.save({"count" :  i });
///     }
///     var url = "/_api/collection/"+ coll.name() + "/count";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSA_get_api_collection_figures
/// @brief Fetch the statistics of a collection
///
/// @RESTHEADER{GET /_api/collection/{collection-name}/figures, Return statistics for a collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// In addition to the above, the result also contains the number of documents
/// and additional statistical information about the collection.
/// **Note** : This will always load the collection into memory.
///
/// **Note**: collection data that are stored in the write-ahead log only are
/// not reported in the results. When the write-ahead log is collected, documents
/// might be added to journals and datafiles of the collection, which may modify
/// the figures of the collection.
///
/// Additionally, the filesizes of collection and index parameter JSON files are
/// not reported. These files should normally have a size of a few bytes
/// each. Please also note that the *fileSize* values are reported in bytes
/// and reflect the logical file sizes. Some filesystems may use optimisations
/// (e.g. sparse files) so that the actual physical file size is somewhat
/// different. Directories and sub-directories may also require space in the
/// file system, but this space is not reported in the *fileSize* results.
///
/// That means that the figures reported do not reflect the actual disk
/// usage of the collection with 100% accuracy. The actual disk usage of
/// a collection is normally slightly higher than the sum of the reported
/// *fileSize* values. Still the sum of the *fileSize* values can still be
/// used as a lower bound approximation of the disk usage.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200}
/// Returns information about the collection:
///
/// @RESTREPLYBODY{count,integer,required,int64}
/// The number of documents currently present in the collection.
///
/// @RESTREPLYBODY{figures,object,required,collection_figures}
/// metrics of the collection
///
/// @RESTSTRUCT{alive,collection_figures,object,required,collection_figures_alive}
/// the currently active figures
///
/// @RESTSTRUCT{count,collection_figures_alive,integer,required,int64}
/// The number of currently active documents in all datafiles
/// and journals of the collection. Documents that are contained in the
/// write-ahead log only are not reported in this figure.
///
/// @RESTSTRUCT{size,collection_figures_alive,integer,required,int64}
/// The total size in bytes used by all active documents of
/// the collection. Documents that are contained in the write-ahead log only are
/// not reported in this figure.
///
/// @RESTSTRUCT{dead,collection_figures,object,required,collection_figures_dead}
/// the items waiting to be swept away by the cleaner
///
/// @RESTSTRUCT{count,collection_figures_dead,integer,required,int64}
/// The number of dead documents. This includes document
/// versions that have been deleted or replaced by a newer version. Documents
/// deleted or replaced that are contained the write-ahead log only are not reported
/// in this figure.
///
/// @RESTSTRUCT{size,collection_figures_dead,integer,required,int64}
/// The total size in bytes used by all dead documents.
///
/// @RESTSTRUCT{deletion,collection_figures_dead,integer,required,int64}
/// The total number of deletion markers. Deletion markers
/// only contained in the write-ahead log are not reporting in this figure.
///
/// @RESTSTRUCT{datafiles,collection_figures,object,required,collection_figures_datafiles}
/// Metrics regarding the datafiles
///
/// @RESTSTRUCT{count,collection_figures_datafiles,integer,required,int64}
/// The number of datafiles.
///
/// @RESTSTRUCT{fileSize,collection_figures_datafiles,integer,required,int64}
/// The total filesize of datafiles (in bytes).
///
/// @RESTSTRUCT{journals,collection_figures,object,required,collection_figures_journals}
/// Metrics regarding the journal files
///
/// @RESTSTRUCT{count,collection_figures_journals,integer,required,int64}
/// The number of journal files.
///
/// @RESTSTRUCT{fileSize,collection_figures_journals,integer,required,int64}
/// The total filesize of all journal files (in bytes).
///
/// @RESTSTRUCT{compactors,collection_figures,object,required,collection_figures_compactors}
///
/// @RESTSTRUCT{count,collection_figures_compactors,integer,required,int64}
/// The number of compactor files.
///
/// @RESTSTRUCT{fileSize,collection_figures_compactors,integer,required,int64}
/// The total filesize of all compactor files (in bytes).
///
/// @RESTSTRUCT{shapefiles,collection_figures,object,required,collection_figures_shapefiles}
/// **deprecated**
///
/// @RESTSTRUCT{count,collection_figures_shapefiles,integer,required,int64}
/// The number of shape files. This value is deprecated and kept for compatibility reasons only.
/// The value will always be 0 since ArangoDB 2.0 and higher.
///
/// @RESTSTRUCT{fileSize,collection_figures_shapefiles,integer,required,int64}
/// The total filesize of the shape files. This value is deprecated and kept
/// for compatibility reasons only. The value will always be 0 in ArangoDB 2.0 and higher.
///
/// @RESTSTRUCT{shapes,collection_figures,object,required,collection_figures_shapes}
/// @RESTSTRUCT{count,collection_figures_shapes,integer,required,int64}
/// The total number of shapes used in the collection. This includes shapes
/// that are not in use anymore. Shapes that are contained
/// in the write-ahead log only are not reported in this figure.
///
/// @RESTSTRUCT{size,collection_figures_shapes,integer,required,int64}
/// The total size of all shapes (in bytes). This includes
/// shapes that are not in use anymore. Shapes that are contained in the
/// write-ahead log only are not reported in this figure.
///
/// @RESTSTRUCT{attributes,collection_figures,object,required,collection_figures_attributes}
/// @RESTSTRUCT{count,collection_figures_attributes,integer,required,int64}
/// The total number of attributes used in the
/// collection. Note: the value includes data of attributes that are not in use
/// anymore. Attributes that are contained in the write-ahead log only are
/// not reported in this figure.
///
/// @RESTSTRUCT{size,collection_figures_attributes,integer,required,int64}
/// The total size of the attribute data (in bytes).
/// Note: the value includes data of attributes that are not in use anymore.
/// Attributes that are contained in the write-ahead log only are not
/// reported in this figure.
///
/// @RESTSTRUCT{indexes,collection_figures,object,required,collection_figures_indexes}
/// @RESTSTRUCT{count,collection_figures_indexes,integer,required,int64}
/// The total number of indexes defined for the collection, including the pre-defined
/// indexes (e.g. primary index).
///
/// @RESTSTRUCT{size,collection_figures_indexes,integer,required,int64}
/// The total memory allocated for indexes in bytes.
///
/// @RESTSTRUCT{maxTick,collection_figures,integer,required,int64}
/// The tick of the last marker that was stored in a journal
/// of the collection. This might be 0 if the collection does not yet have
/// a journal.
///
/// @RESTSTRUCT{uncollectedLogfileEntries,collection_figures,integer,required,int64}
/// The number of markers in the write-ahead
/// log for this collection that have not been transferred to journals or datafiles.
///
/// @RESTSTRUCT{documentReferences,collection_figures,integer,optional,int64}
/// The number of references to documents in datafiles that JavaScript code 
/// currently holds. This information can be used for debugging compaction and 
/// unload issues.
///
/// @RESTSTRUCT{waitingFor,collection_figures,string,optional,string}
/// An optional string value that contains information about which object type is at the 
/// head of the collection's cleanup queue. This information can be used for debugging 
/// compaction and unload issues.
///
/// @RESTSTRUCT{compactionStatus,collection_figures,object,optional,compactionStatus_attributes}
/// @RESTSTRUCT{message,compactionStatus_attributes,string,optional,string}
/// The action that was performed when the compaction was last run for the collection. 
/// This information can be used for debugging compaction issues.
///
/// @RESTSTRUCT{time,compactionStatus_attributes,string,optional,string}
/// The point in time the compaction for the collection was last executed. 
/// This information can be used for debugging compaction issues.
///
/// @RESTREPLYBODY{journalSize,integer,required,int64}
/// The maximal size of a journal or datafile in bytes.
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// Using an identifier and requesting the figures of the collection:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionFigures}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn);
///     coll.save({"test":"hello"});
///     require("internal").wal.flush(true, true);
///     var url = "/_api/collection/"+ coll.name() + "/figures";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSA_get_api_collection_revision
/// @brief Retrieve the collections revision id
///
/// @RESTHEADER{GET /_api/collection/{collection-name}/revision, Return collection revision id}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// In addition to the above, the result will also contain the
/// collection's revision id. The revision id is a server-generated
/// string that clients can use to check whether data in a collection
/// has changed since the last revision check.
///
/// - *revision*: The collection revision id as a string.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// Retrieving the revision of a collection
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionRevision}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: false });
///     var url = "/_api/collection/"+ coll.name() + "/revision";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSA_get_api_collection_checksum
/// @brief returns a checksum for the specified collection
///
/// @RESTHEADER{GET /_api/collection/{collection-name}/checksum, Return checksum for the collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTQUERYPARAMETERS
///
/// @RESTQUERYPARAM{withRevisions,boolean,optional}
/// Whether or not to include document revision ids in the checksum calculation.
///
/// @RESTQUERYPARAM{withData,boolean,optional}
/// Whether or not to include document body data in the checksum calculation.
///
/// @RESTDESCRIPTION
/// Will calculate a checksum of the meta-data (keys and optionally revision ids) and
/// optionally the document data in the collection.
///
/// The checksum can be used to compare if two collections on different ArangoDB
/// instances contain the same contents. The current revision of the collection is
/// returned too so one can make sure the checksums are calculated for the same
/// state of data.
///
/// By default, the checksum will only be calculated on the *_key* system attribute
/// of the documents contained in the collection. For edge collections, the system
/// attributes *_from* and *_to* will also be included in the calculation.
///
/// By setting the optional query parameter *withRevisions* to *true*, then revision
/// ids (*_rev* system attributes) are included in the checksumming.
///
/// By providing the optional query parameter *withData* with a value of *true*,
/// the user-defined document attributes will be included in the calculation too.
/// **Note**: Including user-defined attributes will make the checksumming slower.
///
/// The response is a JSON object with the following attributes:
///
/// - *checksum*: The calculated checksum as a number.
///
/// - *revision*: The collection revision id as a string.
///
/// **Note**: this method is not available in a cluster.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// Retrieving the checksum of a collection:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionChecksum}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn);
///     coll.save({ foo: "bar" });
///     var url = "/_api/collection/" + coll.name() + "/checksum";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Retrieving the checksum of a collection including the collection data,
/// but not the revisions:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionGetCollectionChecksumNoRev}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn);
///     coll.save({ foo: "bar" });
///     var url = "/_api/collection/" + coll.name() + "/checksum?withRevisions=false&withData=true";
///
///     var response = logCurlRequest('GET', url);
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function get_api_collection (req, res) {
  var name;
  var result;
  var sub;

  // .............................................................................
  // /_api/collection
  // .............................................................................

  if (req.suffix.length === 0 && req.parameters.id === undefined) {
    get_api_collections(req, res);
    return;
  }

  // .............................................................................
  // /_api/collection/<name>
  // .............................................................................

  name = decodeURIComponent(req.suffix[0]);

  var collection = arangodb.db._collection(name);

  if (collection === null) {
    actions.collectionNotFound(req, res, name);
    return;
  }

  var headers;

  // .............................................................................
  // /_api/collection/<name>
  // .............................................................................

  if (req.suffix.length === 1) {
    result = collectionRepresentation(collection, false, false, false);
    headers = {
      location : databasePrefix(req, "/_api/collection/" + collection.name())
    };
    actions.resultOk(req, res, actions.HTTP_OK, result, headers);
    return;
  }

  if (req.suffix.length === 2) {
    sub = decodeURIComponent(req.suffix[1]);

    // .............................................................................
    // /_api/collection/<identifier>/checksum
    // .............................................................................

    if (sub === "checksum") {
      var withRevisions = false;
      var withData = false;
      var value;
      if (req.parameters.hasOwnProperty('withRevisions')) {
        value = req.parameters.withRevisions.toLowerCase();
        if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
          withRevisions = true;
        }
      }

      if (req.parameters.hasOwnProperty('withData')) {
        value = req.parameters.withData.toLowerCase();
        if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
          withData = true;
        }
      }

      result = collectionRepresentation(collection, false, false, false);
      var checksum = collection.checksum(withRevisions, withData);
      result.checksum = checksum.checksum;
      result.revision = checksum.revision;
      actions.resultOk(req, res, actions.HTTP_OK, result);
    }

    // .............................................................................
    // /_api/collection/<identifier>/figures
    // .............................................................................

    else if (sub === "figures") {
      result = collectionRepresentation(collection, true, true, true);
      headers = {
        location : databasePrefix(req, "/_api/collection/" + collection.name() + "/figures")
      };
      actions.resultOk(req, res, actions.HTTP_OK, result, headers);
    }

    // .............................................................................
    // /_api/collection/<identifier>/count
    // .............................................................................

    else if (sub === "count") {
      result = collectionRepresentation(collection, true, true, false);
      headers = {
        location : databasePrefix(req, "/_api/collection/" + collection.name() + "/count")
      };
      actions.resultOk(req, res, actions.HTTP_OK, result, headers);
    }

    // .............................................................................
    // /_api/collection/<identifier>/properties
    // .............................................................................

    else if (sub === "properties") {
      result = collectionRepresentation(collection, true, false, false);
      headers = {
        location : databasePrefix(req, "/_api/collection/" + collection.name() + "/properties")
      };
      actions.resultOk(req, res, actions.HTTP_OK, result, headers);
    }

    // .............................................................................
    // /_api/collection/<identifier>/revision
    // .............................................................................

    else if (sub === "revision") {
      result = collectionRepresentation(collection, false, false, false);
      result.revision = collection.revision();
      actions.resultOk(req, res, actions.HTTP_OK, result);
    }

    else {
      actions.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND,
                             "expecting one of the resources 'count',"
                             +" 'figures', 'properties', 'parameter'");
    }
  }
  else {
    actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER,
                      "expect GET /_api/collection/<collection-name>/<method>");
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_put_api_collection_load
/// @brief loads a collection
///
/// @RESTHEADER{PUT /_api/collection/{collection-name}/load, Load collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// Loads a collection into memory. Returns the collection on success.
///
/// The request body object might optionally contain the following attribute:
///
/// - *count*: If set, this controls whether the return value should include
///   the number of documents in the collection. Setting *count* to
///   *false* may speed up loading a collection. The default value for
///   *count* is *true*.
///
/// On success an object with the following attributes is returned:
///
/// - *id*: The identifier of the collection.
///
/// - *name*: The name of the collection.
///
/// - *count*: The number of documents inside the collection. This is only
///   returned if the *count* input parameters is set to *true* or has
///   not been specified.
///
/// - *status*: The status of the collection as number.
///
/// - *type*: The collection type. Valid types are:
///   - 2: document collection
///   - 3: edges collection
///
/// - *isSystem*: If *true* then the collection is a system collection.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionIdentifierLoad}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/"+ coll.name() + "/load";
///
///     var response = logCurlRequest('PUT', url, '');
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function put_api_collection_load (req, res, collection) {
  try {
    collection.load();

    var showCount = true;
    var body = actions.getJsonBody(req, res);

    if (body && body.hasOwnProperty("count")) {
      showCount = body.count;
    }

    var result = collectionRepresentation(collection, false, showCount, false);

    actions.resultOk(req, res, actions.HTTP_OK, result);
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_put_api_collection_unload
/// @brief unloads a collection
///
/// @RESTHEADER{PUT /_api/collection/{collection-name}/unload, Unload collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
///
/// @RESTDESCRIPTION
/// Removes a collection from memory. This call does not delete any documents.
/// You can use the collection afterwards; in which case it will be loaded into
/// memory, again. On success an object with the following attributes is
/// returned:
///
/// - *id*: The identifier of the collection.
///
/// - *name*: The name of the collection.
///
/// - *status*: The status of the collection as number.
///
/// - *type*: The collection type. Valid types are:
///   - 2: document collection
///   - 3: edges collection
///
/// - *isSystem*: If *true* then the collection is a system collection.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404* is returned.
///
/// @EXAMPLES
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionIdentifierUnload}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/"+ coll.name() + "/unload";
///
///     var response = logCurlRequest('PUT', url, '');
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function put_api_collection_unload (req, res, collection) {
  try {
    if (req.parameters.hasOwnProperty('flush')) {
      var value = req.parameters.flush.toLowerCase();
      if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') {
        if (collection.status() === 3 /* loaded */ && 
            collection.figures().uncollectedLogfileEntries > 0) {
          // flush WAL so uncollected logfile entries can get collected
          require("internal").wal.flush();
        }
      }
    }

    // then unload
    collection.unload();

    var result = collectionRepresentation(collection);

    actions.resultOk(req, res, actions.HTTP_OK, result);
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_put_api_collection_truncate
/// @brief truncates a collection
///
/// @RESTHEADER{PUT /_api/collection/{collection-name}/truncate, Truncate collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// Removes all documents from the collection, but leaves the indexes intact.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionIdentifierTruncate}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/"+ coll.name() + "/truncate";
///
///     var response = logCurlRequest('PUT', url, '');
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function put_api_collection_truncate (req, res, collection) {
  try {
    collection.truncate();

    var result = collectionRepresentation(collection);

    actions.resultOk(req, res, actions.HTTP_OK, result);
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_put_api_collection_properties
/// @brief changes a collection
///
/// @RESTHEADER{PUT /_api/collection/{collection-name}/properties, Change properties of a collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// Changes the properties of a collection. Expects an object with the
/// attribute(s)
///
/// - *waitForSync*: If *true* then creating or changing a
///   document will wait until the data has been synchronized to disk.
///
/// - *journalSize*: The maximal size of a journal or datafile in bytes. 
///   The value must be at least `1048576` (1 MB). Note that when
///   changing the journalSize value, it will only have an effect for
///   additional journals or datafiles that are created. Already
///   existing journals or datafiles will not be affected.
///
/// On success an object with the following attributes is returned:
///
/// - *id*: The identifier of the collection.
///
/// - *name*: The name of the collection.
///
/// - *waitForSync*: The new value.
///
/// - *journalSize*: The new value.
///
/// - *status*: The status of the collection as number.
///
/// - *type*: The collection type. Valid types are:
///   - 2: document collection
///   - 3: edges collection
///
/// - *isSystem*: If *true* then the collection is a system collection.
///
/// - *isVolatile*: If *true* then the collection data will be
///   kept in memory only and ArangoDB will not write or sync the data
///   to disk.
///
/// - *doCompact*: Whether or not the collection will be compacted.
///
/// - *keyOptions*: JSON object which contains key generation options:
///   - *type*: specifies the type of the key generator. The currently
///     available generators are *traditional* and *autoincrement*.
///   - *allowUserKeys*: if set to *true*, then it is allowed to supply
///     own key values in the *_key* attribute of a document. If set to
///     *false*, then the key generator is solely responsible for
///     generating keys and supplying own key values in the *_key* attribute
///     of documents is considered an error.
///
/// **Note**: some other collection properties, such as *type*, *isVolatile*,
/// *numberOfShards* or *shardKeys* cannot be changed once a collection is
/// created.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
///
/// @EXAMPLES
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionIdentifierPropertiesSync}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/"+ coll.name() + "/properties";
///
///     var response = logCurlRequest('PUT', url, {"waitForSync" : true });
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
///     db._drop(cn);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function put_api_collection_properties (req, res, collection) {
  var body = actions.getJsonBody(req, res);

  if (body === undefined) {
    return;
  }

  try {
    collection.properties(body);

    var result = collectionRepresentation(collection, true);

    actions.resultOk(req, res, actions.HTTP_OK, result);
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_put_api_collection_rename
/// @brief renames a collection
///
/// @RESTHEADER{PUT /_api/collection/{collection-name}/rename, Rename collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection to rename.
///
/// @RESTDESCRIPTION
/// Renames a collection. Expects an object with the attribute(s)
///
/// - *name*: The new name.
///
/// It returns an object with the attributes
///
/// - *id*: The identifier of the collection.
///
/// - *name*: The new name of the collection.
///
/// - *status*: The status of the collection as number.
///
/// - *type*: The collection type. Valid types are:
///   - 2: document collection
///   - 3: edges collection
///
/// - *isSystem*: If *true* then the collection is a system collection.
///
/// If renaming the collection succeeds, then the collection is also renamed in 
/// all graph definitions inside the `_graphs` collection in the current database.
///
/// **Note**: this method is not available in a cluster.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404*
/// is returned.
/// @EXAMPLES
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionIdentifierRename}
///     var cn = "products1";
///     var coll = db._create(cn);
///     var url = "/_api/collection/" + coll.name() + "/rename";
///
///     var response = logCurlRequest('PUT', url, { name: "newname" });
///
///     assert(response.code === 200);
///     db._flushCache();
///     db._drop("newname");
///
///     logJsonResponse(response);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function put_api_collection_rename (req, res, collection) {
  var body = actions.getJsonBody(req, res);

  if (body === undefined) {
    return;
  }

  if (! body.hasOwnProperty("name")) {
    actions.resultBad(req, res, arangodb.ERROR_ARANGO_ILLEGAL_NAME,
                      "name must be non-empty");
    return;
  }

  var name = body.name;

  try {
    collection.rename(name);

    var result = collectionRepresentation(collection);

    actions.resultOk(req, res, actions.HTTP_OK, result);
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_put_api_collection_rotate
/// @brief rotates the journal of a collection
///
/// @RESTHEADER{PUT /_api/collection/{collection-name}/rotate, Rotate journal of a collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection.
///
/// @RESTDESCRIPTION
/// Rotates the journal of a collection. The current journal of the collection will be closed
/// and made a read-only datafile. The purpose of the rotate method is to make the data in
/// the file available for compaction (compaction is only performed for read-only datafiles, and
/// not for journals).
///
/// Saving new data in the collection subsequently will create a new journal file
/// automatically if there is no current journal.
///
/// It returns an object with the attributes
///
/// - *result*: will be *true* if rotation succeeded
///
/// **Note**: This method is not available in a cluster.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the collection currently has no journal, *HTTP 400* is returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404* is returned.
///
/// @EXAMPLES
///
/// Rotating the journal:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionRotate}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn);
///     coll.save({ "test" : true });
///     require("internal").wal.flush(true, true);
///
///     var url = "/_api/collection/"+ coll.name() + "/rotate";
///     var response = logCurlRequest('PUT', url, { });
///
///     assert(response.code === 200);
///     db._flushCache();
///     db._drop(cn);
///
///     logJsonResponse(response);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Rotating if no journal exists:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionRotateNoJournal}
///     var cn = "products";
///     db._drop(cn);
///     var coll = db._create(cn);
///     var url = "/_api/collection/"+ coll.name() + "/rotate";
///
///     var response = logCurlRequest('PUT', url, { });
///
///     assert(response.code === 400);
///     db._flushCache();
///     db._drop(cn);
///
///     logJsonResponse(response);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function put_api_collection_rotate (req, res, collection) {
  try {
    collection.rotate();

    actions.resultOk(req, res, actions.HTTP_OK, { result: true });
  }
  catch (err) {
    actions.resultException(req, res, err, undefined, false);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief changes a collection
////////////////////////////////////////////////////////////////////////////////

function put_api_collection (req, res) {
  if (req.suffix.length !== 2) {
    actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER,
                      "expected PUT /_api/collection/<collection-name>/<action>");
    return;
  }

  var name = decodeURIComponent(req.suffix[0]);
  var collection = arangodb.db._collection(name);

  if (collection === null) {
    actions.collectionNotFound(req, res, name);
    return;
  }

  var sub = decodeURIComponent(req.suffix[1]);

  if (sub === "load") {
    put_api_collection_load(req, res, collection);
  }
  else if (sub === "unload") {
    put_api_collection_unload(req, res, collection);
    collection = null;
    // run garbage collection once in all threads
    require("internal").executeGlobalContextFunction("collectGarbage");
  }
  else if (sub === "truncate") {
    put_api_collection_truncate(req, res, collection);
  }
  else if (sub === "properties") {
    put_api_collection_properties(req, res, collection);
  }
  else if (sub === "rename") {
    put_api_collection_rename(req, res, collection);
  }
  else if (sub === "rotate") {
    put_api_collection_rotate(req, res, collection);
  }
  else {
    actions.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND,
                           "expecting one of the actions 'load', 'unload',"
                           + " 'truncate', 'properties', 'rename'");
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_delete_api_collection
/// @brief drops a collection
///
/// @RESTHEADER{DELETE /_api/collection/{collection-name}, Drops collection}
///
/// @RESTURLPARAMETERS
///
/// @RESTURLPARAM{collection-name,string,required}
/// The name of the collection to drop.
///
/// @RESTDESCRIPTION
/// Drops the collection identified by *collection-name*.
///
/// If the collection was successfully dropped, an object is returned with
/// the following attributes:
///
/// - *error*: *false*
///
/// - *id*: The identifier of the dropped collection.
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{400}
/// If the *collection-name* is missing, then a *HTTP 400* is
/// returned.
///
/// @RESTRETURNCODE{404}
/// If the *collection-name* is unknown, then a *HTTP 404* is returned.
///
/// @EXAMPLES
///
/// Using an identifier:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionDeleteCollectionIdentifier}
///     var cn = "products1";
///     var coll = db._create(cn, { waitForSync: true });
///     var url = "/_api/collection/"+ coll._id;
///
///     var response = logCurlRequest('DELETE', url);
///     db[cn] = undefined;
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Using a name:
///
/// @EXAMPLE_ARANGOSH_RUN{RestCollectionDeleteCollectionName}
///     var cn = "products1";
///     db._drop(cn);
///     db._create(cn);
///     var url = "/_api/collection/products1";
///
///     var response = logCurlRequest('DELETE', url);
///     db[cn] = undefined;
///
///     assert(response.code === 200);
///
///     logJsonResponse(response);
/// @END_EXAMPLE_ARANGOSH_RUN
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////

function delete_api_collection (req, res) {
  if (req.suffix.length !== 1) {
    actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER,
                      "expected DELETE /_api/collection/<collection-name>");
  }
  else {
    var name = decodeURIComponent(req.suffix[0]);
    var collection = arangodb.db._collection(name);

    if (collection === null) {
      actions.collectionNotFound(req, res, name);
    }
    else {
      try {
        var result = {
          id : collection._id
        };

        collection.drop();

        actions.resultOk(req, res, actions.HTTP_OK, result);
      }
      catch (err) {
        actions.resultException(req, res, err, undefined, false);
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief handles a collection request
////////////////////////////////////////////////////////////////////////////////

actions.defineHttp({
  url : "_api/collection",

  callback : function (req, res) {
    try {
      if (req.requestType === actions.GET) {
        get_api_collection(req, res);
      }
      else if (req.requestType === actions.DELETE) {
        delete_api_collection(req, res);
      }
      else if (req.requestType === actions.POST) {
        post_api_collection(req, res);
      }
      else if (req.requestType === actions.PUT) {
        put_api_collection(req, res);
      }
      else {
        actions.resultUnsupported(req, res);
      }
    }
    catch (err) {
      actions.resultException(req, res, err, undefined, false);
    }
  }
});

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|/// @startDocuBlock\\|// --SECTION--\\|/// @\\}"
// End:
