/**
 * Photo Tours Map Search
 */

(function () {
  /**
   * @class MapSearch Parent map search module
   */
  var MapSearch = {
    config: {
      NUM_RESULTS: 300,
      IFRAME_MAX_HEIGHT: 700,
      DEDICATED_PAGE: false
    },

    init: function () {
      this._resultSet = new MapSearch.ResultSet({
        currentPage: 1,
        numResults: 69
      });
      this.criteria = new MapSearch.Criteria();
      this.bindEvents();
      MapSearch.window.iframeShim();
    },

    bindEvents: function () {
      document.observe('keyup', this.closeWindowOnEsc);

      if ($('searchContent')) {
        $('map').observe('click', this.toggleFullscreenButton);
      } else if ($('map-search-page')) {
        $('map-search-window').show();
        MapSearch.window.open();
      } else {
        $('fullscreen-toggle').observe('click', this.toggleFullscreenButton);
      }

      $('map-search-window').observe('click', this.delegateListingClicks);
      $('map-search-panel-handle').observe('click', this.togglePanel);
      this.bindMapSearchButtons();
    },

    bindMapSearchButtons: function () {
      var closeWindow = MapSearch.window.close.bind(MapSearch.window);
      $$('.close-map-search').each(function (button) {
        button.observe('click', closeWindow);
      });

      var self = this;
      $('map-search-reset-button').observe('click', function () {
        $('map-search-form').reset();
        self.criteria.reset();
      });

      $$('.clear-search-criteria').each(function (el) {
        $(el).observe('click', function (event) {
          Event.stop(event);
          self.criteria.reset();
          $('SearchForm').reset();
        });
      });
    },

    toggleFullscreenButton: function (event) {
      event.preventDefault();
      MapSearch.window.toggle();
    },

    closeWindowOnEsc: function (event) {
      if (MapSearch.config.DEDICATED_PAGE) { return; }
      if (event.keyCode == Event.KEY_ESC && MapSearch.window.isOpen()) {
        MapSearch.window.close();
      }
    },

    togglePanel: function (event) {
      event.preventDefault();
      MapSearch.panel.toggle();
    },

    delegateListingClicks: function (event) {
      var elem = $(event.originalTarget || event.target),
          win, url;
      if (!elem) { return; }
      if (elem.hasClassName('googlemaptext')) {
        event.preventDefault();
        url = elem.readAttribute('href');
        if (url === null) {
          url = elem.up('a.googlemaptext').readAttribute('href');
        }
        MapSearch.detailWindow = new MapSearch.ListingDetailsWindow(url);
      }
    }
  };

  /**
   * @class MapSearch.Criteria
   */
  MapSearch.Criteria = Class.create({
    initialize: function () {
      this.bindQuickSearchEvents();
      this.bindMapSearchEvents();
      this.getCriteria();
      this.sync('quickSearch');
    },

    criteriaFields: [
      'type',
      'city',
      'neighborhood',
      'price-from',
      'price-to',
      'bedrooms',
      'bathrooms',
      'acres-from',
      'acres-to',
      'units-from',
      'units-to',
      'sale-lease',
      'mls_num',
      'address_street_num',
      'address_street_name'
    ],

    _cachedCriteria: {},

    bindQuickSearchEvents: function () {
      this.bindEvents('quickSearch');
      this.bindEvents('SearchForm');
    },

    bindMapSearchEvents: function () {
      this.bindEvents('map-search-form');
    },

    getCriteria: function (context) {
      var that = this;
      context = context === 'SearchForm' ? 'SearchForm' : 'quickSearch';
      this.criteriaFields.each(function (field) {
        var el = $$(that.elemSelector(context, field));
        if (el.length > 0) { that.updateSelectCriteria(context, field); }
      });
    },

    bindEvents: function (context) {
      var that  = this;
      this.criteriaFields.each(function (elem) {
        var handler  = that.updateSelectCriteria.bind(that, context, elem);
        var el = that.formElem(context, elem);
        if (el !== false) { el.observe('change', handler); }
      });
    },

    elemSelector: function (context, elem) {
      return '#' + context + ' .criteria-' + elem;
    },

    textFieldSelector: function (context, elem) {
      var sel = '.' + context + '.criteria-' + elem;
      return sel;
    },

    formElem: function (context, elem) {
      var regSel = this.elemSelector(context, elem),
          txtSel = this.textFieldSelector(context, elem),
          regEl  = $$(regSel),
          txtEl  = $$(txtSel);
      if (regEl.length || txtEl.length) {
        return regEl.length > 0 ? regEl[0] : txtEl[0];
      } else {
        return false;
      }
    },

    updateSelectCriteria: function (context, type) {
      var elem = this.formElem(context, type);
      var tagName = elem.tagName.toLowerCase();
      var selected = [];

      if (tagName == 'select') {
        elem.childElements().each(function (e) {
          if (e.selected) { selected.push(e.value); }
        });
      } else if (tagName == 'input') {
        selected.push(elem.value);
      } else {
        elem.select('input').each(function (e) {
          if (e.checked) { selected.push(e.value); }
        });
      }

      this._cachedCriteria[type] = selected;
    },

    sync: function (context) {
      var that = this, updateField;

      if (context !== 'SearchForm') {
        this.getCriteria('SearchForm');
        this.clearInputCriteriaOnResultsPage();
        updateField = this.updateQuickSearchFields.bind(this);
      } else {
        updateField = this.updateSearchFields.bind(this);
      }

      $H(this._cachedCriteria).each(function (pair) {
        updateField(context, pair.key, pair.value);
      });
    },

    reset: function () {
      var self = this;
      $H(this._cachedCriteria).each(function (pair) {
        self._cachedCriteria[pair.key] = [];
      });
    },

    clearInputCriteriaOnResultsPage: function () {
      var onResultsPage = $$('#SearchForm').length === 0;
      if (onResultsPage) {
        this._cachedCriteria.mls_num = [""];
        this._cachedCriteria.address_street_num = [""];
        this._cachedCriteria.address_street_name = [""];
      }
    },

    updateQuickSearchFields: function (context, field, values) {
      var el = this.formElem(context, field);
      if (el === false) { return; }
      if (el.children.length > 0) {
        el.childElements().each(this.selectOption.bind(this, values));
      } else {
        el.value = values.first();
      }
    },

    updateSearchFields: function (context, field, values) {
      var el = this.formElem(context, field);
      if (el === false) { return; }

      var tagName = el.tagName.toLowerCase();
      if (tagName == 'select') {
        this.updateSelectOptions(el, values);
      } else if (tagName == 'input') {
        el.value = values.first();
      } else {
        this.updateCheckboxOptions(el, values);
      }
    },

    updateSelectOptions: function (el, values) {
      el.childElements().each(this.selectOption.bind(this, values));
    },

    updateCheckboxOptions: function (el, values) {
      el.select('input').each(this.checkOption.bind(this, values));
    },

    selectOption: function (values, self) {
      var shouldBeSelected = (values.indexOf(self.value) != -1);
      self.selected = shouldBeSelected;
    },

    checkOption: function (values, self) {
      var shouldBeSelected = (values.indexOf(self.value) != -1);
      self.checked = shouldBeSelected;
    }
  });


  /**
   * @class MapSearch.ResultSet
   */
  MapSearch.ResultSet = Class.create({
    initialize: function (attrs) {
      this.update(attrs);
    },

    update: function (attrs) {
      var resultsPerPage = MapSearch.config.NUM_RESULTS;

      if (attrs.currentPage) {
        this.currentPage = attrs.currentPage;
      }

      if (attrs.numResults)  {
        this.numResults = attrs.numResults;
        this.maxPages = Math.ceil(attrs.numResults/resultsPerPage);
      }
    },

    nextPage: function () {
      if (this.currentPage >= this.maxPages) { return; }
      this.currentPage += 1;
      this.updateMessageArea();
      this.updateButtonStates();
      this.updateMap();
    },

    prevPage: function () {
      if (this.currentPage == 1) { return; }
      this.currentPage -= 1;
      this.updateMessageArea();
      this.updateButtonStates();
      this.updateMap();
    },

    priceAsc: function () {
      this.sort = 'pa';
      this.updateSortingState();
      this.updateMap();
    },

    priceDesc: function () {
      this.sort = 'pd';
      this.updateSortingState();
      this.updateMap();
    },

    updateMap: function () {
      PT.Gmap.markers.clearAll();
      MapSearch.window._map.update(this.currentPage, this.sort);
    },

    updateButtonStates: function () {
      this.nextButtonToggleDisabled();
      this.prevButtonToggleDisabled();
    },

    updateSortingState: function () {
      var ascButton = $('map-search-control-price-asc-button');
      var descButton = $('map-search-control-price-desc-button');

      descButton.removeClassName('enabled');
      ascButton.removeClassName('enabled');

      if (this.sort == 'pd' || this.sort === undefined) {
        descButton.addClassName('enabled');
      } else {
        ascButton.addClassName('enabled');
      }

      this.toggleSortingVisibility([ascButton, descButton]);
    },

    toggleSortingVisibility: function (elems) {
      var shouldShow = this.numResults > MapSearch.config.NUM_RESULTS;
      this.showSortingControls(elems, shouldShow);
    },

    showSortingControls: function (controls, shouldShow) {
      if (shouldShow) {
        controls.invoke('show');
      } else {
        controls.invoke('hide');
      }
    },

    nextButtonToggleDisabled: function () {
      var elem = $('map-search-control-next-button');
      if (this.currentPage == this.maxPages) {
        elem.addClassName('disabled');
      } else {
        elem.removeClassName('disabled');
      }
    },

    prevButtonToggleDisabled: function () {
      var elem = $('map-search-control-prev-button');
      if (this.currentPage == 1) {
        elem.addClassName('disabled');
      } else {
        elem.removeClassName('disabled');
      }
    },

    updateMessageArea: function () {
      var elem = $('map-search-control-message-area');
      var message = "Showing #{currentRange} of #{numResults} listings.";
      message = message.interpolate({
        currentRange: this.currentRange(), 
        numResults:   this.numResults   
      });
      elem.innerHTML = message;
    },

    currentRange: function () {
      var resultsPerPage = MapSearch.config.NUM_RESULTS;
      var floor, ceil;

      resultsPerPage =
        this.numResults > resultsPerPage ?  resultsPerPage : this.numResults;

      if (this.currentPage == 1) {
        floor = 1;
        ceil  = resultsPerPage;
      } else if (this.currentPage == this.maxPages) {
        floor = (this.currentPage - 1) * resultsPerPage + 1;
        ceil  = this.numResults;
      } else {
        floor = (this.currentPage - 1) * resultsPerPage + 1;
        ceil  = this.currentPage * resultsPerPage;
      }

      return floor + " - " + ceil;
    }
  });

  /**
   * @class MapSearch.Map
   */
  MapSearch.Map = Class.create({
    MAP_CONTAINER: 'map-search-map',
    options: {
      mapTypeControlOptions: {
        position: google.maps.ControlPosition.RIGHT_TOP
      },
      panControlOptions: {
        position: google.maps.ControlPosition.RIGHT_CENTER
      },
      zoomControlOptions: {
        position: google.maps.ControlPosition.RIGHT_CENTER
      },
      markerOptions: {
      }
    },

    initialize: function () {
      this._map = PT.Gmap.initialize({
        mapid:      this.MAP_CONTAINER,
        xmlUrl:     this.xmlUrl(),
        resultSet:  MapSearch._resultSet,
        mapOptions: this.options
      });
      var pagination = new MapSearch.PaginationControl(this._map);
      var closeMap   = new MapSearch.CloseMapControl(this._map);
      pagination.appendToMap();
      closeMap.appendToMap();
    },

    destroy: function () {
      $(this.MAP_CONTAINER).innerHTML = '';
    },

    xmlUrl: function () {
      return [
        PT.Gmap.currentXmlUrl,
        "&numResults=",
        MapSearch.config.NUM_RESULTS
      ].join('');
    },

    update: function (page, sort) {
      page = (page !== undefined) ? '&page=' + (page - 1) : '';
      sort = (sort !== undefined) ? '&sort=' + sort : '&sort=pd';
      PT.Gmap.repopulateGoogleMap(this.xmlUrl() + page + sort);
    }
  });

  /**
   * @class MapSearch.CloseMapControl
   */
  MapSearch.CloseMapControl = Class.create({
    POSITION: google.maps.ControlPosition.TOP_RIGHT,

    initialize: function (map) {
      if (!map) { throw Error('Missing required argument: map'); }
      this._map = map;
      this._wrapperDiv = new Element('div');
      this.updateStyle();
      this.addButtons();
      this.delegateEvents();
    },

    updateStyle: function () {
      this._wrapperDiv.setStyle({
        width: '32px',
        margin: '8px 2px 32px 0',
        height: '24px'
      });
    },

    addButtons: function () {
      this._wrapperDiv.insert({bottom: this.control() });
    },

    control: function () {
      var self = new Element('div', {
        'id': 'map-search-control-close-window',
        'class': 'map-search-control-button'
      });
      self.innerHTML = "&times;";
      return self;
    },

    delegateEvents: function () {
      var closeWindow = MapSearch.window.close.bind(MapSearch.window);
      this._wrapperDiv.observe('click', closeWindow);
    },

    appendToMap: function () {
      if (MapSearch.config.DEDICATED_PAGE) { return; }
      this._map.controls[this.POSITION].push(this._wrapperDiv);
    }
  });

  /**
   * @class MapSearch.PaginationControl
   */
  MapSearch.PaginationControl = Class.create({
    POSITION: google.maps.ControlPosition.TOP_CENTER,

    initialize: function (map) {
      if (!map) { throw Error('Missing required argument: map'); }
      this._map = map;
      this._wrapperDiv = new Element('div');
      this.updateStyle();
      this.addButtons();
      this.delegateEvents();
    },

    updateStyle: function () {
      this._wrapperDiv.setStyle({
        width: '450px',
        margin: '8px 0 0'
      });
    },

    addButtons: function () {
      this._wrapperDiv.insert({bottom: this.messageArea() });
      this._wrapperDiv.insert({bottom: this.prevButton() });
      this._wrapperDiv.insert({bottom: this.nextButton() });
      this._wrapperDiv.insert({bottom: this.priceAscButton() });
      this._wrapperDiv.insert({bottom: this.priceDescButton() });
    },

    messageArea: function () {
      var self = new Element('div', {
        'id': 'map-search-control-message-area',
        'class': 'map-search-control-button'
      });
      self.setStyle({
        width: '235px',
        position: 'absolute',
        top: '0',
        left: '0'
      });
      self.innerHTML = "Loading your search results...";
      return self;
    },

    prevButton: function () {
      var self = new Element('div', {
        'id': 'map-search-control-prev-button',
        'class': 'disabled map-search-control-button'
      });

      if (Prototype.Browser.IE) {
        self.innerHTML = "&#9668;";
      } else {
        self.innerHTML = "&#9664;";
      }

      this._prevButton = self;
      return self;
    },

    nextButton: function () {
      var self = new Element('div', {
        'id': 'map-search-control-next-button',
        'class': 'map-search-control-button'
      });
      self.innerHTML = "&#9658;";

      this._nextButton = self;
      return self;
    },

    priceAscButton: function () {
      var self = new Element('div', {
        'id': 'map-search-control-price-asc-button',
        'class': 'map-search-control-button'
      });
      self.innerHTML = "$ &#9650;";

      this._priceAscButton = self;
      return self;
    },

    priceDescButton: function () {
      var self = new Element('div', {
        'id': 'map-search-control-price-desc-button',
        'class': 'map-search-control-button'
      });
      self.innerHTML = "$ &#9660;";

      this._priceDescButton = self;
      return self;
    },

    delegateEvents: function () {
      var resultSet = MapSearch._resultSet;
      this._nextButton.observe('click', resultSet.nextPage.bind(resultSet));
      this._prevButton.observe('click', resultSet.prevPage.bind(resultSet));
      this._priceAscButton.observe('click', resultSet.priceAsc.bind(resultSet));
      this._priceDescButton.observe('click', resultSet.priceDesc.bind(resultSet));
    },

    appendToMap: function () {
      this._map.controls[this.POSITION].push(this._wrapperDiv);
    }
  });

  /**
   * @class MapSearch.window The fullscreen window that the entire map search
   * lives in
   */
  MapSearch.window = {
    _isOpen: false,
    IFRAME_CLASS: "iframed",

    el: function () {
      var el = $('map-search-window');
      this.el = function () { return el; };
      return el;
    },

    toggle: function () {
      return !this._isOpen ? this.open() : this.close();
    },

    isOpen: function () {
      return this._isOpen;
    },

    iframeShim: function () {
      if (window.self == window.top) { return; }
      if (!this.el().hasClassName(this.IFRAME_CLASS)) {
        this.el().addClassName(this.IFRAME_CLASS);
      }
    },

    isIFramed: function () {
      return this.el().hasClassName(this.IFRAME_CLASS);
    },

    setIFrameHeight: function () {
      var MAX_HEIGHT = MapSearch.config.IFRAME_MAX_HEIGHT;
      $('map-search-window').setStyle({ height: MAX_HEIGHT + "px" });
    },

    open: function () {
      this._isOpen = true;
      if (!MapSearch.config.DEDICATED_PAGE) {
          this._animateOpen();
      } else {
          this._addGoogleMap();
      }
      MapSearch.criteria.sync('map-search-form');
      MapSearch.panel.initialize();
      MapSearch.panel.doSearch();
      this.invokeDocScrollingPolicy();
    },

    close: function () {
      this._isOpen = false;
      if (!MapSearch.config.DEDICATED_PAGE) { this._animateClose(); }
      this._map.destroy();
      this.removeDocScrollingPolicy();

      if ($('searchContent') === null) {
        MapSearch.criteria.sync('quickSearch');
        var url = PT.Gmap.currentXmlUrl;
        url  = url.replace(/&numResults=300/g, '');
        url += '&numResults=10';
        PT.Gmap.initialize({
          mapid: 'map',
          xmlUrl: url
        });
        loadResults(url);
      } else {
        MapSearch.criteria.sync('SearchForm');
      }
    },

    invokeDocScrollingPolicy: function () {
      if (Prototype.Browser.IE) {
        Event.observe(window, 'scroll', this.maintainViewport);
      }
    },

    removeDocScrollingPolicy: function () {
      if (Prototype.Browser.IE) {
        Event.stopObserving(window, 'scroll', this.maintainViewport);
      }
    },

    maintainViewport: function () {
      $('map-search-window').setStyle({ top: document.body.scrollTop + 'px' });
    },

    _animateOpen: function () {
      var maxHeight = MapSearch.config.MAX_HEIGHT;
      this.el().grow({
        afterFinish: function () {
          this._addGoogleMap();
          if (this.isIFramed()) {
            this.setIFrameHeight();
          }
        }.bind(this)
      });
    },

    _addGoogleMap: function () {
      this._map = new MapSearch.Map();
    },

    _animateClose: function () {
      this.el().shrink();
    }
  };

  MapSearch.panel = {
    _isOpen: true,
    initCloseTimeoutId: 0,

    el: function () {
      var el = $('map-search-panel');
      this.el = function () { return el; };
      return el;
    },

    wrapper: function () {
      var el = $('map-search-panel-wrapper');
      this.wrapper = function () { return el; };
      return el;
    },

    initialize: function () {
      this.open();
      this.initEvents();
    },

    initEvents: function () {
      var el = this.el(),
          searchButton = $('map-search-submit'),
          openHouseButton = $('map-search-open-house-search-button');

      el.observe('mouseover', this.increaseOpacity.bind(MapSearch.panel)); 
      el.observe('mouseout',  this.decreaseOpacity.bind(MapSearch.panel)); 

      searchButton.observe('click',    this.onSearchButtonClick.bind(this));
      openHouseButton.observe('click', this.onOpenHouseButtonClick.bind(this));
    },

    xmlUrl: function () {
      var url = [
        'getListingsXML.php?',
        $('map-search-form').serialize().strip(),
        '&page=0'
      ].join('').replace(/&openHouseSearch=\d/g, '');
      return url;
    },

    onSearchButtonClick: function (event) {
      Event.stop(event);
      this.doSearch();
    },

    onOpenHouseButtonClick: function (event) {
      Event.stop(event);
      this.doOpenHouseSearch();
    },

    doSearch: function () {
      var validator = new MapSearch.FormValidator();
      if (validator.valid()) {
        var url = this.xmlUrl();
        this.updateMap(url);
      } else {
        alert(validator.errors[0].msg);
      }
    },

    doOpenHouseSearch: function () {
      var url = [
        this.xmlUrl(),
        '&openHouseSearch=1',
        '&startDate=',
        this.getDate(new Date())
      ].join('');
      this.updateMap(url);
    },

    getDate: function (date) {
      return [
        date.getMonth() + 1,
        "-",
        date.getDate(),
        "-",
        date.getFullYear()
      ].join('');
    },

    updateMap: function (url) {
      MapSearch._resultSet.currentPage = 1;
      PT.Gmap.hasSetCenter = false;
      PT.Gmap.currentXmlUrl = url;
      PT.Gmap.repopulateGoogleMap(url);
    },

    toggle: function () {
      return !this._isOpen ? this.open() : this.close();
    },

    open: function () {
      this._isOpen = true;
      $('map-search-panel-handle').removeClassName('closed');
      return new Effect.Move(this.el(), {x: 0, mode: 'absolute'});
    },

    close: function () {
      this._isOpen = false;
      $('map-search-panel-handle').addClassName('closed');
      return new Effect.Move(this.el(), {x: -288, mode: 'relative'});
    },

    increaseOpacity: function () {
      this.wrapper().addClassName('mouseover');
    },

    decreaseOpacity: function () {
      this.wrapper().removeClassName('mouseover');
    }
  };

  MapSearch.FormValidator = function () {
    this.form = $('map-search-form');
    this.errors = [];
  };

  Object.extend(MapSearch.FormValidator.prototype, {
    valid: function () {
      var streetNum = $('quicksearch-streetNumber').getValue().strip(),
          streetName = $('quicksearch-streetName').getValue().strip();

      if (streetNum.length > 0 && streetName.length === 0) {
        this.errors.push({
          id:  'quicksearch-streetName',
          msg: 'A street name is required when performing an address search.' +
               ' Please enter the street name and try again.'
        });
      }

      return this.errors.length === 0;
    }
  });

  MapSearch.ListingDetailsWindow = function (url) {
    this.constructUrl(url);
    this.close();
    this.render();
    this.initEvents();
  };

  Object.extend(MapSearch.ListingDetailsWindow.prototype, {
    id:      'map-search-listing-detail-window',  
    overlay: 'map-search-listing-detail-overlay', 
    iframe:  'map-search-listing-detail-iframe',
    header:  'map-search-listing-detail-window-header',
    closeButton: '#map-search-listing-detail-window-header .close-window',

    initEvents: function () {
      $$(this.closeButton)[0].observe('click', this.close.bind(this));
    },

    getHeader: function () {
      if (this._header !== undefined) { return this._header; }
      this._header = $(this.header);
      return this._header;
    },

    constructUrl: function (url) {
      var hostname = window.location.hostname,
          pathname = window.location.pathname.replace(/(map\-search|search|results)\.php/, '');
      if (Prototype.Browser.IE) {
        this.url =  url;
      } else {
        this.url = "http://" + hostname + pathname + url;
      }
    },

    close: function (event) {
      if (event) { event.preventDefault(); }
      var curr = $(this.id);
      if (curr) { 
        curr.hide();
        $(this.overlay).hide();
      }
    },

    setWindowWidth: function () {
      var width = MapSearch.config.DETAILS_WINDOW_WIDTH;
      if (width === 0) { return; }
      marginLeft = width / 2 * (-1) + "px";
      width = width + "px";
      $(this.id).setStyle({
        width: width,
        marginLeft: marginLeft
      });
      $(this.iframe).setStyle({ width: width });
    },

    render: function () {
      $(this.iframe).writeAttribute('src', this.url);
      $(this.overlay).show();
      $(this.id).show();
      this.setWindowWidth();
      if (Prototype.Browser.IE) {
        [$(this.id), $(this.overlay)].each(function (el) {
          el.setStyle({'position': 'absolute'});
        });
      }
    }
  });

  // This is where all the magic happens
  Event.observe(window, 'load', function () { MapSearch.init(); });
  window.MapSearch = MapSearch;
})();

