"use strict";

App.module("RepresentationModule", function(RepresentationModule, App, Backbone, Marionette, $, _, Logger) {

    /**
     * Masters are "extensions" that know how to represent particular views and entities
     * They determine:
     *      - which controls are located in the panels
     *      - what labels do columns and rows have
     *      - how high the rows are
     *      - which data sit behind vis instances
     *      - how vis instances render themselves
     */
    RepresentationModule.addInitializer(function(options){

        RepresentationModule.MasterPrototypes = {};
        RepresentationModule.masters = {};

        /**
         * Registers a new master
         */
        RepresentationModule.registerMaster = function(masterDefinition) {
            var MasterPrototype;
            var inheritId = masterDefinition.inherit ? masterDefinition.inherit : "";
            var id = masterDefinition.id;
            if (!id) {
                throw _.str.sprintf("Master definition does not contain an id");
            }
            var masterWithThisId = null;
            try {
                masterWithThisId = RepresentationModule.getMasterById(id);
            } catch (e) {
            }
            if (masterWithThisId) {
                throw _.str.sprintf("Master with id \"%s\" already exists", id);
            }

            var prototypeOptions = {};
            var prototypeDefaultConfigParameterValues = {};
            //var prototypeErrorsToVisInstanceCoverParams = {};
            if (!inheritId) {
                MasterPrototype = Marionette.Object;
            } else {
                try {
                    MasterPrototype = RepresentationModule.getMasterPrototypeById(inheritId);
                    var parentMaster = RepresentationModule.getMasterById(inheritId);
                    prototypeOptions = parentMaster.options;
                    prototypeDefaultConfigParameterValues = parentMaster.defaultConfigParameterValues;
                    //prototypeErrorsToVisInstanceCoverParams = parentMaster.errorsToVisInstanceCoverParams;
                } catch (e) {
                    throw _.str.sprintf("Master with id \"%s\" could not be inherited from master with id \"%s\" (it does not exist)", id, inheritId);
                }
            }

            var combinedOptions = masterDefinition.options
                ? _.defaults({}, masterDefinition.options, prototypeOptions)
                : _.clone(prototypeOptions);
            for (var key in masterDefinition.options) {
                if (masterDefinition.options.hasOwnProperty(key) && masterDefinition.options[key] === undefined) {
                    delete combinedOptions[key];
                }
            }
            masterDefinition.options = combinedOptions;

            var combinedDefaultConfigParameterValues = masterDefinition.defaultConfigParameterValues
                ? _.defaults({}, masterDefinition.defaultConfigParameterValues, prototypeDefaultConfigParameterValues)
                : _.clone(prototypeDefaultConfigParameterValues);
            for (var key in masterDefinition.defaultConfigParameterValues) {
                if (masterDefinition.defaultConfigParameterValues.hasOwnProperty(key) && masterDefinition.defaultConfigParameterValues[key] === undefined) {
                    delete combinedDefaultConfigParameterValues[key];
                }
            }
            masterDefinition.defaultConfigParameterValues = combinedDefaultConfigParameterValues;

            // masterDefinition.errorsToVisInstanceCoverParams = masterDefinition.errorsToVisInstanceCoverParams
            //    ? _.defaults(masterDefinition.errorsToVisInstanceCoverParams, prototypeErrorsToVisInstanceCoverParams)
            //    : prototypeErrorsToVisInstanceCoverParams;

            var Master = MasterPrototype.extend(masterDefinition);
            RepresentationModule.MasterPrototypes[id] = Master;
            RepresentationModule.masters[id] = new Master();
        };

        RepresentationModule.getMasterPrototypeById = function(id) {
            var MasterPrototype = RepresentationModule.MasterPrototypes[id];
            if (!MasterPrototype) {
                throw _.str.sprintf("MasterPrototype with id \"%s\" not found.", id);
            }
            return MasterPrototype;
        };

        RepresentationModule.getMasterById = function(id) {
            var master = RepresentationModule.masters[id];
            if (!master) {
                throw _.str.sprintf("Master with id \"%s\" not found.", id);
            }
            return master;
        };

        RepresentationModule.getMasterForConfig = function(config, lookAtPlanned) {
            var dimension = config.getDimension();
            var configGridType = config.getConfigGridType();
            var configKind = lookAtPlanned ? config.getPlannedParameterValue("kind") : config.getParameterValue("kind");
            configKind = _.str.trim(configKind);
            if (!configKind) {
                configKind = "default";
            }
            var id = [dimension, configGridType, configKind].join(".");
            var master = RepresentationModule.masters[id];
            if (!master) {
                try {
                    master = RepresentationModule.getMasterById([dimension, "_unknown"].join("."));
                } catch (e) {
                    throw _.str.sprintf("Master for config \"%s\" not found (master with id \"%s\" does not exist).", JSON.stringify(config.serialize()), id);
                }
            }
            return master;
        };
    });
}, Logger);
