(function($)
{
    //front controller - we only have a single controller for the wntire application
    function intelesense_data()
    {
        // I am the collection of models. I can contain either cached singleton
        // instances or class definitions (to be instantiated at request).
        this.models = {
            cache: {},
            classes: {}
        };

        // I am the collection of views. I can contain either cached singleton
        // instances of class definitions (to be instantiated at request).
        this.views = {
            cache: {},
            classes: {}
        };

        // Keep track of state
        this.running = false;
        this.current_view = null;

        //and don't forget settings
        this.settings = null;
    };

    // I add a given class to the given cache or class repository.
    intelesense_data.prototype.add_class = function( target, value )
    {
        // Get the constructor of our value class.
        var constructor = value.constructor;

        // Check to see if this constructor is the Function object. If it is,
        // then this is just a class, not an instance.
        if (constructor == Function)
        {

            // This value object is a class, not an instance. Therefore, we need
            // to get the name of the class from the function itself.
            var class_name = value.toString().match( new RegExp( "^function\\s+([^\\s\\(]+)", "i" ) )[ 1 ];

            // Cache the class constructor.
            target.classes[ class_name ] = value;

        }
        else
        {

            // This value object is an actual instance of the given class. Therefore,
            // we need to get the name of the class from its constructor function.
            var class_name = value.constructor.toString().match( new RegExp( "^function\\s+([^\\s\\(]+)", "i" ) )[ 1 ];

            // Cache the class constructor.
            target.classes[ class_name ] = value.constructor;

            // In addition to caching the class constructor, let's cache this instance
            // of the given class itself (as it will act as a singleton).
            target.cache[ class_name ] = value;

            // Check to see if the application is running. If it is, then we need to initialize
            // the singleton instance.
            if (this.running)
            {
                this.init_class( value );
            }

        }
    };

    // I add the given model class or instance to the model class library. Any classes
    // that are passed in AS instances will be cached and act as singletons.
    intelesense_data.prototype.add_model = function( model )
    {
        this.add_class( this.models, model );
    };

    // I add the given view class or instance to the view class library. Any classes
    // that are passed in AS instances will be cached and act as singletons.
    intelesense_data.prototype.add_view = function( view )
    {
        this.add_class( this.views, view );
    };

    // I return an instance of the class with the given name from the given target.
    intelesense_data.prototype.get_class = function( target, class_name, arguments )
    {
        // Check to see if the instance is a cached singleton.
        if (target.cache[ class_name ])
        {
            // This is a cached class - return the singleton.
            return( target.cache[ class_name ] );

        }
        else
        {

            // This is not a cached class - return a new instance. In order to
            // do that, we will have to create an instance of it and then
            // initialize it with the given arguments.
            var instance = new (target.classes[ class_name ])(arguments);

            // Initialize the class
            this.init_class( instance );

            // Return the new instance.
            return( instance );

        }
    };

    // I return an instance of the class with the given name.
    intelesense_data.prototype.get_model = function( class_name, arguments )
    {
        return( this.get_class( this.models, class_name, arguments ) );
    };

    // I return an instance of the class with the given name.
    intelesense_data.prototype.get_view = function( class_name, arguments )
    {
        return( this.get_class( this.views, class_name, arguments ) );
    };

    // I initialize the given class instance.
    intelesense_data.prototype.init_class = function( instance, arguments )
    {
        // Check to see if the target instance has an init method.
        if (instance.init)
        {
            // Invoke the init method.
            instance.init(arguments);
        }
    };

    // I intialize the given collection of class singletons.
    intelesense_data.prototype.init_classes = function( classes )
    {
        $.each(
            classes,
            function( index, instance )
            {
                if (instance.init)
                {
                    // Invoke the init method.
                    instance.init();
                }
            }
        );
    };

    // I start the application.
    intelesense_data.prototype.run = function(input_settings)
    {
        //settings
        this.settings = $.extend({
            'default_view': 'individual',
            'container_view':'container',
            'parent_element':'body'
        },input_settings);

        // Initialize the objects
        this.init_classes( this.models.cache );
        this.init_classes( this.views.cache );

        // Flag that the application is running.
        this.running = true;

        //load container view
        this.get_view(this.settings.container_view).show();

        //load default view if one is set
        if(this.settings.default_view)
        {
            this.show_view(this.get_view(this.settings.default_view));
        }

    };

    // I show the given view; but first, I hide any existing view.
    intelesense_data.prototype.show_view = function( view )
    {
        // Check to see if there is a current view. If so, then hide it.
        if (this.current_view && this.current_view.hide_view)
        {
                this.current_view.hide();
        }

        // Store the given view as the current view.
        this.current_view = view;

        //turn on current view
        view.show();
    };

    // Create a new instance of the application and store it in the window.
    window.intelesense_data = new intelesense_data();

    // Return a new application instance.
    return( window.intelesense_data );

})( jQuery );
