var Toolset = Toolset || {};

Toolset.Gui = Toolset.Gui || {};

/**
 * Generic viewmodel for an item to be displayed in a listing (Toolset.Gui.ListingViewModel and its derivatives).
 *
 * @param {{displayName:string}} model
 * @param {*} itemActions An object with methods to perform actions on field definitions.
 * @constructor
 * @since 2.2
 */
Toolset.Gui.ItemViewModel = function(model, itemActions) {

    var self = this;

    self.displayName = ko.observable(model.displayName);


    /**
     * An object with methods to perform actions on field definitions.
     *
     * Each action accepts an array of field definitions, or a single field definition, as first parameter.
     *
     * @since 2.2
     */
    self.itemActions = itemActions;


    /**
     * Get an updated object with the same properties as the original model.
     *
     * It is assumed that if a property is a function, we're dealing with a knockout observable and the return value
     * will be returned instead of the function itself.
     *
     * @returns {*}
     * @since 2.0
     */
    self.getModelObject = function() {
        var ownModelProperties = _.keys(model);
        var modelObject = {};

        _.each(ownModelProperties, function(propertyName) {
            if(_.has(self, propertyName)) {
                if(_.isFunction(self[propertyName])) {
                    modelObject[propertyName] = self[propertyName]();
                } else {
                    modelObject[propertyName] = self[propertyName];
                }
            }
        });

        return modelObject;
    };


    /**
     * Update this ViewModel's properties by properties from a model object.
     *
     * If there is a property on the model that isn't on the viewmodel, or if such property isn't a function
     * (which is expected to be a knockout observable), it will be created.
     *
     * If there is the property and is a function, it will be called with the new value as a first parameter.
     *
     * @param updatedModel Model object with updated values.
     * @since 2.0
     */
    self.updateModelObject = function(updatedModel) {
        var ownModelProperties = _.keys(updatedModel);

        _.each(ownModelProperties, function(propertyName) {
            if (!_.has(self, propertyName) && !_.isFunction(self[propertyName])) {
                self[propertyName] = ko.observable();
            }
            self[propertyName](updatedModel[propertyName]);
        });
    };


    /**
     * Number of (AJAX) actions currently in progress.
     *
     * Do not touch it directly, use beginAction() and finishAction() instead.
     */
    self.inProgressActionCount = ko.observable(0);


    /**
     * Show a spinner if there is at least one AJAX action in progress.
     */
    self.isSpinnerVisible = ko.pureComputed(function() {
        return (self.inProgressActionCount() > 0);
    });


    /**
     * Indicate a beginning of an AJAX action.
     *
     * Make sure you also call finishAction() afterwards, no matter what the result is.
     */
    self.beginAction = function() {
        self.inProgressActionCount(self.inProgressActionCount() + 1);
    };


    /**
     * Indicate that an AJAX action was completed.
     */
    self.finishAction = function() {
        self.inProgressActionCount(self.inProgressActionCount() - 1);
    };


    self.isSelectedForBulkAction = ko.observable(false);


    /**
     * Handle the status of the bulk action checkbox.
     *
     * We need to act as if the checkbox is not selected if the bulk action is not allowed, and also properly
     * update the isSelectedForBulkAction property which is used by the listing viewmodel when querying items
     * to perform the bulk action.
     *
     * @since 3.0.5
     */
    self.isCheckedForBulkAction = ko.computed({
        read: function() {
            var isSelected = self.isSelectedForBulkAction();
            if(isSelected && ! self.isBulkActionAllowed()) {
                self.isSelectedForBulkAction(false);
                return false;
            }
            return  isSelected;
        },
        write: function(val) {
            if(self.isBulkActionAllowed()) {
                self.isSelectedForBulkAction(val);
            } else {
                self.isSelectedForBulkAction(false);
            }
        }
    });


    /**
     * Check whether a currently selected bulk action can be performed on this item
     *
     * @since 3.0
     */
    self.isBulkActionAllowed = ko.computed(function() {
        return true;
    });



    /**
     * This will be updated by the main ViewModel.
     *
     * @since 2.2
     */
    self.isBeingDisplayed = ko.observable(false);


    /**
     * When the item is not displayed in the listing table, we don't want it to be selected for a bulk action.
     *
     * @since 2.2
     */
    self.isBeingDisplayed.subscribe(function(newValue) {
        if(false === newValue) {
            self.isSelectedForBulkAction(false);
        }
    });


    /**
     * Determine CSS class for the tr tag depending on field status.
     *
     * @since 2.0
     */
    self.trClass = ko.computed(function() {
        // To be overridden
        return '';
    });


    /**
     * Simulates a link when displayNameLink exists
     *
     * @since 2.3
     */
    self.onDisplayNameClick = function() {
        if (!_.isUndefined(model['editLink'])) {
            document.location = model['editLink'];
        }
    }
};
