/**
 * Created by Aaron on 7/3/2015.
 */

import Promise from 'bluebird';

let yieldHandlers = [];

export function isThenable( obj ) {
    return obj && typeof obj.then === 'function';
}

export let isPromise = isThenable;

export function isGenerator( obj ) {
    return 'function' === typeof obj.next && 'function' === typeof obj.throw;
}

export function isGeneratorFunction( obj ) {
    if( !obj.constructor ) {
        return false;

    } else if( 'GeneratorFunction' === obj.constructor.name ||
               'GeneratorFunction' === obj.constructor.displayName ) {
        return true;

    } else {
        return isGenerator( obj.constructor.prototype );
    }
}

function isNativeObject( obj ) {
    if( !obj.constructor ) {
        return true;

    } else if( 'Object' === obj.constructor.name ||
               'Object' === obj.constructor.displayName ) {
        return true;

    } else {
        let p = obj.constructor.prototype;

        return p && !!(!p.constructor || 'Object' === p.constructor.name);
    }
}

function objectToPromise( obj ) {
    let keys = Object.keys( obj );
    let length = keys.length;

    let results = new obj.constructor();
    let promises = new Array( length );

    let i = -1;

    while( ++i < length ) {
        let key = keys[i];

        let value = obj[key];

        let promise = toPromise.call( this, value );

        if( isThenable( promise ) ) {
            results[key] = void 0;

            promises[i] = promise.then( res => results[key] = res );

        } else {
            results[key] = value;
        }
    }

    return Promise.all( promises ).return( results );
}

function resolveGenerator( gen ) {
    return new Promise( ( resolve, reject ) => {
        function next( ret ) {
            if( ret.done ) {
                return resolve( ret.value );

            } else {
                let value = toPromise.call( this, ret.value );

                if( isThenable( value ) ) {
                    return value.then( onFulfilled, onRejected );

                } else {
                    return onRejected( new TypeError( `You may only yield a function, promise, generator, array, or object, but the following object was passed: "${ret.value}"` ) );
                }
            }
        }

        function onFulfilled( res ) {
            try {
                next( gen.next( res ) );

            } catch( e ) {
                return reject( e );
            }
        }

        function onRejected( err ) {
            try {
                next( gen.throw( err ) );

            } catch( e ) {
                return reject( e );
            }
        }

        onFulfilled();
    } );
}

function arrayToPromise( value ) {
    let length = value.length;

    let results = new Array( length );
    let i = -1;

    while( ++i < length ) {
        results[i] = toPromise.call( this, value[i] );
    }

    return Promise.all( results );
}

function toPromise( value ) {
    if( isThenable( value ) ) {
        return value;

    } else if( Array.isArray( value ) ) {
        return arrayToPromise.call( this, value );

    } else if( value && typeof value === 'object' ) {
        if( isGenerator( value ) ) {
            return resolveGenerator.call( this, value );

        } else if( isNativeObject( value ) ) {
            return objectToPromise.call( this, value );

        } else {
            let i = -1;
            let length = yieldHandlers.length;

            while( ++i < length ) {
                let handler = yieldHandlers[i];

                let res = handler.call( this, value );

                if( isThenable( res ) ) {
                    return res;
                }
            }
        }

    } else if( typeof value === 'function' ) {
        if( isGeneratorFunction( value ) ) {
            return Promise.coroutine( value ).call( this );

        } else {
            //Thunks
            return new Promise( ( resolve, reject ) => {
                try {
                    value.call( this, ( err, ...res ) => {
                        if( err ) {
                            reject( err );

                        } else if( res.length > 1 ) {
                            resolve( res );

                        } else {
                            resolve( res[0] );
                        }
                    } );

                } catch( err ) {
                    reject( err );
                }
            } );
        }

    } else {
        let i = -1;
        let length = yieldHandlers.length;

        while( ++i < length ) {
            let handler = yieldHandlers[i];

            let res = handler.call( this, value );

            if( isThenable( res ) ) {
                return res;
            }
        }
    }

    return value;
}

export function addYieldHandler( handler ) {
    if( typeof handler !== 'function' ) {
        throw new TypeError( 'yield handler is not a function' );

    } else {
        yieldHandlers.push( handler );
    }
}

let addedYieldHandler = false;

if( !addedYieldHandler ) {
    Promise.coroutine.addYieldHandler( toPromise );

    addedYieldHandler = true;
}

export default {
    addYieldHandler,
    isThenable,
    isPromise,
    isGenerator,
    isGeneratorFunction
};

@@ -672,367 +672,8 @@
 %0A%7D%0A%0A
-function isNativeObject( obj ) %7B%0A    if( !obj.constructor ) %7B%0A        return true;%0A%0A    %7D else if( 'Object' === obj.constructor.name %7C%7C%0A               'Object' === obj.constructor.displayName ) %7B%0A        return true;%0A%0A    %7D else %7B%0A        let p = obj.constructor.prototype;%0A%0A        return p && !!(!p.constructor %7C%7C 'Object' === p.constructor.name);%0A    %7D%0A%7D%0A%0A
 func
@@ -2765,16 +2765,18 @@
 lse if( 
+!!
 value &&
@@ -2924,31 +2924,58 @@
 if( 
-isNative
 Object
-(
+ ===
  value
- )
+.constructor %7C%7C !value.constructor
  ) %7B

Simplify and inline isNativeObject