import { config } from "rsvp/config";
import { EventTarget } from "rsvp/events";

function objectOrFunction(x) {
  return isFunction(x) || (typeof x === "object" && x !== null);
}

function isFunction(x){
  return typeof x === "function";
}

var Promise = function(resolver) {
  var promise = this,
  resolved = false;

  if (typeof resolver !== 'function') {
    throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
  }

  if (!(promise instanceof Promise)) {
    return new Promise(resolver);
  }

  var resolvePromise = function(value) {
    if (resolved) { return; }
    resolved = true;
    resolve(promise, value);
  };

  var rejectPromise = function(value) {
    if (resolved) { return; }
    resolved = true;
    reject(promise, value);
  };

  this.on('promise:resolved', function(event) {
    this.trigger('success', { detail: event.detail });
  }, this);

  this.on('promise:failed', function(event) {
    this.trigger('error', { detail: event.detail });
  }, this);

  resolver(resolvePromise, rejectPromise);
};

var invokeCallback = function(type, promise, callback, event) {
  var hasCallback = isFunction(callback),
      value, error, succeeded, failed;

  if (hasCallback) {
    try {
      value = callback(event.detail);
      succeeded = true;
    } catch(e) {
      failed = true;
      error = e;
    }
  } else {
    value = event.detail;
    succeeded = true;
  }

  if (objectOrFunction(value) && isFunction(value.then)) {
    value.then(function(value) {
      resolve(promise, value);
    }, function(error) {
      reject(promise, error);
    });
  } else if (hasCallback && succeeded) {
    resolve(promise, value);
  } else if (failed) {
    reject(promise, error);
  } else if (type === 'resolve') {
    resolve(promise, value);
  } else if (type === 'reject') {
    reject(promise, value);
  }
};

Promise.prototype = {
  constructor: Promise,

  then: function(done, fail) {
    var thenPromise = new Promise(function() {});

    if (this.isFulfilled) {
      config.async(function() {
        invokeCallback('resolve', thenPromise, done, { detail: this.fulfillmentValue });
      }, this);
    }

    if (this.isRejected) {
      config.async(function() {
        invokeCallback('reject', thenPromise, fail, { detail: this.rejectedReason });
      }, this);
    }

    this.on('promise:resolved', function(event) {
      invokeCallback('resolve', thenPromise, done, event);
    });

    this.on('promise:failed', function(event) {
      invokeCallback('reject', thenPromise, fail, event);
    });

    return thenPromise;
  }
};

EventTarget.mixin(Promise.prototype);

function resolve(promise, value) {

  if (promise === value) {
    fulfill(promise, value);
  } else if (objectOrFunction(value) && isFunction(value.then)) {
    value.then(function(val) {
      if (value !== val) {
        resolve(promise, val);
      } else {
        fulfill(promise, val);
      }
    }, function(val) {
      reject(promise, val);
    });
  } else {
    fulfill(promise, value);
  }
}

function fulfill(promise, value) {
  config.async(function() {
    promise.trigger('promise:resolved', { detail: value });
    promise.isFulfilled = true;
    promise.fulfillmentValue = value;
  });
}

function reject(promise, value) {
  config.async(function() {
    promise.trigger('promise:failed', { detail: value });
    promise.isRejected = true;
    promise.rejectedReason = value;
  });
}

export { Promise };

@@ -1447,186 +1447,52 @@
 if (
-objectOrFunction(value) && isFunction(value.then)) %7B%0A    value.then(function(value) %7B%0A      resolve(promise, value);%0A    %7D, function(error) %7B%0A      reject(promise, error);%0A    %7D)
+handleThenable(promise, value)) %7B%0A    return
 ;%0A  
@@ -2552,17 +2552,16 @@
 alue) %7B%0A
-%0A
   if (pr
@@ -2625,35 +2625,286 @@
 if (
-objectOrFunction(value) && 
+!handleThenable(promise, value)) %7B%0A    fulfill(promise, value);%0A  %7D%0A%7D%0A%0Afunction handleThenable(promise, value) %7B%0A  var then = null;%0A%0A  if (objectOrFunction(value)) %7B%0A    try %7B%0A      then = value.then;%0A    %7D catch(e) %7B%0A      reject(promise, e);%0A      return true;%0A    %7D%0A%0A    if (
 isFu
@@ -2910,22 +2910,16 @@
 unction(
-value.
 then)) %7B
@@ -2927,19 +2927,41 @@
     
-value.then(
+  try %7B%0A        then.call(value, 
 func
@@ -2974,24 +2974,28 @@
 al) %7B%0A      
+   
+ 
 if (value !=
@@ -2999,24 +2999,28 @@
  !== val) %7B%0A
+    
         reso
@@ -3044,16 +3044,20 @@
 ;%0A      
+ 
+   
 %7D else %7B
@@ -3061,24 +3061,28 @@
 e %7B%0A        
+   
+ 
 fulfill(prom
@@ -3098,22 +3098,30 @@
 ;%0A      
-%7D%0A
+    %7D%0A  
     
+  
 %7D, funct
@@ -3127,24 +3127,28 @@
 tion(val) %7B%0A
+    
       reject
@@ -3159,24 +3159,28 @@
 mise, val);%0A
+    
     %7D);%0A  %7D 
@@ -3181,50 +3181,108 @@
 ;%0A  
-%7D else %7B%0A    fulfill(promise, val
+    %7D catch (e) %7B%0A        reject(promise, e);%0A      %7D%0A      return tr
 ue
-)
 ;%0A
+  
   %7D%0A
+  %7D%0A%0A  return false;%0A
 %7D%0A%0Af

Make rsvp promises/A+ spec-1.1 compliant