/**
 * @class Ext.app.Controller
 * @constructor
 *
 * Controllers are the glue that binds an application together. All they really do is listen for events (usually from
 * views) and take some action. Here's how we might create a Controller to manage Users:
 *
 *     Ext.define('MyApp.controller.Users', {
 *         extend: 'Ext.app.Controller',
 *
 *         init: function() {
 *             console.log('Initialized Users! This happens before the Application launch function is called');
 *         }
 *     });
 *
 * The init function is a special method that is called when your application boots. It is called before the
 * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point to run any code before
 * your Viewport is created.
 *
 * The init function is a great place to set up how your controller interacts with the view, and is usually used in
 * conjunction with another Controller function - {@link Ext.app.Controller#control control}. The control function
 * makes it easy to listen to events on your view classes and take some action with a handler function. Let's update
 * our Users controller to tell us when the panel is rendered:
 *
 *     Ext.define('MyApp.controller.Users', {
 *         extend: 'Ext.app.Controller',
 *
 *         init: function() {
 *             this.control({
 *                 'viewport > panel': {
 *                     render: this.onPanelRendered
 *                 }
 *             });
 *         },
 *
 *         onPanelRendered: function() {
 *             console.log('The panel was rendered');
 *         }
 *     });
 *
 * We've updated the init function to use this.control to set up listeners on views in our application. The control
 * function uses the new ComponentQuery engine to quickly and easily get references to components on the page. If you
 * are not familiar with ComponentQuery yet, be sure to check out THIS GUIDE for a full explanation. In brief though,
 * it allows us to pass a CSS-like selector that will find every matching component on the page.
 *
 * In our init function above we supplied 'viewport > panel', which translates to "find me every Panel that is a direct
 * child of a Viewport". We then supplied an object that maps event names (just 'render' in this case) to handler
 * functions. The overall effect is that whenever any component that matches our selector fires a 'render' event, our
 * onPanelRendered function is called.
 *
 * <u>Using refs</u>
 *
 * One of the most useful parts of Controllers is the new ref system. These use the new {@link Ext.ComponentQuery} to
 * make it really easy to get references to Views on your page. Let's look at an example of this now:
 *
 * Ext.define('MyApp.controller.Users', {
     extend: 'Ext.app.Controller',

     refs: [
         {
             ref: 'list',
             selector: 'grid'
         }
     ],

     init: function() {
         this.control({
             'button': {
                 click: this.refreshGrid
             }
         });
     },

     refreshGrid: function() {
         this.getList().store.load();
     }
 });
 *
 * This example assumes the existence of a {@link Ext.grid.Panel Grid} on the page, which contains a single button to
 * refresh the Grid when clicked. In our refs array, we set up a reference to the grid. There are two parts to this -
 * the 'selector', which is a {@link Ext.ComponentQuery ComponentQuery} selector which finds any grid on the page and
 * assigns it to the reference 'list'.
 *
 * By giving the reference a name, we get a number of things for free. The first is the getList function that we use in
 * the refreshGrid method above. This is generated automatically by the Controller based on the name of our ref, which
 * was capitalized and prepended with get to go from 'list' to 'getList'.
 *
 * The way this works is that the first time getList is called by your code, the ComponentQuery selector is run and the
 * first component that matches the selector ('grid' in this case) will be returned. All future calls to getList will
 * use a cached reference to that grid. Usually it is advised to use a specific ComponentQuery selector that will only
 * match a single View in your application (in the case above our selector will match any grid on the page).
 *
 * Bringing it all together, our init function is called when the application boots, at which time we call this.control
 * to listen to any click on a {@link Ext.button.Button button} and call our refreshGrid function (again, this will
 * match any button on the page so we advise a more specific selector than just 'button', but have left it this way for
 * simplicity). When the button is clicked we use out getList function to refresh the grid.
 *
 * You can create any number of refs and control any number of components this way, simply adding more functions to
 * your Controller as you go. For an example of real-world usage of Controllers see the Feed Viewer example in the
 * examples/app/feed-viewer folder in the SDK download.
 *
 * <u>Generated getter methods</u>
 *
 * Refs aren't the only thing that generate convenient getter methods. Controllers often have to deal with Models and
 * Stores so the framework offers a couple of easy ways to get access to those too. Let's look at another example:
 *
 * Ext.define('MyApp.controller.Users', {
     extend: 'Ext.app.Controller',

     models: ['User'],
     stores: ['AllUsers', 'AdminUsers'],

     init: function() {
         var User = this.getUserModel(),
             allUsers = this.getAllUsersStore();

         var ed = new User({name: 'Ed'});
         allUsers.add(ed);
     }
 });
 *
 * By specifying Models and Stores that the Controller cares about, it again dynamically loads them from the appropriate
 * locations (app/model/User.js, app/store/AllUsers.js and app/store/AdminUsers.js in this case) and creates getter
 * functions for them all. The example above will create a new User model instance and add it to the AllUsers Store.
 * Of course, you could do anything in this function but in this case we just did something simple to demonstrate the
 * functionality.
 *
 * <u>Further Reading</u>
 *
 * For more information about writing Ext JS 4 applications, please see the <a href="../guide/application_architecture">
 * application architecture guide</a>. Also see the {@link Ext.app.Application} documentation.
 *
 * @markdown
 * @docauthor Ed Spencer
 */
 
