define(
    ['backbone', 'underscore', 'underscore.string', 'js/utils/module'],
function(Backbone, _, str, ModuleUtils) {
    'use strict';
    var XBlockInfo = Backbone.Model.extend({

        urlRoot: ModuleUtils.urlRoot,

        // NOTE: 'publish' is not an attribute on XBlockInfo, but it is used to signal the publish
        // and discard changes actions. Therefore 'publish' cannot be introduced as an attribute.
        defaults: {
            'id': null,
            'display_name': null,
            'category': null,
            'data': null,
            'metadata': null,
            /**
             * The Studio URL for this xblock, or null if it doesn't have one.
             */
            'studio_url': null,
            /**
             * An optional object with information about the children as well as about
             * the primary xblock type that is supported as a child.
             */
            'child_info': null,
            /**
             * An optional object with information about each of the ancestors.
             */
            'ancestor_info': null,
            /**
             * Date of the last edit to this xblock or any of its descendants.
             */
            'edited_on': null,
            /**
             * User who last edited the xblock or any of its descendants. Will only be present if
             * publishing info was explicitly requested.
             */
            'edited_by': null,
            /**
             * True iff a published version of the xblock exists.
             */
            'published': null,
            /**
             * Date of the last publish of this xblock, or null if never published.
             */
            'published_on': null,
            /**
             * User who last published the xblock, or null if never published. Will only be present if
             * publishing info was explicitly requested.
             */
            'published_by': null,
            /**
             * True if the xblock is a parentable xblock.
             */
            has_children: null,
            /**
             * True if the xblock has changes.
             * Note: this is not always provided as a performance optimization. It is only provided for
             * verticals functioning as units.
             */
            'has_changes': null,
            /**
             * Represents the possible publish states for an xblock. See the documentation
             * for XBlockVisibility to see a comprehensive enumeration of the states.
             */
            'visibility_state': null,
            /**
             * True if the release date of the xblock is in the past.
             */
            'released_to_students': null,
            /**
             * If the xblock is published, the date on which it will be released to students.
             * This can be null if the release date is unscheduled.
             */
            'release_date': null,
            /**
             * The xblock which is determining the release date. For instance, for a unit,
             * this will either be the parent subsection or the grandparent section.
             * This can be null if the release date is unscheduled. Will only be present if
             * publishing info was explicitly requested.
             */
            'release_date_from': null,
            /**
             * True if this xblock is currently visible to students. This is computed server-side
             * so that the logic isn't duplicated on the client. Will only be present if
             * publishing info was explicitly requested.
             */
            'currently_visible_to_students': null,
            /**
             * If xblock is graded, the date after which student assessment will be evaluated.
             * It has same format as release date, for example: 'Jan 02, 2015 at 00:00 UTC'.
             */
            'due_date': null,
            /**
             * Grading policy for xblock.
             */
            'format': null,
            /**
             * List of course graders names.
             */
            'course_graders': null,
            /**
             * True if this xblock contributes to the final course grade.
             */
            'graded': null,
            /**
             * The same as `release_date` but as an ISO-formatted date string.
             */
            'start': null,
            /**
             * The same as `due_date` but as an ISO-formatted date string.
             */
            'due': null,
            /**
             * True iff this xblock is explicitly staff locked.
             */
            'has_explicit_staff_lock': null,
            /**
             * True iff this any of this xblock's ancestors are staff locked.
             */
            'ancestor_has_staff_lock': null,
            /**
             * The xblock which is determining the staff lock value. For instance, for a unit,
             * this will either be the parent subsection or the grandparent section.
             * This can be null if the xblock has no inherited staff lock. Will only be present if
             * publishing info was explicitly requested.
             */
            'staff_lock_from': null,
            /**
             * True iff this xblock should display a "Contains staff only content" message.
             */
            'staff_only_message': null,
            /**
             * True iff this xblock is a unit, and it has children that are only visible to certain
             * content groups. Note that this is not a recursive property. Will only be present if
             * publishing info was explicitly requested.
             */
            'has_content_group_components': null,
            /**
             * actions defines the state of delete, drag and child add functionality for a xblock.
             * currently, each xblock has default value of 'True' for keys: deletable, draggable and childAddable.
             */
            'actions': null,
            /**
             * Header visible to UI.
             */
            'is_header_visible': null,
            /**
             * Optional explanatory message about the xblock.
             */
            'explanatory_message': null,
            /**
             * The XBlock's group access rules.  This is a dictionary keyed to user partition IDs,
             * where the values are lists of group IDs.
             */
            'group_access': null,
            /**
             * User partition dictionary.  This is pre-processed by Studio, so it contains
             * some additional fields that are not stored in the course descriptor
             * (for example, which groups are selected for this particular XBlock).
             */
            'user_partitions': null
        },

        initialize: function() {
            // Extend our Model by helper methods.
            _.extend(this, this.getCategoryHelpers());
        },

        parse: function(response) {
            if (response.ancestor_info) {
                response.ancestor_info.ancestors = this.parseXBlockInfoList(response.ancestor_info.ancestors);
            }
            if (response.child_info) {
                response.child_info.children = this.parseXBlockInfoList(response.child_info.children);
            }
            return response;
        },

        parseXBlockInfoList: function(list) {
            return _.map(list, function(item) {
                return this.createChild(item);
            }, this);
        },

        createChild: function(response) {
            return new XBlockInfo(response, {parse: true});
        },

        hasChildren: function() {
            var childInfo = this.get('child_info');
            return childInfo && childInfo.children.length > 0;
        },

        isPublishable: function() {
            return !this.get('published') || this.get('has_changes');
        },

        isDeletable: function() {
            return this.isActionRequired('deletable');
        },

        isDuplicable: function() {
            return this.isActionRequired('duplicable');
        },

        isDraggable: function() {
            return this.isActionRequired('draggable');
        },

        isChildAddable: function() {
            return this.isActionRequired('childAddable');
        },

        isHeaderVisible: function() {
            if (this.get('is_header_visible') !== null) {
                return this.get('is_header_visible');
            }
            return true;
        },

        /**
         * Return true if action is required e.g. delete, drag, add new child etc or if given key is not present.
         * @return {boolean}
        */
        isActionRequired: function(actionName) {
            var actions = this.get('actions');
            if (actions !== null) {
                if (_.has(actions, actionName) && !actions[actionName]) {
                    return false;
                }
            }
            return true;
        },

        /**
         * Return a list of convenience methods to check affiliation to the category.
         * @return {Array}
        */
        getCategoryHelpers: function() {
            var categories = ['course', 'chapter', 'sequential', 'vertical'],
                helpers = {};

            _.each(categories, function(item) {
                helpers['is' + str.titleize(item)] = function() {
                    return this.get('category') === item;
                };
            }, this);

            return helpers;
        },

       /**
        * Check if we can edit current XBlock or not on Course Outline page.
        * @return {Boolean}
        */
        isEditableOnCourseOutline: function() {
            return this.isSequential() || this.isChapter() || this.isVertical();
        },

       /*
        * Check whether any verification checkpoints are defined in the course.
        * Verification checkpoints are defined if there exists a user partition
        * that uses the verification partition scheme.
        */
        hasVerifiedCheckpoints: function() {
            var partitions = this.get('user_partitions') || [];

            return Boolean(_.find(partitions, function(p) {
                return p.scheme === 'verification';
            }));
        }
    });
    return XBlockInfo;
});