  /**
   *  Represents the result of an asynchronous operation.
   *  The last value before the OnCompleted notification, or the error received through OnError, is sent to all subscribed observers.
   */   
  var AsyncSubject = Rx.AsyncSubject = (function (__super__) {

    function subscribe(observer) {
      checkDisposed.call(this);
      
      if (!this.isStopped) {
        this.observers.push(observer);
        return new InnerSubscription(this, observer);
      }

      var ex = this.exception,
        hv = this.hasValue,
        v = this.value;

      if (ex) {
        observer.onError(ex);
      } else if (hv) {
        observer.onNext(v);
        observer.onCompleted();
      } else {
        observer.onCompleted();
      }

      return disposableEmpty;
    }

    inherits(AsyncSubject, __super__);

    /**
     * Creates a subject that can only receive one value and that value is cached for all future observations.
     * @constructor
     */ 
    function AsyncSubject() {
      __super__.call(this, subscribe);

      this.isDisposed = false;
      this.isStopped = false;
      this.value = null;
      this.hasValue = false;
      this.observers = [];
      this.exception = null;
    }

    addProperties(AsyncSubject.prototype, Observer, {
      /**
       * Indicates whether the subject has observers subscribed to it.
       * @returns {Boolean} Indicates whether the subject has observers subscribed to it.
       */         
      hasObservers: function () {
        checkDisposed.call(this);
        return this.observers.length > 0;
      },
      /**
       * Notifies all subscribed observers about the end of the sequence, also causing the last received value to be sent out (if any).
       */ 
      onCompleted: function () {
        var o, i, len;
        checkDisposed.call(this);
        if (!this.isStopped) {
          this.isStopped = true;
          var os = this.observers.slice(0),
            v = this.value,
            hv = this.hasValue;

          if (hv) {
            for (i = 0, len = os.length; i < len; i++) {
              o = os[i];
              o.onNext(v);
              o.onCompleted();
            }
          } else {
            for (i = 0, len = os.length; i < len; i++) {
              os[i].onCompleted();
            }
          }

          this.observers = [];
        }
      },
      /**
       * Notifies all subscribed observers about the error.
       * @param {Mixed} error The Error to send to all observers.
       */ 
      onError: function (error) {
        checkDisposed.call(this);
        if (!this.isStopped) {
          var os = this.observers.slice(0);
          this.isStopped = true;
          this.exception = error;

          for (var i = 0, len = os.length; i < len; i++) {
            os[i].onError(error);
          }

          this.observers = [];
        }
      },
      /**
       * Sends a value to the subject. The last value received before successful termination will be sent to all subscribed and future observers.
       * @param {Mixed} value The value to store in the subject.
       */             
      onNext: function (value) {
        checkDisposed.call(this);
        if (this.isStopped) { return; }
        this.value = value;
        this.hasValue = true;
      },
      /**
       * Unsubscribe all observers and release resources.
       */
      dispose: function () {
        this.isDisposed = true;
        this.observers = null;
        this.exception = null;
        this.value = null;
      }
    });

    return AsyncSubject;
  }(Observable));
