Show:
/**
 * @module proact-properties
 */

/**
 * <p>
 *  Constructs a `ProAct.ObjectProperty`.
 *  The properties are simple {{#crossLink "ProAct.Actor"}}{{/crossLink}}s with state. The object property
 *  has a state of a JavaScript object value.
 * </p>
 * <p>
 *  The value of `ProAct.ObjectProperty` is object, turned to reactive ProAct.js object recursively.
 * </p>
 * <p>
 *  On changing the object value to another object the listeners for fields with the same name in the objects,
 *  are moved from the old value's fields to the new value's fields.
 * </p>
 * <p>
 *  If set to null or undefined, the property is re-defined, using {{#crossLink "ProAct.Property/reProb:method"}}{{/crossLink}}
 * </p>
 * <p>
 *  `ProAct.ObjectProperty` is lazy - its object is made reactive on the first read of the property.
 *  Its state is set to {{#crossLink "ProAct.States/ready:property"}}{{/crossLink}} on the first read too.
 * </p>
 * <p>
 *  `ProAct.ObjectProperty` is part of the proact-properties module of ProAct.js.
 * </p>
 *
 * @class ProAct.ObjectProperty
 * @extends ProAct.Property
 * @constructor
 * @param {String} queueName
 *      The name of the queue all the updates should be pushed to.
 *      <p>
 *        If this parameter is null/undefined the default queue of
 *        {{#crossLink "ProAct/flow:property"}}{{/crossLink}} is used.
 *      </p>
 *      <p>
 *        If this parameter is not a string it is used as the
 *        <i>proObject</i>.
 *      </p>
 * @param {Object} proObject
 *      A plain JavaScript object, holding a field, this property will represent.
 * @param {String} property
 *      The name of the field of the object, this property should represent.
 */
function ObjectProperty (queueName, proObject, property) {
  if (queueName && !P.U.isString(queueName)) {
    property = proObject;
    proObject = queueName;
    queueName = null;
  }
  var self = this, getter;

  getter = function () {
    self.addCaller();
    if (!self.val.__pro__) {
      P.prob(self.val);
    }

    var get = P.P.defaultGetter(self),
        set = function (newVal) {
          if (self.val == newVal) {
            return;
          }

          self.oldVal = self.val;
          self.val = newVal;

          if (self.val === null || self.val === undefined) {
            P.P.reProb(self).update();
            return self;
          }

          if (self.oldVal) {
            if (!self.val.__pro__) {
              P.prob(self.val);
            }

            if (P.U.isArray(this)) {
              ProAct.ActorUtil.update.call(self);
              return;
            }

            var oldProps = self.oldVal.__pro__.properties,
                newProps = self.val.__pro__.properties,
                oldPropName, oldProp, newProp, oldListeners, newListeners,
                i, j, oldListenersLength, newListenersLength,
                toAdd, toRemove = [], toRemoveLength;

            for (oldPropName in oldProps) {
              if (oldProps.hasOwnProperty(oldPropName)) {
                newProp = newProps[oldPropName];
                if (!newProp) {
                  continue;
                }
                newListeners = newProp.listeners.change;

                oldProp = oldProps[oldPropName];
                oldListeners = oldProp.listeners.change;
                oldListenersLength = oldListeners.length;

                for (i = 0; i < oldListenersLength; i++) {
                  toAdd = true;
                  for (j = 0; j < newListenersLength; j++) {
                    if (oldListeners[i] == newListeners[j]) {
                      toAdd = false;
                    }
                  }
                  if (toAdd) {
                    newProp.on(oldListeners[i]);
                    toRemove.push(i);
                  }
                }

                toRemoveLength = toRemove.length;
                for (i = 0; i < toRemoveLength; i++) {
                  oldListeners.splice[toRemove[i], 1];
                }
                toRemove = [];
              }
            }
          }

          ActorUtil.update.call(self);
        };

    P.P.defineProp(self.proObject, self.property, get, set);

    self.state = P.States.ready;
    return self.val;
  };

  P.P.call(this, queueName, proObject, property, getter, function () {});
}
ProAct.ObjectProperty = P.OP = ObjectProperty;

ProAct.ObjectProperty.prototype = P.U.ex(Object.create(P.P.prototype), {

  /**
   * Reference to the constructor of this object.
   *
   * @property constructor
   * @type ProAct.ObjectProperty
   * @final
   * @for ProAct.ObjectProperty
   */
  constructor: ProAct.ObjectProperty,

  /**
   * Retrieves the {{#crossLink "ProAct.Property.Types"}}{{/crossLink}} value of <i>this</i> property.
   * <p>
   *  For instances of the `ProAct.ObjectProperty` class, it is
   *  {{#crossLink "ProAct.Property.Types/object:property"}}{{/crossLink}}.
   * </p>
   *
   * @for ProAct.ObjectProperty
   * @instance
   * @method type
   * @return {Number}
   *      The right type of the property.
   */
  type: function () {
    return P.P.Types.object;
  },

  /**
   * Called automatically after initialization of this property.
   * <p>
   *  For `ProAct.ObjectProperty` it does nothing -
   *  the real initialization is lazy and is performed on the first read of <i>this</i>.
   * </p>
   *
   * @for ProAct.ObjectProperty
   * @protected
   * @instance
   * @method afterInit
   */
  afterInit: function () {}
});