var Aplus = function() {

	var State = {
		PENDING: 0,
		FULFILLED: 1,
		REJECTED: 2
	};

	var Aplus = {
		state: State.PENDING,
		changeState: function( state, value ) {

			// catch changing to same state (perhaps trying to change the value)
			if ( this.state == state ) {
				return new Error("can't transition to same state: " + state);
			}

			// trying to change out of fulfilled or rejected
			if ( this.state == State.FULFILLED ||
					this.state == State.REJECTED ) {
				return new Error("can't transition from current state: " + state);
			}

			// if second argument isn't given at all (passing undefined allowed)
			if ( state == State.FULFILLED &&
					arguments.length < 2 ) {
				return new Error("transition to fulfilled must have a non null value");
			}

			// if a null reason is passed in
			if ( state == State.REJECTED &&
					arguments.length < 2 ) {
				return new Error("transition to rejected must have a non null reason");
			}

			//change state
			this.state = state;
			this.value = value;
			this.resolve();
			return this.state;
		},
		fulfill: function( value ) {
			this.changeState( State.FULFILLED, value );
		},
		reject: function( reason ) {
			this.changeState( State.REJECTED, reason );
		},
		then: function( onFulfilled, onRejected ) {

			// initialize array
			this.cache = this.cache || [];

			var promise = Object.create(Aplus);

			var that = this;

			this.async( function() {
				that.cache.push({
					fulfill: onFulfilled,
					reject: onRejected,
					promise: promise
				});
				that.resolve();
			});

			return promise;
		},
		resolve: function() {
			// check if pending
			if ( this.state == State.PENDING ) {
				return false;
			}

			// for each 'then'
			while ( this.cache && this.cache.length ) {
				var obj = this.cache.shift();

				var fn = this.state == State.FULFILLED ?
					obj.fulfill :
					obj.reject;


				if ( typeof fn != 'function' ) {

					obj.promise.changeState( this.state, this.value );

				} else {

					// fulfill promise with value or reject with error
					try {

						var value = fn( this.value );

						// deal with promise returned
						if ( value && typeof value.then == 'function' ) {
							value.then( function( value ) {
								obj.promise.changeState( State.FULFILLED, value );
							}, function( error ) {
								obj.promise.changeState( State.REJECTED, error );
							});
						// deal with other value returned
						} else {
							obj.promise.changeState( State.FULFILLED, value );
						}
					// deal with error thrown
					} catch (error) {
						obj.promise.changeState( State.REJECTED, error );
					}
				}
			}
		},
		async: function(fn) {
			setTimeout(fn, 5);
		}
	};

	return Object.create(Aplus);

};