dojo.provide("parus.gridData");
dojo.require("dojo.data.util.filter");

dojo.require("parus.parus8");
dojo.require("parus.database");

dojo.declare("parus.gridData", null, {

  parentUnitCode: null,

  parentRN: null,

  catalogRN: null,

  identityFieldName: "ID",

  asJson: false,

  _found: null,

  _closed: false,

  onGetConditions: function(store) {
    //to override;
  },

  onComplete: function(store, items, keywordArgs) {
    //to override;
  },

  onClose: function() {
    //to override;
  },

  getConditions: function() {
    return this.onGetConditions(this)
  },

  _data: null,

  _queryMade: null,

  persistent: null,

  sync: false,

  constructor: function(keywordParameters) {
    if (keywordParameters) dojo.mixin(this, keywordParameters);
  },

  destroy: function() {
    this.close();
    this._data = null;
    this._found = null;
  },

  getValue: function(item, attribute, defaultValue) {
    return parus.parus8.nodeField(item, attribute) || defaultValue || null;
  },

  orderByClause: function(sort) {
    var clause = [];
    if (sort) dojo.forEach(sort, function(item) {
      if (item.attribute) dojo.forEach(item.attribute.split(","), function(attr) {
        clause.push(item.descending ? (attr + " desc") : attr);
      });
    });
    return clause.join(",");
  },

  __lastConditions: null,

  __lastSort: null,

  __lastConditionsData: null,

  query: function(query, options) {
    return this.fetch(
    dojo.mixin(options, {
      query: query
    }));
  },

  fetch: function(keywordArgs) {
    this._closed = false;

    if (keywordArgs.query) if (keywordArgs.query[""]) delete keywordArgs.query[""];

    if (!keywordArgs.sort && this.__lastConditions && this.__lastConditions.__sort) keywordArgs.sort = keywordArgs.sort || this.__lastConditions.__sort;

    var gotConditions = this.getConditions() || {};

    //console.log("fetch", this, keywordArgs, keywordArgs.query);
    var queryMade = dojo.toJson({
      query: keywordArgs.query || {},
      queryOptions: keywordArgs.queryOptions || {},
      sort: keywordArgs.sort,
      conditions: gotConditions
    });

    if (!this.persistent && (queryMade != this._queryMade)) {
      //console.log("fetch.query changed", {query: queryMade, oldQuery: this._queryMade});
      this._data = null;
      this._queryMade = queryMade;
    };

    var items = null;

    var conditions = dojo.clone(gotConditions);
    dojo.mixin(conditions, keywordArgs.query);

    var _conditions = dojo.clone(conditions);

    if (keywordArgs.sort) dojo.mixin(_conditions, {
      __sort: keywordArgs.sort
    });

    if (this.__lastConditions && this.__lastConditionsData && parus.parus8.compareObjects(this.__lastConditions || {}, _conditions)) {
      items = dojo.clone(this.__lastConditionsData);
    }
    else {

      this.__lastConditions = {};
      this._found = null;

      if (this._data && this.persistent) {


        if (keywordArgs.sort && !parus.parus8.compareObjects(this.__lastSort || {}, keywordArgs.sort)) {

          this.__lastSort = keywordArgs.sort;

          this._data.sort(dojo.hitch(this, function(a, b) {

            var result = 0;
            var str = keywordArgs.sort;
            if (!dojo.isArray(str)) str = [str];

            for (var i = 0; i < str.length; i++) {
              var va = this.getValue(a, str[i].field || str[i].attribute) || null;
              var vb = this.getValue(b, str[i].field || str[i].attribute) || null;
              var nva = va ? dojo.number.parse(va.toString().split(".").join(",")) : Number.NaN;
              var nvb = vb ? dojo.number.parse(vb.toString().split(".").join(",")) : Number.NaN;

              if (!isNaN(nva) && !isNaN(nvb)) {
                va = nva;
                vb = nvb;
              };

              if (va > vb) result = 1
              else if (va < vb) result = -1;


              if (str[i].descending) result = -result;
              if (result) return result;

            }
            return result;
          }));

        }

        items = dojo.clone(this._data);

        for (var key in conditions) {
          if (key) {
            var pattern = conditions[key];
            if (pattern) {
              var matcher = null;
              if (dojo.isObject(pattern) && dojo.isRegExp(pattern)) matcher = pattern
              else if (dojo.isString(pattern)) matcher = dojo.data.util.filter.patternToRegExp(pattern, /*keywordArgs.queryOptions.ignoreCase*/ false);

              if (matcher) items = dojo.filter(items, dojo.hitch(this, function(item) {
                return (this.getValue(item, key) || "").match(matcher);
              }));
            }
          }
        };

        this.__lastConditions = _conditions;
        this.__lastConditionsData = items;

      };

    }

    var conditions = {};

    var order = this.orderByClause(keywordArgs.sort);
    if (order) dojo.mixin(conditions, {
      order: order
    });

    dojo.mixin(conditions, gotConditions);
    dojo.mixin(conditions, keywordArgs.query);

    var afterparty = dojo.hitch(this, function(items) {

      items = this.fetchFilter(items);

      var startIndex = keywordArgs.start || 0;
      var endIndex = (keywordArgs.count && (keywordArgs.count !== Infinity)) ? (startIndex + keywordArgs.count) : items.length;

      if (startIndex < 0) {
        endIndex = endIndex + startIndex;
        startIndex = 0;
      };

      if (keywordArgs.onBegin) keywordArgs.onBegin(items.length, keywordArgs);

      if (keywordArgs.onItem) for (var i = startIndex;

      (i < items.length) && (i < endIndex); i++) {
        keywordArgs.onItem(items[i], keywordArgs)
      };

      this.onComplete(this, items, keywordArgs);

      if (keywordArgs.onComplete) {

        var subset = null;
        if (!keywordArgs.onItem) {
          subset = items.slice(startIndex, endIndex);
        };

        keywordArgs.onComplete(subset, keywordArgs);

      }

      if (keywordArgs.query) if (keywordArgs.query.onComplete) keywordArgs.query.onComplete(this, items, keywordArgs);

    });

    items = items || this._data;

    if (!items) {
      items = this.asJson ? parus.database.queryObject(this.queryid, conditions) : parus.parus8.rowNodes(parus.database.query(this.queryid, conditions));
      this._data = items;
      this._found = null;
    }

    if (this.sync) afterparty(items)
    else setTimeout(function() {
      afterparty(items)
    }, 0);

    return keywordArgs;
  },

  close: function(request) {
    if (this._closed) return;
    if (this.onClose) this.onClose();
    this._data = null;
    this._found = null;
    this._queryMade = null;
    this.__lastConditions = null;
    this.__lastSort = null;
    this.__lastConditionsData = null;
    this._closed = true;
  },

  getIdentity: function(item) {
    return parus.parus8.nodeField(item, this.identityFieldName);
  },

  getIdentityAttributes: function(item) {
    return [this.identityFieldName];
  },

  //trivial
  getFeatures: function() {
    return {
      "dojo.data.api.Read": true,
      "dojo.data.api.Identity": true
    };
  },

  isItem: function(something) {
    return true;
  },

  fetchItemByIdentity: function(keywordArgs) {
    try {
      this.fetch(keywordArgs);

      var idx = this.findIndex(this.identityFieldName, keywordArgs.identity);
      var item = idx >= 0 ? this._data[idx] : null;

      if (keywordArgs.onItem) {
        var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
        keywordArgs.onItem.call(scope, item);
      }
    }
    catch (e) {
      if (keywordArgs.onError) {
        var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
        keywordArgs.onError.call(scope, e);
      }
    }
  },

  //unimplemented
  getValues: function(item, attribute) {
    parus.parus8.unimplemented(this, "getValues", arguments);
  },

  hasAttribute: function(item, attribute) {
    parus.parus8.unimplemented(this, "hasAttribute", arguments);
  },

  containsValue: function(item, attribute, value) {
    parus.parus8.unimplemented(this, "containsValue", arguments);
  },

  isItemLoaded: function(something) {
    parus.parus8.unimplemented(this, "isItemLoaded", arguments);
  },

  loadItem: function(keywordArgs) {
    parus.parus8.unimplemented(this, "loadItem", arguments);
  },

  getLabel: function(item) {
    parus.parus8.unimplemented(this, "getLabel", arguments);
  },

  getLabelAttributes: function(item) {
    parus.parus8.unimplemented(this, "getLabelAttributes", arguments);
  },

  //non-API functions
  find: function(fieldName, value) {

    if (!value) return null;

    var found = null;

    if (this._found) {
      var f = this._found[fieldName];
      if (f && f.hasOwnProperty(value)) {
        found = f[value] || found;
        if (found) return found;
      }
    };

    var items = dojo.clone(this._data);

    var conditions = dojo.clone(this.__lastConditions || {});
    for (var key in conditions) {
      if (key) {
        var pattern = conditions[key];
        if (pattern) if (dojo.isString(pattern)) {
          var matcher = dojo.data.util.filter.patternToRegExp(pattern, /*keywordArgs.queryOptions.ignoreCase*/ false);
          items = dojo.filter(items, dojo.hitch(this, function(item) {
            return (this.getValue(item, key) || "").match(matcher);
          }));
        }
      }
    };

    dojo.every(items, dojo.hitch(this, function(item) {
      if (item) if (this.getValue(item, fieldName) == value) found = item;
      return !!!found;
    }));

    this._found = this._found || {};
    this._found[fieldName] = this._found[fieldName] || {};
    this._found[fieldName][value] = found;

    return found;
  },

  findIndex: function(fieldName, value) {

    if (!value) return -1;

    var found = 0;

    var items = dojo.clone(this._data);
    items = this.fetchFilter(items);
    var conditions = dojo.clone(this.__lastConditions || {});
    for (var key in conditions) {
      var pattern = conditions[key];
      if (pattern) if (dojo.isString(pattern)) {
        var matcher = dojo.data.util.filter.patternToRegExp(pattern, /*keywordArgs.queryOptions.ignoreCase*/ false);
        items = dojo.filter(items, dojo.hitch(this, function(item) {
          return (this.getValue(item, key) || "").match(matcher);
        }));
      }
    };

    dojo.every(items, dojo.hitch(this, function(item, idx) {
      if (this.getValue(item, fieldName) == value) found = idx + 1;
      return !!!found;
    }));

    return found - 1;
  },

  add: function(fields) {
    this._data.push(dojo.clone(fields));
    this.__lastConditions = null;
    this.__lastConditionsData = null;
  },


  remove: function(compareFunc) {
    this._data = dojo.filter(this._data, function(item) {
      return !compareFunc(item)
    });
    this.__lastConditions = null;
    this.__lastConditionsData = null;
  },


  forEach: function(proc, ignoreFilters) {

    var items = dojo.clone(this._data);

    if (!ignoreFilters) {
      items = this.fetchFilter(items);

      var conditions = dojo.clone(this.__lastConditions || {});
      for (var key in conditions) {
        var pattern = conditions[key];
        if (pattern) if (dojo.isString(pattern)) {
          var matcher = dojo.data.util.filter.patternToRegExp(pattern, /*keywordArgs.queryOptions.ignoreCase*/ false);
          items = dojo.filter(items, dojo.hitch(this, function(item) {
            return (this.getValue(item, key) || "").match(matcher);
          }));
        }
      };
    }

    dojo.forEach(items, dojo.hitch(this, function(item, idx) {
      proc(this, item, idx);
    }));
  },

  fetchFilter: function(items) {
    return items;
  }

});


