text: 'use strict';
  
  /*global require*/
  var URI = require('urijs');
  
  var clone = require('terriajs-cesium/Source/Core/clone');
  var defined = require('terriajs-cesium/Source/Core/defined');
  var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
  var freezeObject = require('terriajs-cesium/Source/Core/freezeObject');
  var knockout = require('terriajs-cesium/Source/ThirdParty/knockout');
  var loadJson = require('terriajs-cesium/Source/Core/loadJson');
  var objectToQuery = require('terriajs-cesium/Source/Core/objectToQuery');
  
  var AbsIttCatalogItem = require('./AbsIttCatalogItem');
  var CatalogGroup = require('./CatalogGroup');
  var inherit = require('../Core/inherit');
  var TerriaError = require('../Core/TerriaError');
  var proxyCatalogItemUrl = require('./proxyCatalogItemUrl');
  
  /**
   * A {@link CatalogGroup} representing a collection of items from an Australian Bureau of Statistics
   * (ABS) ITT server, formed by querying for all the codes in a given dataset and concept.
   *
   * @alias AbsIttCatalogGroup
   * @constructor
   * @extends CatalogGroup
   *
   * @param {Terria} terria The Terria instance.
   */
  var AbsIttCatalogGroup = function(terria) {
      CatalogGroup.call(this, terria, 'abs-itt-by-concept');
  
      /**
       * Gets or sets the URL of the ABS ITT API, typically http://stat.abs.gov.au/itt/query.jsp.
       * This property is observable.
       * @type {String}
       */
      this.url = undefined;
  
      /**
       * Gets or sets the filter for the ABS dataset.  You can obtain a list of all datasets by querying
       * http://stat.abs.gov.au/itt/query.jsp?method=GetDatasetList (or equivalent).  This property
       * is observable.
       * @type {String[]}
       */
      this.filter = undefined;
  
      /**
       * Gets or sets the styling for the abs data.  This property is observable.
       * @type {object}
       * @default undefined
       */
      // this.tableStyle = undefined;
  
      /**
       * Gets or sets the URL of a JSON file containing human-readable names of Australian Bureau of Statistics concept codes.
       * @type {String}
       */
      this.conceptNamesUrl = undefined;
  
      /**
       * Gets or sets the start of a URL of a csv file containing the total number of people in each region, eg.
       * SA4,Tot_P_M,Tot_P_F,Tot_P_P
       * 101,100000,23450,123450
       * 102,130000,100000,234560
       * The region code and '.csv' are appended to the end of this URL for the request, eg.
       * 'data/2011Census_TOT_' -> 'data/2011Census_TOT_SA4.csv' (and other region types).
       * @type {String}
       */
      this.regionPopulationsUrlPrefix = undefined;
  
      /**
       * Gets or sets a description of the custodian of the data sources in this group.
       * This property is an HTML string that must be sanitized before display to the user.
       * This property is observable.
       * @type {String}
       */
      this.dataCustodian = undefined;
  
      /**
       * Gets or sets a hash of names of blacklisted datasets.  A dataset that appears in this hash
       * will not be shown to the user.  In this hash, the keys should be the name of the dataset to blacklist,
       * and the values should be "true".  This property is observable.
       * @type {Object}
       */
      this.blacklist = undefined;
  
      /**
       * Gets or sets a hash of names of whitelisted datasets.  A dataset that doesn't appears in this hash
       * will not be shown to the user.  In this hash, the keys should be the name of the dataset to blacklist,
       * and the values should be "true".  This property is observable.
       * @type {Object}
       */
      this.whitelist = undefined;
  
      /**
       * Gets or sets a hash of properties that will be set on each child item.
       * For example, { 'treat404AsError': false }
       */
      this.itemProperties = undefined;
  
      knockout.track(this, ['url', 'filter', 'dataCustodian', 'blacklist', 'whitelist', 'itemProperties']);
  };
  
  inherit(CatalogGroup, AbsIttCatalogGroup);
  
  defineProperties(AbsIttCatalogGroup.prototype, {
      /**
       * Gets the type of data member represented by this instance.
       * @memberOf AbsIttCatalogGroup.prototype
       * @type {String}
       */
      type : {
          get : function() {
              return 'abs-itt-dataset-list';
          }
      },
  
      /**
       * Gets a human-readable name for this type of data source, such as 'Web Map Service (WMS)'.
       * @memberOf AbsIttCatalogGroup.prototype
       * @type {String}
       */
      typeName : {
          get : function() {
              return 'ABS.Stat Dataset List';
          }
      },
  
       /**
       * Gets the set of functions used to serialize individual properties in {@link CatalogMember#serializeToJson}.
       * When a property name on the model matches the name of a property in the serializers object literal,
       * the value will be called as a function and passed a reference to the model, a reference to the destination
       * JSON object literal, and the name of the property.
       * @memberOf AbsIttCatalogGroup.prototype
       * @type {Object}
       */
      serializers : {
          get : function() {
              return AbsIttCatalogGroup.defaultSerializers;
          }
      }
  });
  
  /**
   * Gets or sets the set of default serializer functions to use in {@link CatalogMember#serializeToJson}.  Types derived from this type
   * should expose this instance - cloned and modified if necesary - through their {@link CatalogMember#serializers} property.
   * @type {Object}
   */
  AbsIttCatalogGroup.defaultSerializers = clone(CatalogGroup.defaultSerializers);
  
  AbsIttCatalogGroup.defaultSerializers.items = CatalogGroup.enabledShareableItemsSerializer;
  
  freezeObject(AbsIttCatalogGroup.defaultSerializers);
  
  AbsIttCatalogGroup.prototype._getValuesThatInfluenceLoad = function() {
      return [this.url, this.blacklist, this.whitelist];
  };
  
  AbsIttCatalogGroup.prototype._load = function() {
      var baseUrl = cleanAndProxyUrl(this, this.url);
      var parameters = {
          method: 'GetDatasetList',
          format: 'json'
      };
  
      var that = this;
  
      var url = baseUrl + '?' + objectToQuery(parameters);
  
      return loadJson(url).then(function(json) {
          var datasets = json.datasets;
  
          var p;
  
          var blacklistWildcardTerms = [];
          for (p in that.blacklist) {
              if (that.blacklist.hasOwnProperty(p)) {
                  if (p[0] === '?') {
                      blacklistWildcardTerms.push(p.substring(1));
                  }
              }
          }
  
          var whitelistWildcardTerms = [];
          for (p in that.whitelist) {
              if (that.whitelist.hasOwnProperty(p)) {
                  if (p[0] === '?') {
                      whitelistWildcardTerms.push(p.substring(1));
                  }
              }
          }
  
          for (var i = 0; i < datasets.length - 1; ++i) {
              var dataset = datasets[i];
  
              var w;
  
                  //apply blacklist
              var blacklist = (defined(that.blacklist) && defined(that.blacklist[dataset.description]));
              for (w = 0; w < blacklistWildcardTerms.length && !blacklist; w++) {
                  if (dataset.id.indexOf(blacklistWildcardTerms[w]) !== -1) {
                      blacklist = true;
                  }
              }
                  //now apply whitelist
              for (w = 0; w < whitelistWildcardTerms.length && !blacklist; w++) {
                  if (dataset.id.indexOf(whitelistWildcardTerms[w]) === -1) {
                      blacklist = true;
                  }
              }
              if (defined(that.whitelist) && defined(that.whitelist[dataset.description])) {
                  blacklist = true;
              }
              if (blacklist) {
                  console.log('Provider Feedback: Filtering out ' + dataset.description + ' (' + dataset.id + ') because it is blacklisted.');
                  continue;
              }
  
              that.add(createItemForDataset(that, dataset));
          }
  
      }).otherwise(function(e) {
          console.log(e.message);
          throw new TerriaError({
              sender: that,
              title: 'Group is not available',
              message: '\
  An error occurred while invoking GetCodeListValue on the ABS ITT server.  \
  <p>This error may indicate that the group you opened is temporarily unavailable or there is a \
  problem with your internet connection.  Try opening the group again, and if the problem persists, please report it by \
  sending an email to <a href="mailto:'+that.terria.supportEmail+'">'+that.terria.supportEmail+'</a>.</p>'
          });
      });
  };
  
  function cleanAndProxyUrl(catalogGroup, url) {
      // Strip off the search portion of the URL
      var uri = new URI(url);
      uri.search('');
  
      var cleanedUrl = uri.toString();
      return proxyCatalogItemUrl(catalogGroup, cleanedUrl, '1d');
  }
  
  function createItemForDataset(absGroup, dataset) {
      var result = new AbsIttCatalogItem(absGroup.terria);
  
      result.name = dataset.description;
      result.description = dataset.description;
      result.dataCustodian = absGroup.dataCustodian;
      result.url = absGroup.url;
      result.datasetId = dataset.id;
      result.filter = absGroup.filter;
      result.conceptNamesUrl = absGroup.conceptNamesUrl;
      result.regionPopulationsUrlPrefix = absGroup.regionPopulationsUrlPrefix;
      // result.tableStyle = absGroup.tableStyle;  // TODO: do we need to be able to define TableStyle on the group?
  
      if (typeof(absGroup.itemProperties) === 'object') {
          result.updateFromJson(absGroup.itemProperties);
      }
  
      return result;
  }
  
  module.exports = AbsIttCatalogGroup;
  