"use strict";

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

    RepresentationModule.addInitializer(function(options){

        RepresentationModule.registerMaster({
            id: "entity.collection.default",
            inherit: "entity._default",

            defaultConfigParameterValues: {
                "library": "",
                "year": "",
                "genre": "",
                "composer": "",
                "performer": "",
                "title": "",
                "place": "",
                "collection": ""
            },

            options: {
                availableLibraries: ["bl", "charm", "ilm", "mazurka"],
                librarySeparator: ";",
                librarySeparatorAlternatives: /\,/g,
                presenceOfParameters: {
                    "title": ["bl", "charm", "ilm", "mazurka"],
                    "year": ["bl", "charm", "mazurka"], // in ILM there is a release date (160 000 / 250 000)
                    "composer": ["bl", "charm", "mazurka"],
                    "performer": ["bl", "charm", "mazurka"], // artist in ILM
                    "collection": ["bl"], // (only for ethnographic, excludes composer)
                    "genre": ["ilm"],
                    "place": ["bl"],
                },
            },


            // =================================================================
            // housekeeping

            extractCleanedConfigParameterValuesFromPlannedParameterValues: function(config) {
                // Parent master's behaviour
                var result = RepresentationModule.getMasterById("entity._default").extractCleanedConfigParameterValuesFromPlannedParameterValues.apply(this, arguments);

                // no need to remove recordingURI as there is no default value for it (it is removed automatically)

                //console.log("CLEANED BEFORE", result);
                // remove all parameters that are not relevant to the chosen library
                var presentParameterNames = _.keys(this._getPresentParameterNamesForLibrary(result.library));

                for (var parameterName in result) {
                    if (result.hasOwnProperty(parameterName) && parameterName !== "library") {
                        if (presentParameterNames.indexOf(parameterName) == -1) {
                            delete result[parameterName];
                        }
                    }
                }

                //console.log("CLEANED AFTER", result);
                return result;
            },


            // =================================================================
            // config grid panel

            _parseLibraryStringToArray: function(library) {
                var fixedLibrary = _.str.trim(library).replace(this.options.librarySeparatorAlternatives, this.options.librarySeparator).toLowerCase();
                if (!fixedLibrary.length) {
                    return [];
                } else {
                    return _.map(fixedLibrary.split(this.options.librarySeparator), function(v) {return _.str.trim(v);});
                }
            },


            _parseLibraryStringToObject: function(library) {
                var result = {};
                var libraryAsArray = this._parseLibraryStringToArray(library);
                for (var i = 0; i < libraryAsArray.length; i++) {
                    result[libraryAsArray[i]] = true;
                }
                return result;
            },


            _restoreLibraryStringFromArray: function(libraryArray) {
                libraryArray.sort();
                return libraryArray.length ? libraryArray.join(this.options.librarySeparator) : "";

            },

            _restoreLibraryStringFromObject: function(libraryObject) {
                return this._restoreLibraryStringFromArray(_.keys(libraryObject));
            },


            _getPresentParameterNamesForLibrary: function(library) {
                if (this._cachedPresentParameterNamesByLibrary === undefined) {
                    this._cachedPresentParameterNamesByLibrary = {};
                }
                if (this._cachedPresentParameterNamesByLibrary[library] === undefined) {
                    var libraries = this._parseLibraryStringToArray(library);
                    var result = {};
                    if (libraries.length) {
                        for (var parameterName in this.options.presenceOfParameters) {
                            var fieldPresence = this.options.presenceOfParameters[parameterName];
                            if (_.intersection(libraries, fieldPresence).length == libraries.length) {
                                result[parameterName] = true;
                            }
                        }
                    }
                    this._cachedPresentParameterNamesByLibrary[library] = result;
                }

                return this._cachedPresentParameterNamesByLibrary[library];
            },


            // -----------------------------------------------------------------
            // config grid panel - prepare

            prepareConfigGridPanelMainArea: function(configGridPanelView) {
                configGridPanelView._$mainArea.data("$libraryLabel_yes", configGridPanelView._$mainArea.find(".cgpma__id_library-label_yes"));
                configGridPanelView._$mainArea.data("$libraryLabel_no",  configGridPanelView._$mainArea.find(".cgpma__id_library-label_no"));

                // Library tickboxes
                for (var i = this.options.availableLibraries.length - 1; i >= 0; --i) {
                    var availableLibrary = this.options.availableLibraries[i];
                    var $tickbox = configGridPanelView._$mainArea.find(".cgpma__id_" + availableLibrary);
                    configGridPanelView._$mainArea.data("$tickbox_library_" + availableLibrary, $tickbox);
                    $tickbox.data("libraryId", availableLibrary);
                }

                // Parent master's behaviour
                RepresentationModule.getMasterById("entity._default").prepareConfigGridPanelMainArea.apply(this, arguments);
            },


            __panelInputChangeValueHandler: function(event) {
                if (event.type == "tickboxchangevalue") {
                    var $thickbox = $(event.target);
                    var libraryId = $thickbox.data("libraryId");
                    if (libraryId) {
                        var configGridPanelView = $thickbox.data("configGridPanelView");
                        var $mainArea = configGridPanelView._$mainArea;
                        var master    = $thickbox.data("configGridPanelView")._masterBehindMainArea;
                        if (!$mainArea.data("ignoreChangesInLibraryTickboxes")) {
                            var plannedLibraries = [];
                            for (var i = 0; i < master.options.availableLibraries.length; i++) {
                                var library = master.options.availableLibraries[i];
                                if ($mainArea.data("$tickbox_library_" + library).tickbox("option", "value")) {
                                    plannedLibraries.push(library);
                                }
                            };
                            master.planConfigParameterUpdateWithRespectToValueAndDefaultValue(configGridPanelView._cachedConfig, "library", plannedLibraries.join(";"));
                        }
                        return;
                    };
                }

                // Parent master's behaviour
                RepresentationModule.getMasterById("entity._default").__panelInputChangeValueHandler.apply(this, arguments);
            },


            // -----------------------------------------------------------------
            // config grid panel - sync

            syncConfigGridPanelMainArea: function(configGridPanelView, instant) {

                // bl, charm, ilm tick boxes + field visibility
                var library = this.getConfigParameterValueOrDefaultValue(configGridPanelView._cachedConfig, "library").toLowerCase();
                var plannedLibrary = this.getConfigPlannedParameterValueOrDefaultValue(configGridPanelView._cachedConfig, "library").toLowerCase();
                var libraryHash = library + plannedLibrary;
                if (configGridPanelView._$mainArea.data("libraryCache") != libraryHash) {
                    configGridPanelView._$mainArea.data("libraryCache", libraryHash);
                    //var libraryAsObject = this._parseLibraryStringToObject(library);
                    var plannedLibraryAsArray = this._parseLibraryStringToArray(plannedLibrary);
                    var plannedLibraryAsObject = this._parseLibraryStringToObject(plannedLibrary);

                    // tick boxes
                    configGridPanelView._$mainArea.data("ignoreChangesInLibraryTickboxes", true);
                    for (var i = this.options.availableLibraries.length - 1; i >= 0; --i) {
                        var availableLibrary = this.options.availableLibraries[i];
                        var $tickbox = configGridPanelView._$mainArea.data("$tickbox_library_" + availableLibrary);
                        var value = (plannedLibraryAsObject[availableLibrary] !== undefined) ? "1" : "";
                        var baseValue = (library !== plannedLibrary)
                                ? (value ? "" : "1")
                                : (value ? "1" : "");

                        $tickbox.tickbox("option", {
                            "value": value,
                            "baseValue": baseValue
                        });
                    }
                    configGridPanelView._$mainArea.removeData("ignoreChangesInLibraryTickboxes");

                    // show / hide input blocks
                    var presentParameterNames = _.keys(this._getPresentParameterNamesForLibrary(plannedLibrary));
                    var absentParameterNames = _.difference(_.keys(this.options.presenceOfParameters), presentParameterNames);

                    for (var i = 0; i < presentParameterNames.length; i++) {
                        configGridPanelView._$mainArea.data("$inputBlock_" + presentParameterNames[i]).show();
                    }
                    for (var i = 0; i < absentParameterNames.length; i++) {
                        configGridPanelView._$mainArea.data("$inputBlock_" + absentParameterNames[i]).hide();
                    }

                    // library label
                    configGridPanelView._$mainArea.data("$libraryLabel_no") .toggle(plannedLibraryAsArray.length == 0);
                    configGridPanelView._$mainArea.data("$libraryLabel_yes").toggle(plannedLibraryAsArray.length != 0);
                }

                // Parent master's behaviour
                RepresentationModule.getMasterById("entity._default").syncConfigGridPanelMainArea.apply(this, arguments);
            },


            // =================================================================
            // config grid header

            _generateHeaderLabel1: function(viewHeaderView) {
                return this._generateCollectionConfigTitle(viewHeaderView.options.config);
            },

            _generateHeaderLabel2: function(viewHeader) {
                var rawConfigParameters = viewHeader.options.config.attributes.parameters.attributes;
                var attributesOfDefinitionForCollection         = viewHeader.dynamicDerivedConfigData.attributes.dynamicDefinitionForCollection.attributes;
                var attributesOfDefinitionForOverlayedRecording = viewHeader.dynamicDerivedConfigData.attributes.dynamicDefinitionForOverlayedRecording.attributes;

                var labelParts = [];

                var collectionSize = attributesOfDefinitionForCollection.fullSize;
                var sampleSize = attributesOfDefinitionForCollection.sampleSize;
                var overlayedRecordingURI = rawConfigParameters.recordingURI;
                var overlayedRecordingLabel = attributesOfDefinitionForOverlayedRecording.label;

                // collection size
                if (_.isNumber(collectionSize)) {
                    var collectionSizeAsStr = collectionSize ? _.str.numberFormat(collectionSize) : "no";
                    labelParts.push(_.str.sprintf("%s recording%s", collectionSizeAsStr, collectionSize !== 1 ? "s" : ""));
                } else if (collectionSize === null) {
                    labelParts.push("updating...");
                } else {
                    if (attributesOfDefinitionForCollection.id === false) {
                        labelParts.push("an error occured");
                    }
                }

                // sample size
                if (_.isNumber(sampleSize) && sampleSize !== collectionSize) {
                    labelParts.push(" (", sampleSize, " in the sample)");
                }

                // recordingURI
                if (overlayedRecordingURI) {
                    if (_.isString(overlayedRecordingLabel)) {
                        labelParts.push(", ‘", overlayedRecordingLabel, "’ selected");
                    } else if (overlayedRecordingLabel === null) {
                        //labelParts.push(", updating selected recording");
                    } else {
                        //labelParts.push(", a problem with selected recording");
                    }
                }
                return labelParts.join("");
            },


            // =================================================================
            // dynamic derived config data

            __upateMethodOfDynamicDerivedConfigData: function() {
                if (this.attributes.dynamicDefinitionForCollection.attributes.errors) {
                    this.attributes.dynamicDefinitionForCollection.update(true);
                }
                if (this.attributes.dynamicDefinitionForOverlayedRecording.attributes.errors) {
                    this.attributes.dynamicDefinitionForOverlayedRecording.update(true);
                }
            },

            generateDynamicDerivedConfigData: function(config, configGrid) {
                var dynamicDefinitionForCollection         = App.dynamicDefinitionProviderForCollections.get(config);
                var dynamicDefinitionForOverlayedRecording = App.dynamicDefinitionProviderForRecordings.get(config);

                var dynamicDerivedConfigData = new RepresentationModule.DynamicDerivedConfigData({
                    dynamicDefinitionForCollection: dynamicDefinitionForCollection,
                    dynamicDefinitionForOverlayedRecording: dynamicDefinitionForOverlayedRecording
                });

                dynamicDerivedConfigData.listenTo(dynamicDefinitionForCollection, "change", function() {
                    dynamicDerivedConfigData.trigger("change:dynamicDefinitionForCollection");
                    dynamicDerivedConfigData.trigger("change");
                });
                dynamicDerivedConfigData.listenTo(dynamicDefinitionForOverlayedRecording, "change", function() {
                    dynamicDerivedConfigData.trigger("change:dynamicDefinitionForOverlayedRecording");
                    dynamicDerivedConfigData.trigger("change");
                });

                dynamicDerivedConfigData.update = this.__upateMethodOfDynamicDerivedConfigData;

                return dynamicDerivedConfigData;
            },


            // =================================================================
            // dynamic derived vis instance data


            // -----------------------------------------------------------------
            // dynamic derived vis instance data - base

            __optionsOfDynamicDerivedVisInstanceDataForBase: {
                attributesToExcludeFromHash: ["apiResponse"],
                customHashSuffixGenerator: function (attributes) {
                    if (attributes.apiResponse) {
                        return JSON.stringify(attributes.apiResponse.errors);
                    } else {
                        return typeof attributes.apiResponse;
                    }
                }
            },


            __upateMethodOfDynamicDerivedVisInstanceDataForBase: function(force) {
                var visInstanceView = this.options.visInstanceView;
                var dynamicDefinitionForCollection = visInstanceView.dynamicDerivedConfigDataForEntity.attributes.dynamicDefinitionForCollection;
                if (!dynamicDefinitionForCollection) { // entity kind has changed (e.g. a grid was reset)
                    return;
                }
                var collectionId = dynamicDefinitionForCollection.attributes.id;
                var requestParams = _.clone(visInstanceView.dynamicDerivedConfigDataForView.attributes.basePerspectiveRequestParams);

                if (!requestParams || !collectionId) {
                    this.set({
                        apiRequestURI:        undefined,
                        apiRequestParamsHash: undefined,
                        apiResponse:          undefined
                    });
                    return;
                }
                requestParams.cid = collectionId;

                var apiRequestParamsHash = JSON.stringify(requestParams);

                if (!force && apiRequestParamsHash == this.attributes.apiRequestParamsHash) {
                    return;
                }

                var _this = this;
                var apiRequestURI = App.DataModule.CliopatriaAPI.request("getCollectionPerspective", requestParams, function(data){
                    if (JSON.stringify(requestParams) != _this.attributes.apiRequestParamsHash) {
                        return;
                    }
                    _this.set({
                        apiRequestParamsHash: undefined,
                        apiResponse: data
                    });
                });

                this.set({
                    apiRequestURI: apiRequestURI,
                    apiRequestParamsHash: apiRequestParamsHash,
                    apiResponse: null
                });
            },


            generateDynamicDerivedVisInstanceDataForBase: function(visInstanceView) {
                var optionsForThisDynamicDerivedVisInstanceDataForBase = _.clone(this.__optionsOfDynamicDerivedVisInstanceDataForBase);
                optionsForThisDynamicDerivedVisInstanceDataForBase.visInstanceView = visInstanceView;

                var dynamicDerivedVisInstanceDataForBase = new RepresentationModule.DynamicDerivedVisInstanceData({
                    apiRequestURI:        undefined,
                    apiRequestParamsHash: undefined,
                    apiResponse:          undefined
                }, optionsForThisDynamicDerivedVisInstanceDataForBase);

                dynamicDerivedVisInstanceDataForBase.update = this.__upateMethodOfDynamicDerivedVisInstanceDataForBase;

                dynamicDerivedVisInstanceDataForBase.listenTo(visInstanceView.dynamicDerivedConfigDataForEntity, "change:dynamicDefinitionForCollection", dynamicDerivedVisInstanceDataForBase.update);
                dynamicDerivedVisInstanceDataForBase.listenTo(visInstanceView.dynamicDerivedConfigDataForView, "change:basePerspectiveRequestParams", dynamicDerivedVisInstanceDataForBase.update);

                dynamicDerivedVisInstanceDataForBase.update();

                return dynamicDerivedVisInstanceDataForBase;
            },


            verifyAllDataForVisInstanceBase: function(visInstanceView) {
                this._verifyThatViewIsNotEmptyOrUnknown(visInstanceView);

//                if (visInstanceView.options.entityConfig.getParameterValue("library") == "charm"
//                    && visInstanceView.options.viewConfig.getParameterValue("kind") == "key-relative-chord-seq") {
//                    throw new RepresentationModule.Error({type: "api-message_progress_base", derivedDataToUpdate: "base"});
//                   }


                var attributesOfDerivedConfigDataForEntity = visInstanceView.dynamicDerivedConfigDataForEntity.attributes;
                var dynamicDefinitionForCollection = attributesOfDerivedConfigDataForEntity.dynamicDefinitionForCollection;
                var attributesOfCollection = dynamicDefinitionForCollection ? dynamicDefinitionForCollection.attributes : {};

                if (attributesOfCollection.id === null) {
                    throw new RepresentationModule.Error({type: "data-preparing_entity-derived"});
                }
                if (attributesOfCollection.id === false) {
                    throw new RepresentationModule.Error({type: "api-error_entity-derived", apiErrors: attributesOfCollection.errors, coverTapAction: this.__coverTapActionThatUpdatesDynamicDerivedData, derivedDataToUpdate: "entity"});
                }
                if (attributesOfCollection.id === "") {
                    throw new RepresentationModule.Error({type: "collection_no-recordings"});
                }
                if (attributesOfCollection.id === undefined) {
                    throw new RepresentationModule.Error({type: "collection_undefined"});
                }

                var attribytesOfDerivedVisInstanceDataForBase = visInstanceView.dynamicDerivedVisInstanceDataForBase.attributes;
                if (!attribytesOfDerivedVisInstanceDataForBase.apiResponse) {
                    throw new RepresentationModule.Error({type: "data-preparing_base"});
                }
                if (attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors) {
                    if (attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors && attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors[0]) {
                        var error0 = attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors[0];
                        if (error0.code == 11 || error0.code == 12  || error0.code == 13) {

                            //FIXME this is a temp hack
                            var checkEvery = 2000;
                            var checkEveryRandomComponent = 500;
                            if (visInstanceView.autoRefreshTimeout) {
                                clearTimeout(visInstanceView.autoRefreshTimeout);
                            }
                            visInstanceView.autoRefreshTimeout = setTimeout(function() {
                                if (visInstanceView.dynamicDerivedVisInstanceDataForBase.attributes.apiResponse.errors) {
                                    var error0 = attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors[0];
                                    if (error0.code == 11 || error0.code == 12 || error0.code == 13) {
                                        visInstanceView.dynamicDerivedVisInstanceDataForBase.update();
                                    }
                                }
                            }, checkEvery + Math.round(Math.random() * checkEveryRandomComponent));
                            // END FIXME

                            throw new RepresentationModule.Error({type: "api-message_progress_base", apiErrors: attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors, coverTapAction: this.__coverTapActionThatUpdatesDynamicDerivedData, derivedDataToUpdate: "base"});

                        // FIXME errors like this should probably go to Master.view.xxx
                        } else if (error0.code == 20) {
                            throw new RepresentationModule.Error({type: "ok-count-0", apiErrors: attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors});
                        }
                    }
                    throw new RepresentationModule.Error({type: "api-error_base", apiErrors: attribytesOfDerivedVisInstanceDataForBase.apiResponse.errors, coverTapAction: this.__coverTapActionThatUpdatesDynamicDerivedData, derivedDataToUpdate: "base"});
                }

            },


            // -----------------------------------------------------------------
            // dynamic derived vis instance data - overlay


            // -----------------------------------------------------------------
            // dynamic derived vis instance data - temp

        });
    });
}, Logger);
