PD.BCL.Aspect = Class.extend({

    //===================================
    // FIELDS
    //===================================

    _fn: null,
    _target: null,
    _targetFnName: null,
    _info: null,
    _context: null,
    _onEnterFn: null,
    _onExitFn: null,
    
    //===================================
    // CONSTRUCTOR
    //===================================

    init: function (aspectInfo) {

        this._onEnterFn = aspectInfo.enterFn;
        this._onExitFn = aspectInfo.exitFn;
        this._target = aspectInfo.target;
        this._targetFnName = aspectInfo.targetFnName;
        this._fn = aspectInfo.fn;

        if (aspectInfo.fn == null 
            && aspectInfo.target != null
            && aspectInfo.targetFnName != null) {
            this._fn = aspectInfo.target[aspectInfo.targetFnName];
        }

        this._context = this.__getContext();

    },

    //===================================
    // PUBLIC METHODS
    //===================================

    get_context: function () {
        return this._context;
    },

    get_target: function () {
        return this._target;
    },

    get_targetFnName: function () {
        return this._targetFnName;
    },

    get_fn: function () {
        return this._fn;
    },

    get_onEnterFn: function () {
        return this._onEnterFn;
    },

    get_onExitFn: function () {
        return this._onExitFn;
    },

    //===================================
    // PUBLIC METHODS
    //===================================
    
    getAspectFn: function () {

        var result = null,
            fn = null;

        if (this.get_fn()) {
            fn = this.get_fn();
        }
        else if (this.get_target() && this.get_targetFnName()) {
            fn = this.get_target()[this.get_targetFnName()];
        }

        if (fn) {
            result = this.__wrapFn(fn);
        }
        return result;

    },

    //===================================
    // PRIVATE METHODS
    //===================================

    __setAspectWrapFn: function (fn, arguments) {

        if (fn) {
            fn.apply(this, arguments);
        }

    },

    __wrapFn: function (fn) {

        var that = this;
        
        return function () {
            
            that.__setAspectWrapFn(that.get_onEnterFn(), arguments);
            fn.apply(that.get_context(), arguments);
            that.__setAspectWrapFn(that.get_onExitFn(), arguments);

        }

    },

    __getContext: function () {
    
        var result = null;

        switch (true) {
            case this._context != null:
                result = this._context;
                break;
            case this._target != null:
                result = this._target;
                break;
            default:
                result = this;
                break;
        }

        return result;

    }

});

//===================================
// STATIC METHODS
//===================================

PD.BCL.Aspect.create = function (aspectInfo) {

    var aspect = new PD.BCL.Aspect(aspectInfo);

    if (aspectInfo.target && aspectInfo.targetFnName) {
        aspectInfo.target[aspectInfo.targetFnName] = aspect.getAspectFn();
    }
    else {
        aspectInfo.fn = aspect.getAspectFn();
    }

};