// Generated by CoffeeScript 1.3.3
var __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  __slice = [].slice,
  __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

define(['mediator', 'underscore', 'backbone', './channels'], function(mediator, _, Backbone, channels) {
  var DataContext, DataContextNode, DataContexts, isBranch, isComposite, isCondition, parseAttrs, updateAttrs;
  isBranch = function(attrs) {
    var _ref;
    return (attrs.type === 'and' || attrs.type === 'or') && ((_ref = attrs.children) != null ? _ref.length : void 0) >= 2;
  };
  isCondition = function(attrs) {
    return attrs.operator && attrs.id && (attrs.value != null);
  };
  isComposite = function(attrs) {
    return attrs.composite === true && attrs.id;
  };
  DataContextNode = (function(_super) {

    __extends(DataContextNode, _super);

    function DataContextNode() {
      return DataContextNode.__super__.constructor.apply(this, arguments);
    }

    DataContextNode.prototype.validate = function(attrs) {
      var key, value;
      if (isBranch(attrs) || isCondition(attrs) || isComposite(attrs)) {
        return;
      }
      for (key in attrs) {
        value = attrs[key];
        if (!(value != null)) {
          return 'Unknown node type';
        }
      }
    };

    DataContextNode.prototype.toJSON = function() {
      var json, node;
      json = null;
      if (this.isBranch()) {
        json = {
          type: this.get('type'),
          children: (function() {
            var _i, _len, _ref, _results;
            _ref = this.get('children');
            _results = [];
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              node = _ref[_i];
              _results.push(node.toJSON());
            }
            return _results;
          }).call(this)
        };
      } else if (this.isComposite()) {
        json = {
          id: this.get('id'),
          composite: this.get('composite')
        };
      } else if (this.isCondition()) {
        json = {
          id: this.get('id'),
          operator: this.get('operator'),
          value: this.get('value')
        };
      }
      return json;
    };

    DataContextNode.prototype.isRoot = function() {
      return !(this.parent != null);
    };

    DataContextNode.prototype.isEmpty = function() {
      return _.isEmpty(this.attributes);
    };

    DataContextNode.prototype.isBranch = function() {
      return isBranch(this.attributes);
    };

    DataContextNode.prototype.isCondition = function() {
      return isCondition(this.attributes);
    };

    DataContextNode.prototype.isComposite = function() {
      return isComposite(this.attributes);
    };

    DataContextNode.prototype.siblings = function() {
      if (this.isRoot()) {
        return false;
      } else {
        return _.without(this.parent.get('children'), this);
      }
    };

    DataContextNode.prototype.promote = function() {
      var children, nodes, type,
        _this = this;
      nodes = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      if (nodes.length === 0) {
        throw new Error('At least one node must be supplied');
      }
      if (this.isRoot()) {
        type = 'and';
      } else {
        type = this.parent.get('type') === 'and' ? 'or' : 'and';
      }
      children = _.map([this.attributes].concat(__slice.call(nodes)), function(attrs) {
        return parseAttrs(attrs, _this);
      });
      this.clear({
        slient: true
      });
      this.set({
        type: type,
        children: children
      });
      return this;
    };

    DataContextNode.prototype.demote = function() {
      if (this.isRoot()) {
        return false;
      }
      if (this.parent.isRoot()) {
        if (this.siblings().length === 0) {
          this.parent.clear({
            silent: true
          });
          this.parent.set(this.attributes);
          return this.parent;
        }
        return false;
      }
      this.parent.parent.get('children').push(this.remove());
      return this;
    };

    DataContextNode.prototype.add = function() {
      var nodes, _ref;
      nodes = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      if (!this.isBranch()) {
        throw new Error('Node is not a branch. Use "promote" to convert it into one');
      }
      (_ref = this.get('children')).push.apply(_ref, nodes);
      return this;
    };

    DataContextNode.prototype.remove = function() {
      var children, idx;
      if (this.isRoot()) {
        this.clear();
      } else {
        children = this.parent.get('children');
        if ((idx = children.indexOf(this)) >= 0) {
          children.splice(idx, 1)[0];
          if (children.length === 1) {
            children[0].demote();
          }
        }
      }
      return this;
    };

    return DataContextNode;

  })(Backbone.Model);
  parseAttrs = function(attrs, parent, callback) {
    var children, node;
    if (!attrs || _.isEmpty(attrs)) {
      node = new DataContextNode;
    } else if (attrs instanceof DataContextNode) {
      node = attrs;
    } else if (isBranch(attrs)) {
      node = new DataContextNode({
        type: attrs.type
      });
      children = _.map(attrs.children, function(_attrs) {
        return parseAttrs(_attrs, node, callback);
      });
      node.set({
        children: children
      });
    } else if (isCondition(attrs)) {
      node = new DataContextNode(attrs);
    } else if (isComposite(attrs)) {
      node = new DataContextNode(attrs);
    } else {
      throw new Error('Unknown node type');
    }
    if (parent) {
      node.parent = parent;
    }
    if (typeof callback === "function") {
      callback(node);
    }
    return node;
  };
  updateAttrs = function(node, attrs) {
    var children, i, _attrs, _i, _len, _ref, _results;
    if (node.isBranch()) {
      children = node.get('children');
      _ref = attrs.children;
      _results = [];
      for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
        _attrs = _ref[i];
        _results.push(updateAttrs(children[i], _attrs));
      }
      return _results;
    } else {
      return node.set(attrs);
    }
  };
  DataContext = (function(_super) {

    __extends(DataContext, _super);

    function DataContext() {
      this._deferenceNode = __bind(this._deferenceNode, this);

      this._referenceNode = __bind(this._referenceNode, this);

      this._nodeSave = __bind(this._nodeSave, this);

      this.save = __bind(this.save, this);

      this.parse = __bind(this.parse, this);
      return DataContext.__super__.constructor.apply(this, arguments);
    }

    DataContext.prototype.deferred = {
      save: true,
      clear: true,
      add: false,
      remove: false
    };

    DataContext.prototype.url = function() {
      if (this.isNew()) {
        return DataContext.__super__.url.apply(this, arguments);
      }
      return this.get('_links').self.href;
    };

    DataContext.prototype.initialize = function() {
      var _ref, _ref1, _ref2,
        _this = this;
      DataContext.__super__.initialize.apply(this, arguments);
      if ((_ref = this.nodes) == null) {
        this.nodes = {};
      }
      if ((_ref1 = this.clientNodes) == null) {
        this.clientNodes = {};
      }
      if ((_ref2 = this.root) == null) {
        this.root = new DataContextNode;
      }
      if (this.isArchived()) {
        this.resolve();
        return;
      }
      this.on('sync', function() {
        this.resolve();
        return mediator.publish(channels.DATACONTEXT_SYNCED, this, 'success');
      });
      this.on('error', function() {
        return mediator.publish(channels.DATACONTEXT_SYNCED, this, 'error');
      });
      this.on('change', function() {
        return mediator.publish(channels.DATACONTEXT_CHANGED, this);
      });
      mediator.subscribe(channels.DATACONTEXT_PAUSE, function(id) {
        if (_this.id === id || !id && _this.isSession()) {
          return _this.pending();
        }
      });
      mediator.subscribe(channels.DATACONTEXT_RESUME, function(id) {
        if (_this.id === id || !id && _this.isSession()) {
          return _this.resolve();
        }
      });
      mediator.subscribe(channels.DATACONTEXT_ADD, function(id, node) {
        if (!_.isNumber(id)) {
          node = id;
          id = null;
        }
        if (_this.id === id || !id && _this.isSession()) {
          return _this.add(node);
        }
      });
      mediator.subscribe(channels.DATACONTEXT_REMOVE, function(id, node) {
        if (!_.isNumber(id)) {
          node = id;
          id = null;
        }
        if (_this.id === id || !id && _this.isSession()) {
          return _this.remove(node);
        }
      });
      mediator.subscribe(channels.DATACONTEXT_CLEAR, function(id) {
        if (_this.id === id || !id && _this.isSession()) {
          return _this.clear();
        }
      });
      return this.resolve();
    };

    DataContext.prototype.parse = function(resp) {
      if (resp) {
        if (!this.root) {
          this.nodes = {};
          this.clientNodes = {};
          this.root = parseAttrs(resp.json, null, this._referenceNode);
        } else {
          updateAttrs(this.root, resp.json);
        }
      }
      return resp;
    };

    DataContext.prototype.save = function() {
      this.set('json', this.root.toJSON());
      DataContext.__super__.save.apply(this, arguments);
      mediator.publish(channels.DATACONTEXT_SYNCING, this);
      return this.pending();
    };

    DataContext.prototype.toJSON = function() {
      var attrs;
      attrs = {
        id: this.id,
        name: this.get('name'),
        json: null,
        description: this.get('description'),
        keywords: this.get('keywords'),
        published: this.get('published'),
        archived: this.get('archived'),
        composite: this.get('composite'),
        session: this.get('session')
      };
      if (this.root && !this.root.isEmpty()) {
        attrs.json = this.root.toJSON();
      }
      return attrs;
    };

    DataContext.prototype._nodeSave = function(node) {
      if ((node.get('id') != null) && (node.get('value') != null)) {
        return this.save();
      }
    };

    DataContext.prototype._referenceNode = function(node) {
      var cache;
      if (!(node.id != null)) {
        return;
      }
      if (!(cache = this.nodes[node.id])) {
        cache = this.nodes[node.id] = [];
      }
      if (cache.indexOf(node) === -1) {
        cache.push(node);
      }
      this.clientNodes[node.cid] = node;
      return node.on('change', this._nodeSave);
    };

    DataContext.prototype._deferenceNode = function(node) {
      var cache, idx;
      if ((cache = this.nodes[node.id]) && (idx = cache.indexOf(node)) >= 0) {
        cache.splice(idx, 1);
      }
      delete this.clientNodes[node.cid];
      return node.off('change', this._nodeSave);
    };

    DataContext.prototype.isSession = function() {
      return this.get('session');
    };

    DataContext.prototype.isArchived = function() {
      return this.get('archived');
    };

    DataContext.prototype.getNodes = function(id) {
      return this.nodes[id] || [];
    };

    DataContext.prototype.add = function(node) {
      var branch;
      if (!this.clientNodes[node.cid]) {
        this._referenceNode(node);
        if (this.root.isEmpty()) {
          this.root = node;
        } else if (this.root.isBranch() && !this.root.id) {
          this.root.get('children').push(node);
        } else {
          branch = new DataContextNode({
            type: 'and',
            children: [this.root, node]
          });
          this.root = branch;
        }
        return this.save();
      }
    };

    DataContext.prototype.remove = function(node) {
      var children, idx;
      if (this.clientNodes[node.cid]) {
        if (node === this.root) {
          this.root = new DataContextNode;
        } else if (this.root.isBranch()) {
          children = this.root.get('children');
          if ((idx = children.indexOf(node)) >= 0) {
            children.splice(idx, 1)[0];
          }
          if (children.length === 1) {
            this.root = children[0];
          }
        }
        this._deferenceNode(node);
        return this.save();
      }
    };

    DataContext.prototype.clear = function() {
      var cid, node, _ref;
      _ref = this.clientNodes;
      for (cid in _ref) {
        node = _ref[cid];
        node.off('change', node);
      }
      this.nodes = {};
      this.clientNodes = {};
      if (this.root.id) {
        this.root = new DataContextNode;
      } else {
        this.root.clear();
      }
      return this.save();
    };

    return DataContext;

  })(Backbone.Model);
  DataContexts = (function(_super) {

    __extends(DataContexts, _super);

    function DataContexts() {
      return DataContexts.__super__.constructor.apply(this, arguments);
    }

    DataContexts.prototype.model = DataContext;

    DataContexts.prototype.initialize = function() {
      DataContexts.__super__.initialize.apply(this, arguments);
      return this.on('reset', function(collection) {
        var model, _i, _len, _ref;
        this.resolve();
        _ref = collection.models;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          model = _ref[_i];
          model.trigger('sync');
        }
      });
    };

    DataContexts.prototype.hasSession = function() {
      return !!(this.filter(function(model) {
        return model.get('session');
      }))[0];
    };

    return DataContexts;

  })(Backbone.Collection);
  return {
    DataContextNode: DataContextNode,
    DataContext: DataContext,
    DataContexts: DataContexts
  };
});
