/*global define*/
/*jslint browser:true,white:true,single:true */

/*
 * Provides app spec functionality.
 */

define([
    'require',
    'bluebird',
    'common/lang',
    'common/sdk',
    'common/specValidation',
    'widgets/appWidgets2/validators/resolver'
], function(require, Promise, lang, sdk, Validation, validationResolver) {
    'use strict';

    function factory(config) {
        var spec;

        if (config.spec) {
            spec = config.spec;
        } else if (config.appSpec) {
            spec = sdk.convertAppSpec(config.appSpec);
        } else {
            throw new Error('Either a spec or appSpec must be provided');
        }

        function getSpec() {
            return spec;
        }

        /*
         * Make a "shell" model based on the spec. Recursively build an object
         * with properties as defined by the spec.
         * Effectively this means that only the top level is represented, since
         */
        function makeEmptyModel() {
            var model = {};
            spec.parameters.layout.forEach(function(id) {
                model[id] = spec.parameters.specs[id].data.defaultValue || spec.parameters.specs[id].data.nullValue;
            });
            return model;
        }

        /*
        Makes a model (not a view model quite yet, really, because just data)
        from the given spec.
        It does this by:
        The top level spec is treated as a struct.
        The default value for each paramater is simply set as the value for the given parameter
        on a model object.
        One exception is that if a parameter is a
        */
        function makeDefaultedModel() {
            var model = {};
            spec.parameters.layout.forEach(function(id) {
                var paramSpec = spec.parameters.specs[id];
                var modelValue;
                if (paramSpec.data.type === 'struct') {
                    if (paramSpec.data.constraints.required) {
                        modelValue = lang.copy(paramSpec.data.defaultValue);
                    } else {
                        modelValue = paramSpec.data.nullValue;
                    }
                } else {
                    modelValue = lang.copy(paramSpec.data.defaultValue);
                }
                model[id] = modelValue;
            });
            return model;
        }

        var typeToValidatorModule = {
            string: 'text',
            int: 'int',
            float: 'float',
            sequence: 'sequence',
            struct: 'struct'
        }

        function getValidatorModule(fieldSpec) {
            var moduleName = typeToValidatorModule[fieldSpec.data.type];
            if (!moduleName) {
                throw new Error('No validator for type: ' + fieldSpec.data.type);
            }
            return moduleName;
        }

        // function validateField(fieldValue, fieldSpec) {
        //     return new Promise(function(resolve, reject) {
        //         require(['../widgets/appWidgets2/validators/' + getValidatorModule(fieldSpec)], function(validator) {
        //             resolve(validator.applyConstraints(fieldValue, fieldSpec.data.constraints))
        //         }, function(err) {
        //             reject(err);
        //         });
        //     });
        // }


        function validateModel(model) {
            // TODO: spec at the top level should be a struct...
            // return;
            var validationMap = {};
            var output_values = new Set();
            spec.parameters.layout.forEach(function(id) {
                var fieldValue = model[id];
                var fieldSpec = spec.parameters.specs[id];
                // return validateField(fieldValue, fieldSpec);
                validationMap[id] = validationResolver.validate(fieldValue, fieldSpec);
                if (fieldSpec.ui.class === 'output') {
                    if (output_values.has(fieldValue)){
                        validationMap[id] = {
                            isValid: false,
                            messageId: "obj-duplicate-name",
                            errorMessage: "This value duplicates an existing output object name",
                            shortMessage: "",
                            diagnosis: "invalid"
                        }
                    }
                    output_values.add(fieldValue);
                }
            });
            return Promise.props(validationMap);
        }

        return Object.freeze({
            getSpec: getSpec,
            makeEmptyModel: makeEmptyModel,
            makeDefaultedModel: makeDefaultedModel,
            validateModel: validateModel
        });
    }

    return {
        make: function(config) {
            return factory(config);
        }
    };
});

@@ -3480,51 +3480,8 @@
 %7B%7D;%0A
-            var output_values = new Set();%0A
     
@@ -3793,580 +3793,8 @@
 c);%0A
-                if (fieldSpec.ui.class === 'output') %7B%0A                    if (output_values.has(fieldValue))%7B%0A                        validationMap%5Bid%5D = %7B%0A                            isValid: false,%0A                            messageId: %22obj-duplicate-name%22,%0A                            errorMessage: %22This value duplicates an existing output object name%22,%0A                            shortMessage: %22%22,%0A                            diagnosis: %22invalid%22%0A                        %7D%0A                    %7D%0A                    output_values.add(fieldValue);%0A                %7D%0A
     

remove spec.js changes