Ext.define('Ext.app.Controller', {
    /**
     * @cfg {Object} id The id of this controller. You can use this id when dispatching.
     */


    mixins
: {
        observable
: 'Ext.util.Observable'
   
},

    onClassExtended
: function(cls, data) {
       
var className = Ext.getClassName(cls),
            match
= className.match(/^(.*)\.controller\./);

       
if (match !== null) {
           
var namespace = Ext.Loader.getPrefix(className) || match[1],
                onBeforeClassCreated
= data.onBeforeClassCreated,
                requires
= [],
                modules
= ['model', 'view', 'store'],
                prefix
;

            data
.onBeforeClassCreated = function(cls, data) {
               
var i, ln, module,
                    items
, j, subLn, item;

               
for (i = 0,ln = modules.length; i < ln; i++) {
                   
module = modules[i];

                    items
= Ext.Array.from(data[module + 's']);

                   
for (j = 0,subLn = items.length; j < subLn; j++) {
                        item
= items[j];

                        prefix
= Ext.Loader.getPrefix(item);

                       
if (prefix === '' || prefix === item) {
                            requires
.push(namespace + '.' + module + '.' + item);
                       
}
                       
else {
                            requires
.push(item);
                       
}
                   
}
               
}

               
Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
           
};
       
}
   
},

    constructor
: function(config) {
       
this.mixins.observable.constructor.call(this, config);

       
Ext.apply(this, config || {});

       
this.createGetters('model', this.models);
       
this.createGetters('store', this.stores);
       
this.createGetters('view', this.views);

       
if (this.refs) {
           
this.ref(this.refs);
       
}
   
},

   
// Template method
    init
: function(application) {},
   
// Template method
    onLaunch
: function(application) {},

    createGetters
: function(type, refs) {
        type
= Ext.String.capitalize(type);
       
Ext.Array.each(refs, function(ref) {
           
var fn = 'get',
                parts
= ref.split('.');

           
// Handle namespaced class names. E.g. feed.Add becomes getFeedAddView etc.
           
Ext.Array.each(parts, function(part) {
                fn
+= Ext.String.capitalize(part);
           
});
            fn
+= type;

           
if (!this[fn]) {
               
this[fn] = Ext.Function.pass(this['get' + type], [ref], this);
           
}
           
// Execute it right away
           
this[fn](ref);
       
},
       
this);
   
},

   
ref: function(refs) {
       
var me = this;
        refs
= Ext.Array.from(refs);
       
Ext.Array.each(refs, function(info) {
           
var ref = info.ref,
                fn
= 'get' + Ext.String.capitalize(ref);
           
if (!me[fn]) {
                me
[fn] = Ext.Function.pass(me.getRef, [ref, info], me);
           
}
       
});
   
},

    getRef
: function(ref, info, config) {
       
this.refCache = this.refCache || {};
        info
= info || {};
        config
= config || {};

       
Ext.apply(info, config);

       
if (info.forceCreate) {
           
return Ext.ComponentManager.create(info, 'component');
       
}

       
var me = this,
            selector
= info.selector,
            cached
= me.refCache[ref];

       
if (!cached) {
            me
.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
           
if (!cached && info.autoCreate) {
                me
.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
           
}
           
if (cached) {
                cached
.on('beforedestroy', function() {
                    me
.refCache[ref] = null;
               
});
           
}
       
}

       
return cached;
   
},

    control
: function(selectors, listeners) {
       
this.application.control(selectors, listeners, this);
   
},

    getController
: function(name) {
       
return this.application.getController(name);
   
},

    getStore
: function(name) {
       
return this.application.getStore(name);
   
},

    getModel
: function(model) {
       
return this.application.getModel(model);
   
},

    getView
: function(view) {
       
return this.application.getView(view);
   
}
});