/*jslint indent: 2, maxlen: 80, nomen: true, regexp: true, unparam: true */
/*global define, window, RSVP, jIO, complex_queries */

(function (dependencies, module) {
  "use strict";
  if (typeof define === 'function' && define.amd) {
    return define(dependencies, module);
  }
  window.query_storage = {};
  module(window.query_storage, RSVP, jIO);
}(['exports', 'rsvp', 'jio'], function (exports, RSVP, jIO) {
  "use strict";


  /**
   * The jIO QueryStorage extension
   *
   * @class QueryStorage
   * @constructor
   */
  function QueryStorage(spec) {
    this._substorage = jIO.createJIO(spec.sub_storage);
    this._key_schema = spec.key_schema;
  }


  /**
   * Get a document
   * Parameters are passed through to the sub storage.
   *
   * @method get
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.get = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.get.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Create a document in the sub storage.
   * Parameters are passed through to the sub storage.
   *
   * @method post
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.post = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.post.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Create or update a document in the sub storage.
   * Parameters are passed through to the sub storage.
   *
   * @method put
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.put = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.put.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Remove a document.
   * Parameters are passed through to the sub storage.
   *
   * @method remove
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.remove = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.remove.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Get an attachment.
   * Parameters are passed through to the sub storage.
   *
   * @method getAttachment
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.getAttachment = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.getAttachment.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Add an attachment to a document.
   * Parameters are passed through to the sub storage.
   *
   * @method putAttachment
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.putAttachment = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.putAttachment.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Remove an attachment.
   * Parameters are passed through to the sub storage.
   *
   * @method removeAttachment
   * @param  {Object} command The JIO command
   */
  QueryStorage.prototype.removeAttachment = function (command) {
    var args = [].slice.call(arguments, 1);
    this._substorage.removeAttachment.apply(this._substorage, args).
      then(command.success, command.error, command.notify);
  };


  /**
   * Retrieve documents.
   * This method performs an .allDocs() call on the substorage,
   * retrieving everything, then runs a complex query on the result.
   *
   * @method allDocs
   * @param  {Object} command The given parameters
   * @param  {Object} options The command options
   */
  QueryStorage.prototype.allDocs = function (command, param, options) {
    var that = this,
      // we need the full documents in order to perform the query, will
      // remove them later if they were not required.
      include_docs = (options.include_docs || options.query) ? true : false;

    this._substorage.allDocs({include_docs: include_docs}).
      then(function (response) {

        if (options.query) {
          var docs = response.data.rows.map(function (row) {
            return row.doc;
          }), rows_map = {};

          // build a mapping to avoid slowness
          response.data.rows.forEach(function (row) {
            rows_map[row.id] = row;
          });

          return complex_queries.QueryFactory.create(options.query,
                                                     that._key_schema).
            exec(docs, options).
            then(function (filtered_docs) {
              // reconstruct filtered rows, preserving the order from docs
              var rows = filtered_docs.map(function (doc) {
                var row = rows_map[doc._id];
                // remove docs if not needed in the original call
                if (!options.include_docs) {
                  delete row.doc;
                }
                return row;
              });
              response.data.rows = rows;
              response.data.total_rows = rows.length;
              return response;
            });
        }
        return RSVP.resolve(response);

      }).
      then(command.success, command.error, command.notify);
  };


  jIO.addStorage('query', QueryStorage);

}));