'use strict';

define(
  [
    'jquery',
    'underscore',
    'backbone',
    'models/maps/assets/MapAsset',
    'models/maps/assets/Cesium3DTileset',
    'models/maps/assets/CesiumVectorData',
    'models/maps/assets/CesiumImagery',
    'models/maps/assets/CesiumTerrain',
    'models/maps/assets/CesiumGeohash'
  ],
  function (
    $,
    _,
    Backbone,
    MapAsset,
    Cesium3DTileset,
    CesiumVectorData,
    CesiumImagery,
    CesiumTerrain,
    CesiumGeohash
  ) {

    /**
     * @class MapAssets
     * @classdesc A MapAssets collection is a group of MapAsset models - models that
     * provide the information required to render geo-spatial data on a map, including
     * imagery (raster), vector, and terrain data.
     * @class MapAssets
     * @classcategory Collections/Maps
     * @extends Backbone.Collection
     * @since 2.18.0
     * @constructor
     */
    var MapAssets = Backbone.Collection.extend(
      /** @lends MapAssets.prototype */ {

        /**
         * Creates the type of Map Asset based on the given type. This function is
         * typically not called directly. It is used by Backbone.js when adding a new
         * model to the collection.
         * @param {MapConfig#MapAssetConfig} assetConfig - An object that configured the
         * source the asset data, as well as metadata and display properties of the asset.
         * @returns {(Cesium3DTileset|CesiumImagery|CesiumTerrain|CesiumVectorData)}
         * Returns a MapAsset model
        */
        model: function (assetConfig) {
          try {

            // Supported types: Matches each 'type' attribute to the appropriate MapAsset
            // model. See also CesiumWidgetView.mapAssetRenderFunctions
            var mapAssetTypes = [
              {
                types: ['Cesium3DTileset'],
                model: Cesium3DTileset
              },
              {
                types: ['GeoJsonDataSource'],
                model: CesiumVectorData
              },
              {
                types: ['BingMapsImageryProvider', 'IonImageryProvider', 'WebMapTileServiceImageryProvider', 'TileMapServiceImageryProvider', 'WebMapServiceImageryProvider', 'NaturalEarthII', 'USGSImageryTopo'],
                model: CesiumImagery
              },
              {
                types: ['CesiumTerrainProvider'],
                model: CesiumTerrain
              },
              {
                types: ['CesiumGeohash'],
                model: CesiumGeohash
              }
            ];
              
            var type = assetConfig.type
            var modelOption = _.find(mapAssetTypes, function (option) {
              return option.types.includes(type)
            })

            // Don't add an unsupported type to  the collection
            if (modelOption) {
              return new modelOption.model(assetConfig)
            } else {
              // Return a generic MapAsset as a default
              return new MapAsset(assetConfig)
            }

          }
          catch (error) {
            console.log(
              'Failed to add a new model to a MapAssets collection' +
              '. Error details: ' + error
            );
          }
        },

        /**
         * Executed when a new MapAssets collection is created.
         */
        initialize: function () {
          try {

            // Only allow one Map Asset in the collection to be selected at a time. When a
            // Map Asset model's 'selected' attribute is changed to true, change all of the
            // other models' selected attributes to false.
            this.stopListening(this, 'change:selected');
            this.listenTo(this, 'change:selected', function (changedAsset, newValue) {
              if (newValue === true) {
                var otherModels = this.reject(function (assetModel) {
                  return assetModel === changedAsset
                })
                otherModels.forEach(function (otherModel) {
                  otherModel.set('selected', false)
                })
              }
            })
          }
          catch (error) {
            console.log(
              'There was an error initializing a MapAssets collection' +
              '. Error details: ' + error
            );
          }
        },

        /**
         * Set the parent map model on each of the MapAsset models in this collection.
         * This must be the Map model that contains this asset collection.
         * @param {MapModel} mapModel The map model to set on each of the MapAsset models
         */
        setMapModel: function (mapModel) {
          try {
            this.each(function (mapAssetModel) {
              mapAssetModel.set('mapModel', mapModel)
            })
          }
          catch (error) {
            console.log(
              'Failed to set the map model on a MapAssets collection' +
              '. Error details: ' + error
            );
          }
        },

        /**
         * Get a list of MapAsset models from this collection that are of a
         * given type.
         * @param {'Cesium3DTileset'|'CesiumVectorData'|'CesiumImagery'|'CesiumTerrain'} assetType -
         * The general type of asset to filter the collection by.
         * @returns {MapAsset[]} - Returns an array of MapAsset models that are
         * instances of the given asset type.
         * @since 2.22.0
         */
        getAll: function (assetType) {
          try {
            // map strings to the models they represent
            var assetTypeMap = {
              'Cesium3DTileset': Cesium3DTileset,
              'CesiumVectorData': CesiumVectorData,
              'CesiumImagery': CesiumImagery,
              'CesiumTerrain': CesiumTerrain
            }
            if (assetType) {
              return this.filter(function (assetModel) {
                return assetModel instanceof assetTypeMap[assetType]
              })
            } else {
              return this.models
            }
          }
          catch (error) {
            console.log(
              'Failed to get all of the MapAssets in a MapAssets collection' +
              '. Error details: ' + error +
              '\n\n' +
              'Returning all models in the asset collection.'
            );
            return this.models
          }
        }

      }
    );

    return MapAssets;

  }
);