var SearchWidget = Class.create({
  
  // Elements
  // mainContainer
  // searchContentsContainer
  // regionSelect
  // destinationSelect
  // monthsContainer
  // noAvailabilityMessage
  
  // Other Properties
  // initData
  // data
  // monthsObj : {"11/2011": HTMLDivElement}
  // searchURLTemplate
  // monthClickHandlers
  
  initialize: function(o) {
    
    SearchWidget.SELECT_PADDING = 2;
    
    this.initData = o;
    this.monthsObj = new Hash();
    this.monthClickHandlers = [];
    this.buildElements();
    
    new Ajax.Request(o.dataURL, {
      method: "get",
      onSuccess: this.handleDataSuccess.bind(this)
    });
    
  },
  
  handleDataSuccess: function(response) {
    
    var responseJSON = response.responseJSON || response.responseText.evalJSON();
    this.data = responseJSON.searchWidgetData;
    
    this.regionSelect.insert( new Element("option").update(this.initData.labelSelectARegion) );
    
    this.data.each(function(regionData){
      this.regionSelect.insert( new Element("option", {value: regionData.id}).update(regionData.displayName) );
    }, this);
    
    this.activateStep(1, true);
    
    if (this.initData.initDestId) {
      this.prepopToDestId(this.initData.initDestId);
    };
    
  },
  
  buildElements: function() {
    
    this.mainContainer = $(this.initData.containerId);
    
    this.searchContentsContainer = new Element("div").hide();
    this.searchContentsContainer.insert( new Element("div", {id: "searchWidgetHeader"}).update(this.initData.headerText || "Destination Quick Search") );
    this.searchContentsContainer.insert( new Element("div", {id: "searchWidgetInstructions"}).update(this.initData.instructions || "Choose a region, a destination, then select an available highlighted month.") );
    
    this.regionSelect = new Element("select", {id: "searchWidgetRegionSelect"});
    this.mainContainer.insert(this.regionSelect); // need to add it to the dom to get a measurement...
    var selectHeight = this.regionSelect.getHeight();
    
    var regionSelectContainer = new Element("div").addClassName("searchWidgetSelectContainer").setStyle({height: (selectHeight + SearchWidget.SELECT_PADDING * 2) + "px"});
    regionSelectContainer.insert( this.regionSelect );
     
    this.destinationSelect          = new Element("select", {id: "searchWidgetDestinationSelect"});
    var destinationSelectContainer = new Element("div").addClassName("searchWidgetSelectContainer").setStyle({height: (selectHeight + SearchWidget.SELECT_PADDING * 2) + "px"});
    
    this.destinationSelect.insert( new Element("option").update( this.initData.labelSelectADestination || "Select a Destination") );
    destinationSelectContainer.insert( this.destinationSelect );
    
    var date = new Date();
    var currMonthIndex = date.getMonth();
    
    function pad(number, length) {
      var str = String(number);
      while (str.length < length) {
        str = '0' + str;
      }
      return str;
    }
    
    this.monthsContainer          = new Element("div", {id: "searchWidgetMonthContainer"});
    this.monthButtonsContainer    = new Element("div", {id: "searchWidgetMonthButtonsContainer"});
    this.noAvailabilityMessage    = new Element("div", {id: "searchWidgetNoAvailability"}).update( this.initData.noAvailHTML || "Sorry, there is no availability<br/>for this destination.<br/>Please choose another." );
    this.monthsContainer.insert( this.monthButtonsContainer );
    this.monthsContainer.insert( this.noAvailabilityMessage );
    var prevMonthIndex = 0;
    var yearTurned = false;
    for (var i=0; i < 12; i++) {
      
      var thisMonthIndex = (currMonthIndex + i) % 12;
      if (thisMonthIndex < prevMonthIndex) yearTurned = true;
      var thisYearValue = (yearTurned) ? date.getFullYear() + 1 : date.getFullYear();
      
      var monthDiv = new Element("div").addClassName("searchWidgetMonth disabled").update( this.initData.monthLabels[thisMonthIndex] );
      this.monthClickHandlers.push( monthDiv.on("click", "div.searchWidgetMonth", this.doMonthClick.bind(this)) );
      
      if (i == 5 || i == 11) monthDiv.addClassName("col6");
      if (i > 5) monthDiv.addClassName("row2");
      
      this.monthButtonsContainer.insert( monthDiv );
      
      var thisMonthKey = pad((thisMonthIndex + 1), 2) + "/" + thisYearValue;
      
      this.monthsObj.set(thisMonthKey, monthDiv);
      
      prevMonthIndex = thisMonthIndex;
    };
    
    this.regionSelect.on("change", this.doRegionSelectChange.bind(this));
    this.destinationSelect.on("change", this.doDestinationSelectChange.bind(this));
    
    this.searchContentsContainer.insert( regionSelectContainer );
    this.searchContentsContainer.insert( destinationSelectContainer );
    this.searchContentsContainer.insert( this.monthsContainer );
    
    this.mainContainer.insert( this.searchContentsContainer );

  },
  
  doRegionSelectChange: function(event) {
    this.regionSelect.blur();
    this.removeInstructionOptionForSelect(this.regionSelect);
    this.populateDestinationSelectWithGroupsArray( this.getCurrGroupsArray() );
    this.activateStep(2, !event);
  },
  doDestinationSelectChange: function(event) {
    this.destinationSelect.blur();
    this.removeInstructionOptionForSelect(this.destinationSelect);
    this.activateStep(3, !event);
    this.updateMonthButtons();
  },
  
  updateMonthButtons: function() {
    var currDestData = this.getCurrDestinationData();
    var enabledMonthsCount = 0;
    if (currDestData.avail) {
      
      var monthKeyArray = $A(currDestData.avail.split(","));
      
      var currDestId = parseInt($F(this.destinationSelect), 10);
      
      this.monthsObj.each(function(pair) {
        
        if (monthKeyArray.indexOf(pair.key) != -1) {
          
          pair.value.removeClassName("disabled");
          
          if (this.initData.initDestId && currDestId == this.initData.initDestId && this.initData.currMonthYear == pair.key) {
              pair.value.addClassName("current");
          } else {
            pair.value.removeClassName("current");
          }
          
          enabledMonthsCount++;
        } else {
          pair.value.addClassName("disabled");
        }
        
      }, this);
      
    } else {
      this.disableAllMonths();
    }
    
    if (enabledMonthsCount) {
      
      new Effect.Parallel([
        new Effect.Morph(this.monthButtonsContainer, { sync: true, style: {opacity: '1'}}),
        new Effect.Morph(this.noAvailabilityMessage, { sync: true, style: {opacity: '0'}})
      ], { 
        duration: 0.25
      });
      
      
    } else {
      
      this.noAvailabilityMessage.appear({duration: 0.25});
      new Effect.Morph(this.monthButtonsContainer, { style: {opacity: '0'}, duration: 0.25 });
      
    };
    
  },
  
  removeInstructionOptionForSelect: function(select) {
    var firstOption = select.firstDescendant();
    if (firstOption.tagName.toUpperCase() != "OPTGROUP" && !firstOption.hasAttribute("value")) firstOption.remove();
  },
  
  populateDestinationSelectWithGroupsArray: function(groupsArray) {
    
    this.destinationSelect.update( new Element("option").update( this.initData.labelSelectADestination || "Select a Destination") );
    
    groupsArray.each(function(groupData){
      
      var optGroup = new Element("optgroup", {label: groupData.headerName});
      
      groupData.destinations.each(function(destinationData){
        optGroup.insert( new Element("option", {value: destinationData.destId}).update(destinationData.displayName) );
      });
      
      this.destinationSelect.insert( optGroup );
      
    }, this);
    
  },
  
  activateStep: function(state, quickly) {
    
    var duration = (quickly) ? 0 : 0.25;
    
    switch (state) {
      
      case 1:
        this.destinationSelect.disable();
        this.monthButtonsContainer.setOpacity(0.25);
        this.noAvailabilityMessage.setOpacity(0);
        this.searchContentsContainer.show();
        break;
        
      case 2:
        this.destinationSelect.enable();
        this.disableAllMonths();
        new Effect.Morph(this.noAvailabilityMessage, { style: {opacity: '0'}, duration: duration });
        new Effect.Morph(this.monthButtonsContainer, { style: {opacity: '0.25'}, duration: duration });
        break;
        
      case 3:
        this.monthsContainer.appear({duration: duration});
        break;
    }
    
  },
  
  doMonthClick: function(event, element) {
    if (!element.hasClassName("disabled") && !element.hasClassName("current")) {
      
      var monthYear = this.monthsObj.detect(function(pair) { return pair.value == element; }).key;
      var destId    = $F(this.destinationSelect);
      var templateRegEx = /(^|.|\r|\n)(\[(\w+)])/; // Custom template regexp matches on '[variable]', not '#{variable}'
      var targetURL = new Template(this.initData.searchURLTemplateString, templateRegEx).evaluate({monthyear: monthYear, destid: destId});
      window.location.href = targetURL;
      
    };
  },
  
  disableAllMonths: function() {
    this.monthsObj.each(function(pair){
      pair.value.addClassName("disabled");
      pair.value.removeClassName("current");
    });
  },
  
  getCurrGroupsArray: function() {
    var regionId = this.regionSelect.getValue();
    return this.data.detect(function(regionData){ return regionData.id == regionId; }).destinationGroups;
  },
  
  getCurrDestinationData: function() {
    
    var currGroupsArray = this.getCurrGroupsArray();
    var currGroupLabel = this.destinationSelect.options[this.destinationSelect.selectedIndex].up().readAttribute("label");
    var selectedDestId = parseInt(this.destinationSelect.getValue(), 10);
    
    var groupDestinationsObj = currGroupsArray.detect(function(group) {
      return group.headerName == currGroupLabel;
    });
    
    var currDestData = groupDestinationsObj.destinations.detect(function(destination){
      return destination.destId == selectedDestId;
    });
    
    return currDestData;
  },
  
  prepopToDestId: function(destId) {
    var found = false;
    this.data.each(function(groupData) {
      if (found) throw $break;
      groupData.destinationGroups.each(function(destinationGroup) {
        if (found) throw $break;
        destinationGroup.destinations.each(function(destination) {
          if (found) throw $break;
          if (destination.destId == destId) {
            this.regionSelect.setValue(groupData.id);
            this.doRegionSelectChange();
            this.destinationSelect.setValue(destination.destId);
            this.doDestinationSelectChange();
            found = true;
          };
        }, this);
      }, this);
    }, this);
  }
  
});
