import ko from 'knockout';
import _ from 'underscore';
import uuid from 'uuid';
import dispose from 'utils/dispose';

/**
* A viewmodel used for generic widgets
*
* @constructor
* @name WidgetViewModel
*
* @param  {string} params - a configuration object
*/
var WidgetViewModel = function(params) {
    this.externalObservables = ['value', 'config', 'expanded', 'defaultValueSubscription', 'valueSubscription'];
    var self = this;
    this.state = params.state || 'form';
    var expanded = params.expanded || ko.observable(false);
    var nodeid = params.node ? params.node.nodeid : uuid.generate();
    this.expanded = ko.computed({
        read: function() {
            return nodeid === expanded();
        },
        write: function(val) {
            if (val) {
                expanded(nodeid);
            } else {
                expanded(false);
            }
        }
    });
    this.value = params.value || ko.observable(null);
    this.formData = params.formData || null;
    this.form = params.form || null;
    this.tile = params.tile || null;
    this.widget = params.widget || null;
    this.inResourceEditor = (typeof params.inResourceEditor === "boolean" ? params.inResourceEditor : null);
    this.results = params.results || null;
    this.hideEmptyNodes = params.hideEmptyNodes;
    this.displayValue = ko.computed(function() {
        if (self.value.value) {
            return ko.unwrap(self.value.value);
        }
        else {
            return ko.unwrap(self.value);
        }
    });
    this.disabled = params.disabled || ko.observable(false);
    this.node = params.node || null;
    this.configForm = params.configForm || false;
    this.config = params.config || ko.observable({});
    this.configObservables = params.configObservables || {};
    this.configKeys = params.configKeys || [];
    this.configKeys.push('label');
    this.configKeys.push('required');
    this.valueProperties = params.valueProperties || [];
    if (this.node) {
        this.required = this.node.isrequired;
    }
    if (typeof this.config !== 'function') {
        this.config = ko.observable(this.config);
    }

    this.nodeCssClasses = ko.pureComputed(function() {
        return [ko.unwrap(self.node?.alias),
            self.node?.graph?.attributes?.slug,
            self.widget?.widgetLookup[ko.unwrap(self.widget?.widget_id)].name
            ].join(" ").trim();
    });

    this.disposables = [];

    var subscribeConfigObservable = function(obs, key) {
        self[key] = obs;

        var forwardSubscription = self[key].subscribe(function(val) {
            if (params.hasOwnProperty('graphDesignerHasDirtyWidget')) {
                if (val && val !== params.config()[key]) {
                    params.graphDesignerHasDirtyWidget(true);
                }
            }

            var configObj = self.config();
            configObj[key] = val;
            self.config(configObj);
        });

        var reverseSubscription = self.config.subscribe(function(val) {
            if (val[key] !== self[key]()) {
                self[key](val[key]);
            }
        });
        self.disposables.push(forwardSubscription);
        self.disposables.push(reverseSubscription);
    };
    _.each(this.configObservables, subscribeConfigObservable);
    _.each(this.configKeys, function(key) {
        var obs = ko.observable(self.config()[key]);
        subscribeConfigObservable(obs, key);
    });

    if (ko.isObservable(this.value) && ko.isObservable(this.defaultValue)) {
        var defaultValue = this.defaultValue();
        if (this.tile && !this.tile.noDefaults && !ko.unwrap(this.tile.dirty) && ko.unwrap(this.tile.tileid) == "" && defaultValue != null && defaultValue != "") {
            this.value(defaultValue);
        }

        if (!self.form) {
            if (ko.isObservable(self.value)) {
                self.valueSubscription = self.value.subscribe(function(val){
                    if (self.defaultValue() != val) {
                        self.defaultValue(val);
                    }
                });
                self.defaultValueSubscription = self.defaultValue.subscribe(function(val){
                    if (self.value() != val) {
                        self.value(val);
                    }
                });
            }
        }
    }

    this.valueProperties.forEach(function(property) {
        if (ko.isObservable(self.value)){
            self[property] = ko.observable();
            self[property].subscribe(function() {
                self.value(
                    self.valueProperties.reduce(
                        function(data, property){
                            data[property] = self[property]();
                            return data;
                        }, {}
                    )
                );
            }, this);
        } else {
            self[property] = self.value[property];
        }
    });

    this.disposables.push(this.defaultValueSubscription);
    this.disposables.push(this.valueSubscription);

    this.onInit = params.onInit;
    if (typeof this.onInit === 'function') {
        this.onInit();
    }

    this.dispose = function(){
        //console.log('disposing ' + self.constructor.name);
        dispose(self);
    };
};

export default WidgetViewModel;
