(function (context, $) {

  "use strict";

  context.JK = context.JK || {};

  context.JK.SelectLocation = Class.extend({

    init: function ($countries, $regions, $cities, app, useEasyDropdown) {
      this.api = context.JK.Rest();
      this.logger = context.JK.logger;
      this.loadingCitiesData = false;
      this.loadingRegionsData = false;
      this.loadingCountriesData = false;
      this.nilOptionStr = '<option value=""></option>';
      this.nilOptionText = 'n/a';
      this.countriesLoaded = false;
      this.$countries = $countries;
      this.$regions = $regions;
      this.$cities = $cities;
      this.$deferred = null;
      this.useEasyDropdown = useEasyDropdown === undefined ? true : useEasyDropdown;
      this.app = app;

      $countries.on('change', function (evt) {
        evt.stopPropagation();
        this.handleCountryChanged();
        return false;
      }.bind(this));
      if($regions) {
        $regions.on('change', function (evt) {
          evt.stopPropagation();
          this.handleRegionChanged();
          return false;
        }.bind(this));
      }
    },
    selectCountry: function (country) {
      this.$countries.val(country)
    },
    ready: function() {
      return this.$deferred;
    },
    load: function (country, region, city) {

      this.country = country;
      this.region = region;
      this.city = city;

      if (!country) {
        // this case shouldn't happen because sign up makes you pick a location.  This is just 'in case', so that the UI is more error-resilient
        this.logger.debug("user has no specified country: " + country)
        country = 'US';
      }

      // make the 3 slower requests, which only matter if the user wants to affect their ISP or location
      this.loadingCountriesData = true;
      this.$deferred = this.api.getCountries()
        .done(function (countriesx) {
          this.populateCountriesx(countriesx["countriesx"], country);
        }.bind(this))
        .fail(this.app.ajaxError)
        .always(function () {
          this.loadingCountriesData = false;
        }.bind(this))


      if (country && this.$regions) {
        this.loadingRegionsData = true;
        this.api.getRegions({ country: country })
          .done(function (regions) {
            this.populateRegions(regions["regions"], region);
          }.bind(this))
          .fail(this.regionListFailure.bind(this))
          .always(function () {
            this.loadingRegionsData = false;
          }.bind(this))

        if (region && this.$cities) {
          this.loadingCitiesData = true;
          this.api.getCities({ country: country, region: region })
            .done(function (cities) {
              this.populateCities(cities["cities"], this.city)
            }.bind(this))
            .fail(this.cityListFailure.bind(this))
            .always(function () {
              this.loadingCitiesData = false;
            }.bind(this))
        }
      }
      return this.$deferred;
    },
    handleCountryChanged: function () {
      var selectedCountry = this.$countries.val()

      if(!this.$regions) {
        return;
      }

      var selectedRegion = this.$regions.val()
      var cityElement = this.$cities

      this.updateRegionList(selectedCountry, this.$regions);
      this.updateCityList(selectedCountry, null, cityElement);
    },
    handleRegionChanged: function () {
      var selectedCountry = this.$countries.val()
      var selectedRegion = this.$regions.val()
      var cityElement = this.$cities;

      this.updateCityList(selectedCountry, selectedRegion, cityElement);
    },
    updateRegionList: function (selectedCountry, regionElement) {
      // only update region
      if (selectedCountry) {
        // set city disabled while updating
        regionElement.attr('disabled', true)
        this.loadingRegionsData = true;

        regionElement.children().remove()
        regionElement.append($(this.nilOptionStr).text('loading...'))

        this.api.getRegions({ country: selectedCountry })
          .done(this.getRegionsDone.bind(this))
          .error(function (err) {
            regionElement.children().remove()
            regionElement.append($(this.nilOptionStr).text(this.nilOptionText))
          }.bind(this))
          .always(function () {
            console.log("regions load: this.loadingRegionsData; " + this.loadingRegionsData)
            this.loadingRegionsData = false;
          }.bind(this))
      }
      else {
        regionElement.children().remove()
        regionElement.append($(this.nilOptionStr).text(this.nilOptionText))
      }
    },
    updateCityList: function (selectedCountry, selectedRegion, cityElement) {

      // only update cities
      if (selectedCountry && selectedRegion) {
        // set city disabled while updating
        cityElement.attr('disabled', true)
        this.loadingCitiesData = true;

        cityElement.children().remove()
        cityElement.append($(this.nilOptionStr).text('loading...'))

        this.api.getCities({ country: selectedCountry, region: selectedRegion })
          .done(this.getCitiesDone.bind(this))
          .error(function (err) {
            cityElement.children().remove()
            cityElement.append($(this.nilOptionStr).text(this.nilOptionText))
          }.bind(this))
          .always(function () {
            this.loadingCitiesData = false;
          }.bind(this))
      }
      else {
        cityElement.children().remove();
        cityElement.append($(this.nilOptionStr).text(this.nilOptionText));
      }
    },

    getCitiesDone: function (data) {
      this.populateCities(data['cities'], this.city);
    },
    getRegionsDone: function (data) {
      this.populateRegions(data['regions'], this.region);
      this.updateCityList(this.$countries.val(), this.$regions.val(), this.$cities);
    },
    writeCountry: function (index, countryx) {
      if (!countryx.countrycode) return;

      var option = $(this.nilOptionStr);
      option.text(countryx.countryname ? countryx.countryname : countryx.countrycode);
      option.attr("value", countryx.countrycode);

      if (countryx.countrycode == this.country) {
        this.foundCountry = true;
      }

      this.$countries.append(option);
    },
    populateCountriesx: function (countriesx) {

      this.countriesLoaded = true;

      // countriesx has the format [{countrycode: "US", countryname: "United States"}, ...]

      this.foundCountry = false;
      this.$countries.children().remove();

      var nilOption = $(this.nilOptionStr);
      nilOption.text(this.nilOptionText);
      this.$countries.append(nilOption);

      $.each(countriesx, this.writeCountry.bind(this));

      if (!this.foundCountry) {
        this.logger.warn("user has no country in the database. user's country:" + this.country)
        // in this case, the user has a country that is not in the database
        // this can happen in a development/test scenario, but let's assume it can
        // happen in production too.
        var option = $(this.nilOptionStr);
        option.text(this.country);
        option.attr("value", this.country);
        this.$countries.append(option);
      }

      this.$countries.val(this.country);
      this.$countries.attr("disabled", null)
    },

    writeRegion: function (index, region) {
      if (!region) return;

      var option = $(this.nilOptionStr)
      option.text(region['name'])
      option.attr("value", region['region'])

      this.$regions.append(option)
    },

    populateRegions: function (regions, userRegion) {
      this.$regions.children().remove()

      var nilOption = $(this.nilOptionStr);
      nilOption.text(this.nilOptionText);
      this.$regions.append(nilOption);

      $.each(regions, this.writeRegion.bind(this))

      this.$regions.val(userRegion)
      this.$regions.attr("disabled", null)
    },

    writeCity: function (index, city) {
      if (!city) return;

      var option = $(this.nilOptionStr)
      option.text(city)
      option.attr("value", city)

      this.$cities.append(option)
    },

    populateCities: function (cities, userCity) {
      this.$cities.children().remove();

      var nilOption = $(this.nilOptionStr);
      nilOption.text(this.nilOptionText);
      this.$cities.append(nilOption);

      $.each(cities, this.writeCity.bind(this))

      this.$cities.val(userCity)
      this.$cities.attr("disabled", null)
    },

    regionListFailure: function (jqXHR, textStatus, errorThrown) {
      if (jqXHR.status == 422) {
        this.logger.debug("no regions found for country: " + this.country);
      }
      else {
        this.app.ajaxError(arguments);
      }
    },

    cityListFailure: function (jqXHR, textStatus, errorThrown) {
      if (jqXHR.status == 422) {
        this.logger.debug("no cities found for country/region: " + this.country + "/" + this.region);
      }
      else {
        this.app.ajaxError(arguments);
      }
    }
  });

})(window, jQuery);

