'use strict';

var angular = require('angular');
var apply = require('apply-fn');

var ngClassified = angular.module('ngClassified', [])
.config(['$injector', function ($injector) {
	if ($injector.has('$classify')) {
		$injector.$classify = $injector.get('$classify');
	}
	$injector = injectorInjectInvokers($injector);
}])
.run(['$injector', function ($injector) {
	if ($injector.has('$classify')) {
		$injector.$classify = $injector.get('$classify');
	}
	$injector = injectorInjectInvokers($injector);
}]);

/**
 * Invoker injects invokee,
 * Injector invokes injectee,
 * Morrow instantiates.
 */

function injectorInjectInvokers(injector) {
	var invoke_ = injector.invoke;

	injector.instantiate = instantiate;
	injector.invoke = invoke;

	return injector;

	// pasted method that picks up patched 'invoke'
	function instantiate(Type, locals, serviceName) {
		var instance = Object.create((Array.isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
		var returnedValue = invoke(Type, instance, locals, serviceName);

		return (returnedValue !== null && typeof returnedValue === 'object') || (typeof returnedValue === 'function') ? returnedValue : instance;
	}

	function invoke() {
		var argsLength = arguments.length;
		var args = new Array(argsLength);
		for (var i = 0; i < argsLength; i++) {
			args[i] = arguments[i];
		}

		var nativeClassRegex = /^class/;
		var classRegex = /class/i;

		var invokeResult;
		var fn = args[0];
		var skipClassify = !angular.isFunction(fn) || fn.hasOwnProperty('$$classified');

		if (!skipClassify) {
			if (fn.$classify || nativeClassRegex.test(fn.toString())) {
				classifyInvokee(args);
			} else if (this.$classify || (this.$classify !== false && classRegex.test(fn.toString()))) {
				try {
					invokeResult = apply(injector, invoke_, args);
					fn.$$classified = false;
				} catch (e) {
					if (e instanceof TypeError && classRegex.test(e.message)) {
						classifyInvokee(args);
					} else {
						throw e;
					}
				}
			}	
		}

		return invokeResult || apply(injector, invoke_, args);
	}

	function classifyInvokee(args) {
		var fn_ = args[0]; 

		var fn = args[0] = function () {
			var fnArgsLength = arguments.length;
			var fnArgs = new Array(fnArgsLength);
			for (var i = 0; i < fnArgsLength; i++) {
				fnArgs[i] = arguments[i];
			}

			return new (apply(fn_, Function.prototype.bind, [null].concat(fnArgs)));
		};

		injector.annotate(fn_);
		fn.$inject = fn_.$inject;
		fn.$$classified = true;
	}
}

module.exports = ngClassified.name;
@@ -2259,24 +2259,28 @@
 ments.length
+ + 1
 ;%0D%0A%09%09%09var fn
@@ -2324,25 +2324,25 @@
 or (var i = 
-0
+1
 ; i %3C fnArgs
@@ -2380,24 +2380,28 @@
  arguments%5Bi
+ - 1
 %5D;%0D%0A%09%09%09%7D%0D%0A%0D%0A
@@ -2400,16 +2400,48 @@
 %09%09%09%7D%0D%0A%0D%0A
+%09%09%09// fnArgs%5B0%5D === undefined %0D%0A
 %09%09%09retur
@@ -2487,30 +2487,16 @@
 nd, 
-%5Bnull%5D.concat(
 fnArgs))
 );%0D%0A
@@ -2491,17 +2491,16 @@
 fnArgs))
-)
 ;%0D%0A%09%09%7D;%0D

optimize bound arguments