var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
define(["require", "exports", 'react', '../utilities/Async/Async', '../utilities/eventGroup/EventGroup'], function (require, exports, React, Async_1, EventGroup_1) {
    "use strict";
    var BaseComponent = (function (_super) {
        __extends(BaseComponent, _super);
        function BaseComponent(props) {
            _super.call(this, props);
            _makeAllSafe(this, BaseComponent.prototype, [
                'componentWillMount',
                'componentDidMount',
                'shouldComponentUpdate',
                'componentWillUpdate',
                'componentWillReceiveProps',
                'render',
                'componentDidUpdate',
                'componentWillUnmount'
            ]);
        }
        /** If we have disposables, dispose them automatically on unmount. */
        BaseComponent.prototype.componentWillUnmount = function () {
            if (this.__disposables) {
                for (var i = 0, len = this._disposables.length; i < len; i++) {
                    this.__disposables[i].dispose();
                }
                this.__disposables = null;
            }
        };
        Object.defineProperty(BaseComponent.prototype, "className", {
            /** Gets the object's class name. */
            get: function () {
                var funcNameRegex = /function (.{1,})\(/;
                var results = (funcNameRegex).exec((this).constructor.toString());
                return (results && results.length > 1) ? results[1] : '';
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(BaseComponent.prototype, "_disposables", {
            /** Allows subclasses to push things to this._disposables to be auto disposed. */
            get: function () {
                if (!this.__disposables) {
                    this.__disposables = [];
                }
                return this.__disposables;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(BaseComponent.prototype, "_async", {
            /**
             * Gets the async instance associated with the component, created on demand. The async instance gives
             * subclasses a way to execute setTimeout/setInterval async calls safely, where the callbacks
             * will be cleared/ignored automatically after unmounting. The helpers within the async object also
             * preserve the this pointer so that you don't need to "bind" the callbacks.
             */
            get: function () {
                if (!this.__async) {
                    this.__async = new Async_1.Async(this);
                    this._disposables.push(this.__async);
                }
                return this.__async;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(BaseComponent.prototype, "_events", {
            /**
             * Gets the event group instance assocaited with the component, created on demand. The event instance
             * provides on/off methods for listening to DOM (or regular javascript object) events. The event callbacks
             * will be automatically disconnected after unmounting. The helpers within the events object also
             * preserve the this reference so that you don't need to "bind" the callbacks.
             */
            get: function () {
                if (!this.__events) {
                    this.__events = new EventGroup_1.EventGroup(this);
                    this._disposables.push(this.__events);
                }
                return this.__events;
            },
            enumerable: true,
            configurable: true
        });
        return BaseComponent;
    }(React.Component));
    exports.BaseComponent = BaseComponent;
    /**
     * Helper to override a given method with a wrapper method that can try/catch the original, but also
     * ensures that the BaseComponent's methods are called before the subclass's. This ensures that
     * componentWillUnmount in the base is called and that things in the _disposables array are disposed.
     **/
    function _makeAllSafe(obj, prototype, methodNames) {
        for (var i = 0, len = methodNames.length; i < len; i++) {
            _makeSafe(obj, prototype, methodNames[i]);
        }
    }
    function _makeSafe(obj, prototype, methodName) {
        var classMethod = obj[methodName];
        var prototypeMethod = prototype[methodName];
        if (classMethod || prototypeMethod) {
            obj[methodName] = function () {
                var retVal;
                try {
                    if (prototypeMethod) {
                        retVal = prototypeMethod.apply(this, arguments);
                    }
                    if (classMethod) {
                        retVal = classMethod.apply(this, arguments);
                    }
                }
                catch (e) {
                    var errorMessage = "Exception in " + obj.className + "." + methodName + "(): " + (typeof e === 'string' ? e : e.stack);
                    if (BaseComponent.onError) {
                        BaseComponent.onError(errorMessage, e);
                    }
                }
                return retVal;
            };
        }
    }
    BaseComponent.onError = function (errorMessage) { return console.error(errorMessage); };
});