dojo.declare("parus.emptyGridData", null, {

  parentUnitCode: null,

  parentRN: null,

  catalogRN: null,

  identityFieldName: null,

  onGetConditions: function(store) {},

  onComplete: function(store, items, keywordArgs) {},

  getConditions: function() {},

  persistent: null,

  constructor: function(keywordParameters) {
    if (keywordParameters) dojo.mixin(this, keywordParameters);
  },

  destroy: function() {},

  getValue: function(item, attribute, defaultValue) {},

  orderByClause: function(sort) {
    return ""
  },

  fetch: function(keywordArgs) {

    if (keywordArgs.onBegin) keywordArgs.onBegin(0, keywordArgs);

    if (keywordArgs.onComplete) keywordArgs.onComplete([], keywordArgs);

    return keywordArgs;
  },


  close: function(request) {},

  getIdentity: function(item) {},

  getIdentityAttributes: function(item) {},

  //trivial
  getFeatures: function() {
    return {
      "dojo.data.api.Read": true,
      "dojo.data.api.Identity": true
    };
  },

  isItem: function(something) {},

  //unimplemented
  fetchItemByIdentity: function(keywordArgs) {
    parus.parus8.unimplemented(this, "fetchItemByIdentity", arguments);
  },

  getValues: function(item, attribute) {
    parus.parus8.unimplemented(this, "getValues", arguments);
  },

  hasAttribute: function(item, attribute) {
    parus.parus8.unimplemented(this, "hasAttribute", arguments);
  },

  containsValue: function(item, attribute, value) {
    parus.parus8.unimplemented(this, "containsValue", arguments);
  },

  isItemLoaded: function(something) {
    parus.parus8.unimplemented(this, "isItemLoaded", arguments);
  },

  loadItem: function(keywordArgs) {
    parus.parus8.unimplemented(this, "loadItem", arguments);
  },

  getLabel: function(item) {
    parus.parus8.unimplemented(this, "getLabel", arguments);
  },

  getLabelAttributes: function(item) {
    parus.parus8.unimplemented(this, "getLabelAttributes", arguments);
  },

  //non-API functions
  find: function(fieldName, value) {},

  findIndex: function(fieldName, value) {},

  add: function(fields) {},

  forEach: function(proc) {},

  fetchFilter: function(items) {
    return items;
  }

});