Source: Models/RegionTypeParameter.js

'use strict';

/*global require*/
var defined = require('terriajs-cesium/Source/Core/defined');
var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
var knockout = require('terriajs-cesium/Source/ThirdParty/knockout');
var overrideProperty = require('../Core/overrideProperty');

var FunctionParameter = require('./FunctionParameter');
var inherit = require('../Core/inherit');
var RegionProvider = require('../Map/RegionProvider');
var RegionProviderList = require('../Map/RegionProviderList');

/**
 * A parameter that specifies a type of region.
 *
 * @alias RegionTypeParameter
 * @constructor
 * @extends FunctionParameter
 *
 * @param {Object} [options] Object with the following properties:
 * @param {Terria} options.terria The Terria instance.
 * @param {String} options.id The unique ID of this parameter.
 * @param {String} [options.name] The name of this parameter.  If not specified, the ID is used as the name.
 * @param {String} [options.description] The description of the parameter.
 * @param {String[]} [options.validRegionTypes] The region types from which this RegionTypeParameter selects.  If this parameter is not specified, all region types
 *                                              known to {@link Terria} may be selected.
 */
var RegionTypeParameter = function(options) {
    FunctionParameter.call(this, options);

    this._regionProviderPromise = undefined;
    this._regionProviderList = undefined;

    this.validRegionTypes = options.validRegionTypes;

    // Track this so that defaultValue can update once regionProviderList is known.
    knockout.track(this, ['_regionProviderList']);

    /**
     * Gets the default region provider if the user has not specified one.  If region-mapped data
     * is loaded on the map, this property returns the {@link RegionProvider} of the topmost
     * region-mapped catalog item.  Otherwise, it returns the first region provider.  If the
     * parameter has not yet been loaded, this property returns undefined.
     * @memberof RegionTypeParameter.prototype
     * @type {RegionProvider}
     */
    overrideProperty(this, 'defaultValue', {
        get: function() {
            if (defined(this._defaultValue)) {
                return this._defaultValue;
            }

            const nowViewingItems = this.terria.nowViewing.items;
            if (nowViewingItems.length > 0) {
                for (let i = 0; i < nowViewingItems.length; ++i) {
                    const item = nowViewingItems[i];
                    if (defined(item.regionMapping) && defined(item.regionMapping.regionDetails) && item.regionMapping.regionDetails.length > 0) {
                        return item.regionMapping.regionDetails[0].regionProvider;
                    }
                }
            }
            if (defined(this._regionProviderList) && this._regionProviderList.length > 0) {
                return this._regionProviderList[0];
            }

            // No defaults available; have we requested the region providers yet?
            this.load();
            return undefined;
        }
    });
};

/**
 * Resolves value that may be either a {@link RegionProvider} or a {@link RegionTypeParameter}
 * to a {@link RegionProvider}.
 * @param {RegionProvider|RegionTypeParameter} regionProviderOrParameter The region provider or parameter.
 * @return {RegionProvider} If `regionProviderOrParameter` is a {@link RegionProvider}, that value is returned.  If it is a
 *         {@link RegionTypeParameter}, the value of the parameter is returned.
 *         The return value may be undefined if `regionProviderOrParameter` is undefined, or if the value of the
 *         region type parameter is undefined.
 */
RegionTypeParameter.resolveRegionProvider = function(regionProviderOrParameter) {
    if (regionProviderOrParameter instanceof RegionProvider) {
        return regionProviderOrParameter;
    } else if (regionProviderOrParameter instanceof RegionTypeParameter) {
        return regionProviderOrParameter.value;
    } else {
        return undefined;
    }
};

inherit(FunctionParameter, RegionTypeParameter);

defineProperties(RegionTypeParameter.prototype, {
    /**
     * Gets the type of this parameter.
     * @memberof RegionTypeParameter.prototype
     * @type {String}
     */
    type: {
        get: function() {
            return 'regionType';
        }
    },

    /**
     * Gets or sets the value of this parameter.
     * @memberof RegionTypeParameter.prototype
     * @member {RegionProvider} value
     */
});

RegionTypeParameter.prototype._load = function() {
    return this.getAllRegionTypes();
};

/**
 * Gets all list of region types that may be selected for this parameter.
 * Also caches the promise in this._regionProviderPromise, and the promise result in
 * this._regionProviderList.
 * @return {Promise.<RegionProvider[]>} [description]
 */
RegionTypeParameter.prototype.getAllRegionTypes = function() {
    var that = this;
    if (defined(this._regionProviderPromise)) {
        return this._regionProviderPromise;
    }
    this._regionProviderPromise = RegionProviderList.fromUrl(this.terria.configParameters.regionMappingDefinitionsUrl, this.terria.corsProxy).then(function(regionProviderList) {
        var result;
        if (!defined(that.validRegionTypes)) {
            result = regionProviderList.regionProviders;
        } else {
            result = regionProviderList.regionProviders.filter(function(regionProvider) {
                return that.validRegionTypes.indexOf(regionProvider.regionType) >= 0;
            });
        }
        // Filter out region types that don't have a WMS server associated (e.g. those that are purely vector tile)
        result = result.filter(function(regionProvider) {
            return defined(regionProvider.analyticsWmsServer);
        });
        that._regionProviderList = result;
        return result;
    });
    return this._regionProviderPromise;
};

module.exports = RegionTypeParameter;