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

import _ from 'lodash';
import $ from 'jquery';
import Backbone from "backbone";
import app from '../../../app/app';

//PagingCollection
//----------------

// A PagingCollection knows how to build appropriate requests to the
// CouchDB-like server and how to fetch. The Collection will always contain a
// single page of documents.

export const PagingCollection = Backbone.Collection.extend({

  // initialize parameters and page size
  constructor: function() {
    Backbone.Collection.apply(this, arguments);
    this.configure.apply(this, arguments);
  },

  configure: function(collections, options) {
    var querystring = _.result(this, "url").split("?")[1] || "";
    this.paging = _.defaults((options.paging || {}), {
      defaultParams: _.defaults({}, options.params, this._parseQueryString(querystring)),
      hasNext: false,
      hasPrevious: false,
      params: {},
      pageSize: 20,
      direction: undefined
    });

    this.paging.params = _.clone(this.paging.defaultParams);
    this.updateUrlQuery(this.paging.defaultParams);
  },

  calculateParams: function(currentParams, skipIncrement, limitIncrement) {

    var params = _.clone(currentParams);
    params.skip = (parseInt(currentParams.skip, 10) || 0) + skipIncrement;

    // guard against hard limits
    if (this.paging.defaultParams.limit) {
      params.limit = Math.min(this.paging.defaultParams.limit, params.limit);
    }
    // request an extra row so we know that there are more results
    params.limit = limitIncrement + 1;
    // prevent illegal skip values
    params.skip = Math.max(params.skip, 0);

    return params;
  },

  pageSizeReset: function(pageSize, opts) {
    var options = _.defaults((opts || {}), {fetch: true});
    this.paging.direction = undefined;
    this.paging.pageSize = pageSize;
    this.paging.params = this.paging.defaultParams;
    this.paging.params.limit = pageSize;
    this.updateUrlQuery(this.paging.params);
    if (options.fetch) {
      return this.fetch();
    }
  },

  _parseQueryString: function(uri) {
    var queryString = decodeURI(uri).split(/&/);

    return _.reduce(queryString, function (parsedQuery, item) {
        var nameValue = item.split(/=/);
        if (nameValue.length === 2) {
          parsedQuery[nameValue[0]] = nameValue[1];
        }

        return parsedQuery;
    }, {});
  },

  _iterate: function(offset, opts) {
    var options = _.defaults((opts || {}), {fetch: true});

    this.paging.params = this.calculateParams(this.paging.params, offset, this.paging.pageSize);

    // Fetch the next page of documents
    this.updateUrlQuery(this.paging.params);
    if (options.fetch) {
      return this.fetch({reset: true});
    }
  },

  // `next` is called with the number of items for the next page.
  // It returns the fetch promise.
  next: function(options) {
    this.paging.direction = "next";
    return this._iterate(this.paging.pageSize, options);
  },

  // `previous` is called with the number of items for the previous page.
  // It returns the fetch promise.
  previous: function(options) {
    this.paging.direction = "previous";
    return this._iterate(0 - this.paging.pageSize, options);
  },

  shouldStringify: function (val) {
    try {
      JSON.parse(val);
      return false;
    } catch (e) {
      return true;
    }
  },

  // Encodes the parameters so that couchdb will understand them
  // and then sets the url with the new url.
  updateUrlQuery: function (params) {
    var url = _.result(this, "url").split("?")[0];

    _.each(['startkey', 'endkey', 'key'], (key) => {
      if (_.has(params, key) && this.shouldStringify(params[key])) {
        params[key] = JSON.stringify(params[key]);
      }
    });

    this.url = url + '?' + app.utils.queryParams(params);
  },

  fetch: function () {
    // if this is a fetch for the first time, fetch one extra to see if there is a next
    if (!this.paging.direction && this.paging.params.limit > 0) {
      this.paging.direction = 'fetch';
      this.paging.params.limit = this.paging.params.limit + 1;
      this.updateUrlQuery(this.paging.params);
    }

    return Backbone.Collection.prototype.fetch.apply(this, arguments);
  },

  parse: function (resp) {
    var rows = resp.rows;

    this.paging.hasNext = this.paging.hasPrevious = false;

    this.viewMeta = {
      total_rows: resp.total_rows,
      offset: resp.offset,
      update_seq: resp.update_seq
    };

    if (this.paging.params.skip > 0) {
      this.paging.hasPrevious = true;
    }

    if (rows.length === this.paging.pageSize + 1) {
      this.paging.hasNext = true;

      // remove the next page marker result
      rows.pop();
      this.viewMeta.total_rows = this.viewMeta.total_rows - 1;
    }
    return rows;
  },

  hasNext: function() {
    return this.paging.hasNext;
  },

  hasPrevious: function() {
    return this.paging.hasPrevious;
  }
});

export default PagingCollection;


//   if (exports) {
//     // Overload the Backbone.ajax method, this allows PagingCollection to be able to
//     // work in node.js
//     exports.setAjax = function (ajax) {
//       Backbone.ajax = ajax;
//     };

//     exports.PagingCollection = PagingCollection;
//   }

//   return PagingCollection;
// }));


