window.ABS = window.ABS || {};
jQuery(function () {
    ABS.Model = ABS.Model || {};

    ABS.Model.Tag = Backbone.Model.extend({
        getReferences : function() {
            return _.compact(_.flatten(_.pluck(this.get('taggings'), 'references')));
        }
    });

    ABS.Model.Note = Backbone.Model.extend({
        initialize : function() {
            // convert reference array to Reference Collection
            var refs = this.get('references');
            this.set({
                references : new ABS.Collection.ReferenceList(refs)
            }, {silent: true});
        },

        parse : function (resp) {
            if (resp.references) {
                resp.references = new ABS.Collection.ReferenceList(resp.references);
            }
            return resp;
        },

        getTitle : function () {
            // show snippet of content if no title
            if (this.get('title') && this.get('title').length > 0) {
                return this.get('title');
            }
            else if (this.get('content') && this.get('content').length > 0) {
                var snippet = this.get('content').substring(0, 35);
                return snippet.replace(/\W+\w+\W?$/, '...');
            }
            else {
                return ABS.common.i18n('no_title');
            }
        },

        url : function () {
            if (this.id) {
                return '/notes/' + this.id + '.json';
            }
            else {
                return '/notes.json';
            }
        }
    });

    ABS.Model.Reference = Backbone.Model.extend({
        link : function () {
            if (this.get('url')) {
                return this.get('url');
            }
            else {
                var url = ABS.common.osis.toUrl(this.get('start'));
                if (this.get('start') !== this.get('end')) {
                    url += '-' + ABS.common.osis.getComponents(this.get('end')).verse;
                }
                return url;
            }
        },

        display : function () {
            if (this.get('display')) {
                return this.get('display');
            }
            else {
                var start = ABS.common.osis.getComponents(this.get('start'));
                var display = start.book + ' ' + start.chapter + ':' + start.verse;
                if (this.get('start') !== this.get('end')) {
                    display += '-' + ABS.common.osis.getComponents(this.get('end')).verse;
                }
                return display;
            }
        }
    });

    ABS.Model.Tagging = Backbone.Model.extend({
        url : function() {
            return '/taggings.json';
        }
    });

    ABS.Model.Version = Backbone.Model.extend({
        initialize: function() {
            this.books = new ABS.Collection.BookList();
            this.books.url = 'versions/' + this.id + '/books.json';
            this.sections = new ABS.Collection.SectionList(this.get('sections'));
            this.browseChildren = this.books;
        },
        displayForBrowse: function() {
            return "(" + this.get('display_abbreviation') + ") " + this.get('name');
        }
    });

    ABS.Model.Section = Backbone.Model.extend({
        initialize: function() {
            this.books = new ABS.Collection.BookList();
            this.books.url = 'versions/' + this.get('version_id') + '/books.json?testament=' + this.id;

            this.browseChildren = this.books;
        },

        displayForBrowse: function() {
            return ABS.common.i18n(this.get('id').toLowerCase());
        }
    });

    ABS.Model.Book = Backbone.Model.extend({
        initialize: function() {
            this.chapters = new ABS.Collection.ChapterList;
            this.chapters.url = 'books/' + this.id + '/chapters.json';
            this.browseChildren = this.chapters;
        },
        displayForBrowse: function() {
            return this.get('name');
        }
    });

    ABS.Model.Chapter = Backbone.Model.extend({
        displayForBrowse: function() {
            return this.get('chapter');
        },
        url: function() {
            return '/chapters/' + this.id + '.json';
        }
    });
    
    ABS.Model.Verse = Backbone.Model.extend({
        
        components: function () {
            return ABS.common.osis.getComponents(this.get('id'));
        },
        
        // the version for this verse (just a string, not a model)
        version: function () {
            return this.components().version;
        },
        
        // a string representing the actual range of this verse.  some verses 
        // are in fact multi-verse spans.  Returns, e.g., '1', '1-3', etc.
        range: function () {
            var range = this.get('verse');
            if (this.get('lastverse') != range) {
                range += '-' + this.get('lastverse');
            }
            return range;
        },
        
        // a URL for display
        link: function () {
            return ABS.common.osis.toUrl(this.id);
        }
        
    });

    // Model for controlling the Browse panel
    ABS.Model.Browse = Backbone.Model.extend({
        types: ['version', 'book', 'chapter'],

        // cache for version list
        versions: false,

        initialize: function () {
            var that = this;
            this.updateList();
        },

        // reset to version list
        reset : function() {
            this.pop('version');
        },

        // given a type, pop off all items below and including this type
        pop: function(popType) {
            var found = false,
                that  = this;
            _.each(this.types, function(type) {
                if (popType === type || found) {
                    that.unset(type);
                    found = true;
                }
            });

            if (found) {
                this.updateList();
            }
        },

        // add this choice to the first available slot
        push: function(choice) {
            var typeFound = false,
                that = this;
            _.each(this.types, function(type) {
                if (!typeFound && !that.get(type)) {
                    var attributes = {};
                    attributes[type] = choice;
                    that.set(attributes);
                    typeFound = type;
                }
            });

            this.updateList();
        },

        // get currently selected choices and next unselected choice
        getChoices: function() {
            var that = this,
                foundEnd = false,
                choices = [];
            _.each(this.types, function(type) {
                if (!foundEnd && that.get(type)) {
                    var choice = {'type' : type};
                    if (type === 'section') {
                        choice.name = that.get(type).id;
                    }
                    else if (type === 'version') {
                        choice.name = that.get(type).get('display_abbreviation');
                    }
                    else if (type === 'book') {
                        choice.name = that.get(type).get('name');
                    }
                    choices.push(choice);
                }
                else if (!foundEnd) {
                    choices.push({
                        'type' : type,
                        'name' : ''
                    });
                    foundEnd = true;
                }
            });
            return choices;
        },

        // based on values set, update list for next selection
        updateList: function() {
            var typeFound = 'none',
                that = this;
            that.unset('list');

            _.each(this.types, function(type) {
                if (that.get(type)) {
                    typeFound = type;
                }
            });

            if (typeFound === 'none') {
                // if nothing is currently set, set list to list of versions
                if (!that.versions) {
                    var versionsJson = ABS.bootstrap.versions;
                    that.versions = new ABS.Collection.VersionList(versionsJson);
                    that.set({'list' : that.versions});
                }
                else {
                    that.set({'list': that.versions});
                }
            }
            else {
                if (that.get(typeFound).browseChildren.length < 1) {
                    // This event fires to let listeners know that we're
                    // about to start fetching data from the server
                    that.trigger('startfetch');

                    that.get(typeFound).browseChildren.fetch({
                        success: function(collection) {
                            that.set({'list': collection});
                        }
                    });
                }
                else {
                    that.set({'list': that.get(typeFound).browseChildren});
                }
            }
        }
    });

    ABS.Model.SearchResults = Backbone.Model.extend({
        query: "",
        versionList: "",
        data: {},
        offset: 0,

        initialize: function(options) {
            this.query = options.query;
            if (options.versionList) {
                this.versionList = options.versionList;
            }
            if (options.offset) {
                this.offset = options.offset;
            }
            if (options.data) {
                this.data = options.data;
            }
        },

        url: function() {
            var urlString = '/search.json?query=' + this.query;
            if (this.versionList !== "") {
                urlString += '&version=' + this.versionList;
            }
            var data = $.param(this.data);
            if (data !== "") {
                urlString += '&' + data;
            }

            urlString += '&offset=' + this.offset;

            return urlString;
        }
    });
    
    ABS.Model.ExpandSearch = Backbone.Model.extend({
        keyword: "",
        versions: [],
        passage: "",
        
        initialize: function (options) {
            this.keyword  = options.keyword || '';
            this.passage  = options.passage || '';
            
            // set versions, filtering out the one that was used for the 
            // passage, so we don't include it in the expansion
            var version = ABS.common.osis.getComponents(this.passage).version;
            this.versions = _.reject(options.versions || [], function (ver) {
                return ver == version;
            });
        },
        
        url: function () {
            return (
                '/verses/expand.json?' + 
                _.flatten([
                    $.param({
                        keyword: this.keyword,
                        expandPassage: this.passage
                    }),
                    _.map(this.versions, function (version) {
                        return ('expandVersion[]=' + version);
                    })
                ]).join('&')
            );
        },
        
        parse: function (resp) {
            resp.verses = new ABS.Collection.VerseList(resp.verses);
            return resp;
        }
    });

    ABS.Model.TagSearchResults = Backbone.Model.extend({
        name: "",

        initialize: function(options) {
            if (options && options.name) {
                this.name = options.name;
            }
        },

        url: function() {
            return '/tags/' + this.name + '.json';
        }
    });

    // This is not a real model, but a class to encapsulate Chapter Queue
    // This is a queue for chapter requests.  Requests are added on by osisId.
    // add() responds with deferred.promise() which will be resolved when
    // the Ajax request completes.  If the queue already had a request for
    // a particular chapter, then it'll merge the two.  Any deferred callbacks
    // should be executed in the order that they were added.
    ABS.ChapterQueue = {
        queue : [],

        add : function(osisId) {
            var dfd = $.Deferred();

            // if false-y osisId, return promise and immediately reject it
            if (!osisId) {
                _.defer(function() {
                    dfd.reject();
                });
                return dfd.promise();
            }

            var existingRequest = _.detect(this.queue, function(req) {
                return req.osisId === osisId;
            });

            if (existingRequest) {
                return existingRequest.deferred.promise();
            }
            else {
                this.queue.push({
                    osisId   : osisId,
                    deferred : dfd
                });
                // if we've just added the first element, process queue
                // otherwise we assume the queue is processing
                if (this.queue.length === 1) {
                    this.next();
                }
                return dfd.promise();
            }
        },

        next : function() {
            var that = this;
            // grab first request, but only remove from queue after completion
            // since we check for queue.length === 1 in add() to start
            // and this allows us to collapse requests that come in for the 
            // current pending request
            var request = _.first(this.queue);
            if (!request || !request.osisId || !request.deferred) { return; }

            var chapter = new ABS.Model.Chapter({id: request.osisId});

            chapter.fetch({
                success : function () {
                    that.queue.shift();
                    request.deferred.resolve(chapter);
                    that.next();
                },
                error : function () {
                    that.queue.shift();
                    request.deferred.reject(chapter);
                    that.next();
                }
            });
        }
    };

    ABS.Model.RelevancyVote = Backbone.Model.extend({
        url : function () {
            if (this.id) {
                return '/relevancy/' + this.id + '.json';
            }
            else {
                return '/relevancy.json';
            }
        }
    });
    
    // stub for Developer Tools
    ABS.Model.DeveloperTool = Backbone.Model.extend({
        
        // get the template name for this tool
        template: function () {
            return 'Developer' + _.capitalize(this.get('id'));
        }
        
    });

});



