function CategoryBrowser() {
  this.init();
}

CategoryBrowser.prototype = {
  init: function() {
    this.layout = (window.location.toString().match(/layout=\w+/)) ? window.location.toString().match(/layout=(\w+)/)[1] : "narrowmap";
    this.originalUrl = window.location.pathname.toString();
    this.uncheckedFilter = '';

    this.categoryFilter = Selector.query('.category-filter', null, true);
    this.categoryListing = Selector.query('.category-listing', null, true);
    this.categorySidebar = Selector.query('.category-sidebar__inner', null, true);

    if (!this.categoryFilter || !this.categoryListing || !this.categorySidebar) return;

    this.categoryFilterSpinner = new QYPE.layout.OverlaySpinner(this.categoryFilter);
    // XXX disabled for now
    // this.mapExplorerSearch = new QYPE.MapExplorerSearch();

    // Subscribe to map updates
    QypeMap.onMapUpdated.subscribe(this.mapCallback, this);

    // Custom events
    this.onLayoutChanged = new YAHOO.util.CustomEvent('CategoryBrowser.layoutChanged');

    this.initFilters();
    this.initExpansions();
    this.initMapPosition();
    this.initMapToggle();
    this.initMapToolbar();
    this.initTooltips();
    this.initPaging();
  },

  initFilters: function() {
    // Both links and checkboxes act as filters
    this.categoryFilterOptions = Selector.query(".category-filter a, .category-filter input");

    for (var i=0; i<this.categoryFilterOptions.length; i++) {
      // Check the option id against the d[]= params (URL params) and the locator id (URL path)
      var option = this.categoryFilterOptions[i];

      if (option.tagName.toLowerCase() == "input") {
        var pathRe = this.escapeRegexp('/' + option.id + '/');
        var paramsRe = this.escapeRegexp('d[]=' + option.id);
        if (window.location.pathname.toString().match(pathRe) || decodeURIComponent(window.location.search.toString()).match(paramsRe)) {
          option.checked = true;
        }
      }

      Event.on(option, 'click', function(e) {
        var target = Event.getTarget(e);
        var input = Dom.getPreviousSibling(target);

        input = (input && input.tagName.toLowerCase() == "input") ? input : null;

        if (target.tagName.toLowerCase() == "a") {
          Event.preventDefault(e);

          // Toggle the corresponding input when a link is clicked (area and details filters)
          if (input) {
            input.checked = !input.checked;

            // If an input has been unchecked, save it - it will be needed in deciding the final URL
            if (!input.checked) {
              this.uncheckedFilter = input.id;
            }
          }
        } else if (target.tagName.toLowerCase() == "input") {
          // If an input has been unchecked, save it - it will be needed in deciding the final URL
          if (!target.checked) {
            this.uncheckedFilter = target.id;
          }
        }

        if (target.className.indexOf('category-filter__more') == -1 &&
            target.className.indexOf('category-filter-dropdown__more') == -1 &&
            target.className.indexOf('category-filter-dropdown__close') == -1 &&
            target.className.indexOf('CloseTooltipLink') == -1)
        {
          // Remove the 'open' state class from the dropdown toggle
          Dom.batch(Selector.query('.category-filter__more_open', this.categoryFilter), function(el) {
            Dom.removeClass(el, 'category-filter__more_open');
          });

          // Hide the dropdown
          Dom.addClass(Dom.getAncestorByClassName(target, 'category-filter-dropdown'), 'Hidden');

          this.categoryFilterSpinner.show();

          // If there's an input (checkbox), submit the form using its parameters, otherwise - use a link
          this.submitForm(input || target, this.collectForm());
        }
      }, null, this);
    }
  },

  initExpansions: function() {
    this.initCategoryExpansions();
    this.initCategoryExpansionsClose();
    this.initDropdownExpansions();
    this.initRecommendationExpansions();
  },

  initCategoryExpansions: function() {
    var categoryExpansions = Selector.query(".category-filter__more", this.categoryFilter);

    for (var i=0; i<categoryExpansions.length; i++) {
      Event.on(categoryExpansions[i], 'click', function(e) {
        var toggle = Event.getTarget(e);

        Event.preventDefault(e);

        // Toggle the 'open' state of header dropdown toggles
        Dom.batch(categoryExpansions, function(el) {
          if (el != toggle) {
            Dom.removeClass(el, 'category-filter__more_open');
          }
        });

        if (Dom.getAncestorByClassName(toggle, 'category-filter__head')) {
          QYPE.dom.toggleClass(toggle, 'category-filter__more_open');
        }

        // Toggle the dropdowns themselves
        var toggleOffset = Dom.getX(toggle) - Dom.getX('Container');

        var dropdowns = Selector.query(".category-filter-dropdown"),
            current = Selector.query(".category-filter-dropdown", toggle.parentNode, true);

        for (var j=0; j<dropdowns.length; j++) {
          var dropdown = dropdowns[j];

          if (dropdown != current) {
            Dom.addClass(dropdown, 'Hidden');
          } else {
            QYPE.dom.toggleClass(dropdown, 'Hidden');

            var columnWidth = this._calculateWidth(dropdown);

            Dom.setStyle(dropdown, 'width', columnWidth + 'px');

            // Reset left/right
            Dom.setStyle(dropdown, 'left', null);
            Dom.setStyle(dropdown, 'right', null);

            if (Dom.hasClass(dropdown, 'category-filter-dropdown_header')) { // Positioned relative to a toggle
              Dom.setStyle(dropdown, 'left', '-10px');
            } else { // Positioned relative to a document
              // Set top
              if (YAHOO.env.ua.ie > 0) {
                Dom.setStyle(dropdown, 'top', Dom.getY(toggle) + 'px');
              }
              // Set left/right depending on which boundary is closer
              if (toggleOffset + columnWidth > parseInt(Dom.getStyle('Container', 'width'))) {
                Dom.setStyle(dropdown, 'right', 0);
              } else {
                Dom.setStyle(dropdown, 'left', toggleOffset + 'px');
                
              }
            }
          }
        }
      }, null, this);
    }
  },

  initCategoryExpansionsClose: function() {
    Dom.batch(Selector.query(".category-filter-dropdown__close"), function(el) {
      Event.on(el, 'click', function(e) {
        var target = Event.getTarget(e);

        Event.preventDefault(e);

        Dom.addClass(Dom.getAncestorByClassName(target, 'category-filter-dropdown'), 'Hidden');
      }, null, this);
    });
  },

  initDropdownExpansions: function() {
    var dropdownExpansions = Selector.query(".category-filter-dropdown__more", this.categoryFilter);

    for (var j=0; j<dropdownExpansions.length; j++) {
      Event.on(dropdownExpansions[j], 'click', function(e) {
        var toggle = Event.getTarget(e);

        Event.preventDefault(e);

        var dropdown = Dom.getAncestorByClassName(toggle, 'category-filter-dropdown'),
            columns = Selector.query(".category-filter-dropdown__column", dropdown);

        Dom.batch(columns, function(el) {
          Dom.removeClass(el, 'Hidden');
        });

        var columnWidth = this._calculateWidth(dropdown);

        Dom.setStyle(dropdown, 'width', columnWidth + 'px');

        Dom.addClass(toggle, 'Hidden');
      }, null, this);
    }
  },

  initRecommendationExpansions: function() {
    var recommendationExpansions = Selector.query(".category-review__recommendation__more");

    for (var k=0; k<recommendationExpansions.length; k++) {
      Event.on(recommendationExpansions[k], 'click', function(e) {
        var toggle = Event.getTarget(e);

        Event.preventDefault(e);

        var dropdown = Selector.query('.category-review__recommendation__users-dropdown', toggle.parentNode, this);

        QYPE.dom.toggleClass(dropdown, 'Hidden');
        QYPE.dom.toggleClass(toggle, 'category-review__recommendation__more_open');
      });
    }
  },

  initMapPosition: function() {
    var top,
        mapBlock = Selector.query('.category-sidebar #ExpandableMapWidget', null, true),
        ie6 = YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie <= 6;

    var blockOffset = 0,
        footerPosition = 0,
        maxHeight = 0;

    if (mapBlock) {
      mapBlock.style.position = 'fixed';
      recalculateTop();
      Event.on(window, 'resize', recalculateTop);
      this.onLayoutChanged.subscribe(recalculateTop);

      Event.on(window, 'scroll', function() {
        if (this.layout == "narrowmap") {
          return;
        }

        // If the map block is taller than the content - do nothing
        var contentBlock = Selector.query('.category-listing', null, true),
            contentHeight = parseFloat(Dom.getStyle(contentBlock, 'height'));

        if (mapBlock.offsetHeight > contentHeight) {
          return;
        }

        var scroll = window.pageYOffset || window.scrollTop,
            css = null;

        if (ie6) {
          css = {
            'position': 'absolute',
            'top': scroll > top ? (scroll - top) + 'px' : 0
          };
          if (scroll + maxHeight > footerPosition) {
            css.top = (footerPosition - maxHeight - blockOffset) + 'px';
          }

        } else {
          if (scroll > top) {
            if (scroll + maxHeight > footerPosition) {
              css = {
                'position': 'absolute',
                'top': (footerPosition - maxHeight - blockOffset) + 'px'
              };

            } else {
              css = {
                'position': 'fixed',
                'top': 0
              };
            }

          } else {
            css = {
              'position': 'relative',
              'top': 0
            };

            // Force reflow in Opera
            if (YAHOO.env.ua.opera) {
              Dom.setStyle(document.body, 'z-index', parseInt(Math.random() * 10, 10));
            }
          }
        }

        // Apply styles
        for (var i in css) {
          Dom.setStyle(mapBlock, i, css[i]);
        }
      }, null, this);
    }

    function recalculateTop() {
      top = Math.round(Dom.getY(mapBlock.parentNode));
      footerPosition = Math.round(Dom.getY('Footer')) - 20;
      maxHeight = 0;

      var css = {
        'position': Dom.getStyle(mapBlock, 'position'),
        'width': null
      };

      // Reset width to find out the actual width
      Dom.setStyle(mapBlock, 'position', 'relative');
      Dom.setStyle(mapBlock, 'width', '');

      css.width = mapBlock.offsetWidth;

      // Set width again
      Dom.setStyle(mapBlock, 'width', css.width + 'px');

      var blockHeight = mapBlock.offsetHeight;

      if (blockHeight > maxHeight) {
        maxHeight = blockHeight;
      }

      blockOffset = Math.round(Dom.getY(mapBlock.parentNode));
    }
  },

  initMapToggle: function() {
    var toggle = Selector.query('.category-map__toggle', null, true);

    Event.on(toggle, 'click', function(e) {
      Event.preventDefault(e);
      this._toggleMap(Event.getTarget(e));
    }, null, this);

    // Preserve page layout between reloads
    if (this.layout == "widemap") {
      this._toggleMap(toggle);
    }
  },

  _toggleMap: function(el) {
      var curToggleLabel = el.innerHTML;

      // Toggle label
      el.innerHTML = el.getAttribute('data-toggle-label');
      el.setAttribute('data-toggle-label', curToggleLabel);
      QYPE.dom.toggleClass(el, 'category-map__toggle_expanded');

      // Toggle columns
      QYPE.dom.toggleClass(this.categoryListing, 'category-listing_narrow');
      QYPE.dom.toggleClass(this.categoryListing, 'span-15 append-1');
      QYPE.dom.toggleClass(this.categoryListing, 'span-8');

      QYPE.dom.toggleClass(this.categorySidebar, 'category-sidebar__inner_wide');
      QYPE.dom.toggleClass(this.categorySidebar, 'span-8');
      QYPE.dom.toggleClass(this.categorySidebar, 'span-16');

      if (Dom.hasClass(this.categorySidebar, 'category-sidebar__inner_wide')) {
        this.wideMapEnabled = true;
        this.layout = "widemap";
      } else {
        this.layout = "narrowmap";
      }

      this.onLayoutChanged.fire();

      if (QypeMap.map()) {
        google.maps.event.trigger(QypeMap.map(), 'resize');
        QypeMap.centerAndZoom();
      }
  },

  initMapToolbar: function() {
    this.mapToolbar = Selector.query('.category-map-toolbar', this.categorySidebar, true);

    this.mapToolbarMore = Selector.query('.category-map-toolbar__more', this.mapToolbar, true);
    this.mapToolbarRedo = Selector.query('.category-map-toolbar__redo', this.mapToolbar, true);
    this.mapToolbarDraw = Selector.query('.category-map-toolbar__draw', this.mapToolbar, true);
    this.mapToolbarReset = Selector.query('.category-map-toolbar__reset', this.mapToolbar, true);

    // When search is toggled by the map, deselect the areas in the filter (with anim)
    function deselectAreas(el) {
      if (el.checked) {
        el.checked = false;

        var anim = new YAHOO.util.ColorAnim(Dom.getNextSibling(el), { backgroundColor: { from: '#FFA', to: '#F8F8F8' } });
        anim.animate();
      }
    }

    Event.on(this.mapToolbarMore, 'click', function(e) {
      Event.preventDefault(e);

      var el = Event.getTarget(e);
      var curToggleLabel = el.innerHTML;

      // Toggle label
      el.innerHTML = el.getAttribute('data-toggle-label');
      el.setAttribute('data-toggle-label', curToggleLabel);

      // Toggle the number of markers (10<->20)
      var mapExplorer = window.map_explorer;

      mapExplorer.setMarkersPerPage((mapExplorer.getMarkersPerPage() == '10') ? '20' : '10');

      // Update the map
      QypeMap.onMapUpdated.fire({map: QypeMap.map(), action: 'more'})
    }, null, this);

    Event.on(this.mapToolbarRedo, 'click', function(e) {
      Event.preventDefault(e);

      // Deselect areas in the filter - now we're searching by map boundary
      Dom.batch(Selector.query('.category-filter__area input', this.categoryFilter), function(el) {
        deselectAreas(el);
      });

      // Hide the button
      Dom.addClass(Event.getTarget(e), 'Hidden');

      // Update the map
      QypeMap.onMapUpdated.fire({map: QypeMap.map(), action: 'redo'});
    }, null, this);

    Event.on(this.mapToolbarDraw, 'click', function(e) {
      Event.preventDefault(e);
      QYPE.dom.toggleClass(this.mapToolbarDraw, 'Hidden');
      QYPE.dom.toggleClass(this.mapToolbarReset, 'Hidden');
      Dom.addClass(this.mapToolbarRedo, 'Hidden');
      this.mapExplorerSearch.enableDragGuide();
    }, null, this);

    Event.on(this.mapToolbarReset, 'click', function(e) {
      Event.preventDefault(e);
      QYPE.dom.toggleClass(this.mapToolbarDraw, 'Hidden');
      QYPE.dom.toggleClass(this.mapToolbarReset, 'Hidden');
      Dom.removeClass(this.mapToolbarRedo, 'Hidden');
      this.mapExplorerSearch.reset();
      this.mapExplorerSearch.disableDragGuide();
      QypeMap.onMapUpdated.fire({map: QypeMap.map(), action: 'reset'});
    }, null, this);
  },

  initTooltips: function() {
    // Tooltip show
    Dom.batch(Selector.query('.category-filter__sort__beta', this.categoryFilter), function(el) {
      Event.on(el, 'click', function(e) {
        var target = Event.getTarget(e);
        var tooltip = Dom.getNextSibling(target);

        QYPE.dom.toggleClass(tooltip, 'Hidden');
        Dom.setStyle(tooltip, 'left', Dom.getX(target) - Dom.getX('Container') - 15 + 'px');
        if (YAHOO.env.ua.ie > 0) {
          Dom.setStyle(tooltip, 'top', Dom.getY(target) + 5 + 'px');
        } else {
          Dom.setStyle(tooltip, 'top', Dom.getY(target) + 20 + 'px');
        }

      }, null, this);
    });

    // Tooltip hide
    Dom.batch(Selector.query('.CloseTooltipLink', this.categoryFilter), function(el) {
      Event.on(el, 'click', function(e) {
        var target = Event.getTarget(e);

        Event.preventDefault(e);

        Dom.addClass(Dom.getAncestorByClassName(target, 'TooltipV1b'), 'Hidden');
      }, null, this);
    });
  },

  initPaging: function() {
    this.onLayoutChanged.subscribe(updatePaging);

    // Update paging links with the current layout value
    var self = this;
    function updatePaging() {
      Dom.batch(Selector.query('.Pagination a'), function(el) {
        el.href = el.href.replace(/layout=\w+/g, 'layout=' + self.layout);

        if (!el.href.match(/layout=\w+/)) {
          el.href += '&layout=' + self.layout;
        }
      });
    }
  },

  collectForm: function() {
    var filterSet = [];

    // Collect the active checkboxes
    for (var i=0; i<this.categoryFilterOptions.length; i++) {
      var option = this.categoryFilterOptions[i];

      if (option.checked) {
        var optionGroup = Dom.getAncestorByTagName(option, 'td').className.match(/category-filter__(\w+)/)[1];

        switch (optionGroup) {
          case "area":
            filterSet.push('d[]=' + encodeURIComponent(option.name));
            break;

          case "price":
            filterSet.push('p[]=' + encodeURIComponent(option.name));
            break;

          case "details":
            filterSet.push('a[]=' + encodeURIComponent(option.name));
            break;
        }
      }
    }

    return filterSet.join("&");
  },

  submitForm: function(el, params) {
    var path = (el.tagName.toLowerCase() == "a" ? el.href : window.location.pathname.toString()).replace(/#/, '');

    path += "?layout=" + this.layout + ((params) ? "&" + params : "");

    // We need this to be able to navigate to a higher-level locator (/de600-hamburg-altona/... -> /de600-hamburg/...)
    if (this.uncheckedFilter !== '' && this.originalUrl.match(this.escapeRegexp(this.uncheckedFilter))) {
      path += "&force_uncheck=true";
    }

    window.location = path;
  },

  /*
   name = "QypeMap.onMapUpdated"
   data = Callback data
   ctx = this
   */
  mapCallback: function(name, data, ctx) {
    var mapExplorer = window.map_explorer;

    // Map scrolling parameters should be recalculated when page contents (and therefore height) change
    var onload = function() {
      ctx.onLayoutChanged.fire();
    };

    switch (data[0].action) {
      case 'drag':
      case 'zoom':
        if (Dom.hasClass(ctx.categorySidebar, 'category-sidebar__inner_wide')) {
          if (ctx.wideMapEnabled === true)
            ctx.wideMapEnabled = false;
          else
            Dom.removeClass(ctx.mapToolbarRedo, 'Hidden');
        }
        break;
      default:
        mapExplorer.loadNextPoints(mapExplorer.getMarkersPerPage(), ctx.collectForm(), onload);
    }
  },

  escapeRegexp: function(str) {
    var escapeRegexpRe = /([\|!\[\]\^\$\(\)\{\}\+=\?\.\*\\])/g;

    return new RegExp(str.replace(escapeRegexpRe, "\\$1"));
  },

  _calculateWidth: function(el) {
    // Set column width
    var columns = Selector.query(".category-filter-dropdown__column", el),
        columnWidth = 0;

    Dom.batch(columns, function(el) {
      columnWidth += Math.round(parseFloat(Dom.getRegion(el).width) + parseFloat(Dom.getStyle(el, 'margin-right')));
    });

    // Throw in some additional width to make sure everything fits
    columnWidth += 20;

    return columnWidth;
  }
};

Event.onDOMReady(function() {
  new CategoryBrowser();
});