/********************************** 
  Date parser, should move to library 
**********************************/

function DateParser() {
  this.parseFunctions = new Object();
  this.today = new Date();
  this.error = "";
}

//"public" methods
DateParser.prototype.parse = function (dateString, format) {
  this.error = "";
  try {
    if(this.parseFunctions[format] == null) {
      this.parseFunctions[format] = this.createParseFunction(format);
    }
    return this.parseFunctions[format](dateString);
  }
  catch(e) {
    this.error = e.message;
  }
  return false;
};

DateParser.prototype.getErrorMessage = function () {
  return this.error;
};


//"private" methods
DateParser.prototype.createParseFunction = function(format) {
  var re = "^";
  var funcs = new Array();
  for (var i = 0; i < format.length; ++i) {
    ch = format.charAt(i);
    re += this.getRe(ch);
    f = this.getFunction(ch);
    if(f) {
      funcs.push(f);
    }
  }
  re += "$";
  re = new RegExp(re);

  var obj = this;
  return function(dateString) {return obj.parseInternal(dateString, re, funcs);};
};

DateParser.prototype.getRe = function(ch) {
  if(ch in this.partReMap) {
    return this.partReMap[ch];
  }

  return this.reEscape(ch);
};

DateParser.prototype.getFunction  = function(ch) {
   if(ch in this.partFuncMap) {
    return this.partFuncMap[ch];
  }

  return false;
};

DateParser.prototype.parseInternal = function (dateString, re, funcs) {
  var dateParts = new Object;
  var values = dateString.match(re);
  if(!values) {
    throw new Error("Date does not match format");
  }

  for(var i = 1; i < values.length; i++) {
    var func = funcs[i-1];
    if(!func) {
      throw new Error("Internal Error.  Please check back later.");
    }

    //values[0] is the entire match.  Funcs only contains matches 
    //for the parts
    var callback = func["func"];
    dateParts[func["var"]] = callback.call(this, values[i]);
  }

  //set up defaults in case we are missing some values.
  result = new Date(this.today.getFullYear(), this.today.getMonth(), 1, 0, 0, 0, 0);
   
  if("y" in dateParts) {
    result.setFullYear(dateParts["y"]);
  }
  if("m" in dateParts) {
    result.setMonth(dateParts["m"] -1);
    if(result.getMonth() != dateParts["m"]-1) {
      throw new Error("Month must be between 1 and 12");
    }
  }
  if("d" in dateParts) {
    result.setDate(dateParts["d"]);
    if(result.getDate() != dateParts["d"]) {
      throw new Error("Day is invalid.");
    }
  }

  return result;
};

//part parse methods / utility methods

DateParser.prototype.parseYear = function(str) {
  var value = this.convertToInt(str, "Year");

  //assume that value is a year in the range (currentYear - 30, currentYear + 70]
  if(value < 100) {
    var currentYear = (new Date()).getFullYear();
    var century = Math.round(currentYear/100) * 100;
    value += century;
    if(value > currentYear + 70) {
      value -= 100;
    }
  }

  return value;
};

DateParser.prototype.parseFullYear  = function(str) {
  return this.convertToInt(str, "Year");
};

DateParser.prototype.parseMonth  = function(str) {
  return this.convertToInt(str, "Month");
};

DateParser.prototype.parseDay  = function(str) {
  return this.convertToInt(str, "Day");
};

DateParser.prototype.convertToInt = function(str, field) {
  var value = Number(str);
  if(isNaN(value) || Math.floor(value) != value) {
    throw new Error(field + " is not valid");
  }

  return value;
};

DateParser.prototype.convertToIntRange = function(str, field, min, max) {
  return this.convertToInt(str, field);
};

/*
  This is the only dependancy on mootools (or any other package) in the 
  date parser.  If you want to use something else, override this function 
  with whatever you use or your own implementation
*/
DateParser.prototype.reEscape = function(str) {
  return str.escapeRegExp();
};

//some "static class variables
DateParser.prototype.partReMap = {
  "y" : "(\\d{1,4})",
  "Y" : "(\\d{1,4})",
  "m" : "(\\d{1,2})",
  "d" : "(\\d{1,2})",
  "/" : "[/\\.-]"
};

//needs to be last so that callbacks are defined
DateParser.prototype.partFuncMap = {
  "y" : {"var" : "y", "func" : DateParser.prototype.parseYear},
  "Y" : {"var" : "y", "func" : DateParser.prototype.parseFullYear},
  "m" : {"var" : "m", "func" : DateParser.prototype.parseMonth},
  "d" : {"var" : "d", "func" : DateParser.prototype.parseDay}
};


/********************************** 
 Functions to process availability with 3rd party sites 
**********************************/

var cAjaxBase = new Class({
  options : {
    url : ""
  },

  initialize : function (options) {
    this.setOptions(options);
    this.m_lastAjaxCall = null;
  },

  isRunning : function () {
    return this.m_lastAjaxCall && this.m_lastAjaxCall.running;
  },

  load: function(ajaxOptions) {
    if(this.m_lastAjaxCall && this.m_lastAjaxCall.running) {
      return;
    }
    this.m_lastAjaxCall = null;

    if(ajaxOptions.hasOwnProperty("onFailure")) {
      ajaxOptions.onFailure = this.makeOneRetry(ajaxOptions.onFailure).bind(this);
    }
    else {
      ajaxOptions.onFailure = this.makeOneRetry(this.empty).bind(this);
    }

    //console.log(this.options.url + "?" + ajaxOptions.data);
    this.m_lastAjaxCall = new Ajax(this.options.url, ajaxOptions).request();
  },

  makeOneRetry : function(failure) {
    return function() {
      var opt = this.m_lastAjaxCall.options;
      opt.onFailure = failure.bind(this);
      this.m_lastAjaxCall = new Ajax(this.options.url, opt).request();
    }
  },

  //we get some intermittant and inexplicable failures.  Retry once 
  //since that seems to help
  oneRetry : function () {
    var opt = this.m_lastAjaxCall.options;
    opt.onFailure = this.options.failure.bind(this);
    this.m_lastAjaxCall = new Ajax(this.m_url, opt).request();
  }
});
cAjaxBase.implement(new Options);


var cWebervationsBase =  cAjaxBase.extend ({
  initialize: function (callback, options) {
    this.m_baseCallOptions = {
      method: 'get', 
      onComplete: this.handleResponse.bind(this),
      onFailure: function(){this.m_callback(false, this.defaultData());}.bind(this)  
    };

    this.m_callback = callback;
    this.parent(options);
  },

  load: function(innList, start, days) {
    if(this.isRunning()) {
      return;
    }
  
    //save some things for the callbacks
    this.m_innList = innList;
    this.m_start = start;
    this.m_days = days;

    var ids = new Array();
    var wids = new Array();
    for (id in innList) {
      if(this.shouldHandle(innList[id]["type"])) {
        ids.push(id);
        wids.push(innList[id]["eid"]);
      }
    }

    var data = this.makeQueryString(start, days, wids, ids);
    if(!data) {
      //this.m_callback(false, this.defaultData());
      this.m_baseCallOptions.onFailure();
      return;
    }

    //get new copy of options
    var opt = $merge(this.m_baseCallOptions);
    opt.data = data;
    this.parent(opt)
  },

  makeQueryString : function(start, days, weberIds, innIds) {
    if(innIds.length == 0 || innIds.length != weberIds.length) {
      return false;
    }

    //set up the query data
    var queryData = {cbp:"BBO",lud:"14"};
    queryData["ids"] = weberIds.join(",");
    queryData["rids"] = innIds.join(",");


    if(typeof(start) == "string") {
      queryData["mdy"] = start;
    }
    //assume date
    else if(start != null && typeof(start) == "object") {
      queryData["mdy"] = (start.getMonth()+1) + "/" + start.getDate() + "/" + start.getFullYear();
    }
    else {
      throw new Error("Invalid date type"); 
    }

    queryData["los"] = days;
    return Object.toQueryString(queryData);
  },

  parseText : function(text) {
    var lines = text.split("\n");
    if(lines[lines.length-1] != "-eof") {
      return false;
    }

    var data = new Array();
    for(var i = 0; i < lines.length -1; i++) {
      var lineData = lines[i].split(",");
      data.push(lineData); 
    }
    return data;
  },

  shouldHandle : function(type) {
    return (type == "w" || type == "W");
  }
});



var cAvailResult = new Class({});

var cWAvailResult = cAvailResult.extend ({
  initialize : function (wid, avail) {
    this.m_wid = wid;
    this.m_avail = avail;
    this.m_isAvail = avail.indexOf("0") == -1;  
  },

  getAvailabilityStatus : function () {
    if(this.m_isAvail) {
      return "Y";
    }
    else {
      return "N";
    }
  }

});

var cNAAvailResult = cAvailResult.extend ({
  initialize : function (id) {
    this.m_id = id;
  },

  getAvailabilityStatus : function () {
    return "M";
  }
});



var cRoomAvailResult = new Class({});

var cWRoomAvailResult = cRoomAvailResult.extend ({
  initialize : function(dArray, start, days) {
    this.id = dArray[0];
    this.reservations = dArray[1];
    this.showRates = dArray[2];
//    thi.m_isConfirmed = dArray[3];
    this.weberId = dArray[4];
    this.roomId = dArray[5];
    this.availabilityMask = dArray[6];
    this.rates = this.getRates(dArray[7]);
    this.maxOccupancy = dArray[8];
    this.baseOccupancy = dArray[9];
    this.extraAdult = dArray[10];
    this.extraChild = dArray[11];
    this.roomName = dArray[12];
    this.start = start;
    this.days = days;
  },

  getIsAvailable : function() {
    return this.availabilityMask.indexOf("0") == -1;   
  },

  getRates : function(ratestring) {
    if(ratestring.length % 4 != 0) {
      return Array();
    }

    var rates = new Array();
    for(var i = 0; i < ratestring.length; i+=4) {
       rates.push(Number(ratestring.slice(i, i+4)));
    }
    return rates;
  }, 

  getTotal : function() {
    var total=0;
    for(var i = 0; i < this.rates.length; i++) {
    	total=total + this.rates[i];
	}
    return total;
  }, 

  getBookingUrl : function (returnUrl) {
    var q = new Object();
    q.memberid= this.weberId;
    q.roomid = this.roomId;
    q.cpb="bbonline";
    if(returnUrl) {
	    q.zcaller= returnUrl;
    }
    else {
	q.zcaller = document.location.href;
    }
    if(typeof(this.start) == "string") {
      q.zmdy = this.start;
    }
    //assume date
    else if(this.start != null && typeof(this.start) == "object") {
      q.zmdy = (this.start.getMonth()+1) + "/" + this.start.getDate() + "/" + this.start.getFullYear();
    }
    else {
      throw new Error("Invalid date type"); 
    }

  
    q.zlength = this.days;

    var url = "http://www.webervations.com/magic-scripts/roombook.asp?" +  Object.toQueryString(q);
    return url;  
  }
});

var cAvailability = new Class({
  initialize: function (callback) {
    this.m_callback = callback;
    this.m_data = new Object;
    this.m_list = null;
    this.m_web = new cWebervationsData(this.handleResponse.bind(this));
  },

  load: function(innInfo, start, days) {
    //this.startLoadDisplay();
    this.m_list = innInfo;
    this.m_web.load(innInfo, start, days);
  },

  handleResponse: function (isSuccess, data) {
    //eventually we want to merge this with previous calls and sync.
    this.m_data = data;
    this.setDefaultData();
    this.m_callback(isSuccess, this.m_data); 
    //this.endLoadDisplay();
  },

  setDefaultData : function () {
    for (id in this.m_list) {
      if(!(id in this.m_data)) {
        this.m_data[id] = new cNAAvailResult(id);
      }
    }
  }
});


var cRoomAvailability = new Class({
  initialize: function (callback) {
    this.m_callback = callback;
    this.m_data = new Object;
    this.m_list = null;
    this.m_web = new cWebervationsDetailData(this.handleResponse.bind(this));
  },

  /*
    We only intend to lookup an inn at a time, but the backend handles
    multiple.  There is no reason to write the underlying code to only 
    handle a single value
  */
  load: function(innInfo, start, days) {
    this.m_list = innInfo;
    this.m_web.load(innInfo, start, days);
  },

  loadOneInn: function(id, eid, type, start, days) {
    var innInfo = new Object();
    innInfo[id] = {eid:eid,type:type};

    if(this.m_web.shouldHandle(type)) {
      this.m_web.load(innInfo, start, days);
    }
  },

  handleResponse: function (isSuccess, data) {
    //eventually we want to merge this with previous calls and sync.
    this.m_data = data;
    this.setDefaultData();
    this.m_callback(isSuccess, this.m_data); 
  },

  setDefaultData : function () {
  }
});

var cWebervationsData = cWebervationsBase.extend ({
  initialize: function (callback, options) {
    this.setOptions({url : "/services/webervations/summary_availability.asp"});
    this.parent(callback, options);
  },

  handleResponse: function(text) {
    var dArray = this.parseText(text);
    if(dArray === false) {
      this.m_callback(false, this.defaultData());
      return;  
    }

    var data = new Object();
    for(var i = 0; i < dArray.length; i++) {
      var lineData = dArray[i];
      if(lineData.length==2) {
        var id = lineData[0];
        if(this.m_innList.hasOwnProperty(id)) {
          data[id] = new cWAvailResult(this.m_innList[id]["wid"], lineData[1]);
        }
      }
    }

    this.m_callback(true, data);
  },

  defaultData: function() {
    var data = new Object();
    for (id in this.m_list) {
      if(this.m_list[id]["type"] == "w" || this.m_list[id]["type"] == "W") {
        data[id] = new cNAAvailResult(id);
      }
    }
    return data;
  }
});

var cWebervationsDetailData = cWebervationsBase.extend ({
  initialize: function (callback, options) {
    this.setOptions({url : "/services/webervations/room_availability.asp"});
    this.parent(callback, options);
  },

  handleResponse: function(text) {
    var dArray = this.parseText(text);
    if(dArray === false) {
      this.m_callback(false, this.defaultData());
      return;  
    }

    var data = new Object();
    for(var i = 0; i < dArray.length; i++) {
      var id = dArray[i][0];
      if(!data.hasOwnProperty(id)) {
        data[id] = new Array();
      }
      data[id].push(new cWRoomAvailResult(dArray[i], this.m_start, this.m_days));
    }

    this.m_callback(true, data);
  },

  defaultData: function() {
    var data = new Object();
    for (id in this.m_list) {
      if(this.shouldHandle(this.m_list[id]["type"])) {
        data[id] = new Array();
      }
    }
    return data;
  }
});

/********************************** 
  Calendar form handling
  Handle multiple calendars and calendar/text box communication in a somewhat
  generic way.
**********************************/

var cCalendarFormController = new Class({
  options : {
    caloptions: {},
    textDefault: "mm/dd/yy"
  },

  initialize : function(form, textfields, containers, options) {
    this.setOptions(options);

    if(textfields.length != containers.length) {
      throw new Error("Internal Error: Container and field counts do not match");
    }

    this.m_defaultText = "";
    this.m_form = form;
    this.m_calFields = textfields;

    this.m_cals = new Object;
    for(var i = 0; i < textfields.length; i++) {
      var field = textfields[i];
      var container = containers[i];

      if(!this.m_form[field]) {
        throw new Error("Internal Error: calendar field not found");
      }

      if(!$(container)) {
        throw new Error("Internal Error: calendar container not found");
      }

      if(!this.m_form[field].value) {
        this.m_form[field].value = this.options.textDefault;
      }

      var cal = new YAHOO.widget.Calendar(field+"-calid", container, this.options.caloptions);
      cal = this.initCal(cal);
      cal.selectEvent.subscribe(this.makeSelectHandler(field), this, true); 
      this.m_cals[field] = cal;
    }
   
    for (var i = 0; i < this.m_calFields.length; i++) {
      var ele = $(this.m_form[this.m_calFields[i]]);
      ele.addEvent("focus", this.makeTextClearDefaultHandler(ele));
    }

    //mozilla does't always initialize form state on refresh, which can leave us 
    //in an unfortunate state.  We make sure that we start with the fields enabled.
    this.setCalFieldsDisabled(false);
  },

  toggleCal: function(calfield) {
    var cal = this.m_cals[this.getName(calfield)];
    if(cal.isVisible()) {
      this.hideAllCals();
      this.setCalFieldsDisabled(false);
    }
    else {
      this.showCal(calfield);
    }
  },

  hideCal: function(calfield) {
    var cal = this.m_cals[this.getName(calfield)];
    if(cal.isVisible()) {
      this.hideAllCals();
      this.setCalFieldsDisabled(false);
    }
  },

  showCal: function(calfield) {
    var cal = this.m_cals[this.getName(calfield)];
    this.hideAllCals(cal);
    this.setCalFieldsDisabled(true);

    var initDate = this.getDefaultDate(calfield);

    //cal.select(initDate);
    cal.select(initDate);
    cal.cfg.setProperty("pagedate", (initDate.getMonth()+1) + "/" + initDate.getFullYear()); 
    cal.render();
    cal.show();
  },

  /*  
    "private functions"
  */

  setCalFieldsDisabled: function(disabled) {
    for (var i = 0; i < this.m_calFields.length; i++) {
      var field = this.m_calFields[i];
      this.m_form[field].disabled = disabled;
    }
  },

  getName: function(textfield) {
    if(typeof textfield == "string") {
      return textfield;
    }
    else {
      return textfield.name;
    }
  },


  hideAllCals: function(exception) {
    for(index in this.m_cals) {
      if(!(exception && exception == this.m_cals[index])) {
        this.m_cals[index].hide();
      }
    }
  },

  getDefaultDate: function(calfield) {
    //default date to value in the selected calendar
    var parser = new DateParser();
    var initDate = parser.parse(this.m_form[calfield].value, "m/d/y");
    if(initDate) {
      return initDate;
    }

    //if that's no good, default to the next calendar in the list, wrapping
    var index = this.m_calFields.indexOf(calfield);
    if(index != -1) {
      var nextCal = this.m_calFields[(index+1) % this.m_calFields.length];
      initDate = parser.parse(this.m_form[nextCal].value, "m/d/y");
      if(initDate) {
        return initDate;
      }
    }
    
    //otherwise go with today
    return new Date();
  },

  initCal: function(cal) {
    cal.hide = function () {
      this.customIsVisible = false;
      return YAHOO.widget.Calendar.prototype.hide.call(this) ;
    }

    cal.show = function () {
      this.customIsVisible = true;
      return YAHOO.widget.Calendar.prototype.show.call(this) ;
    }

    cal.isVisible = function () {
      return this.customIsVisible;
    }

    cal.toggle = function () {
      if(this.customIsVisible) {
        this.hide();
      }
      else {
        this.show();
      }
    }
  
    cal.render();
    cal.hide();
    return cal;
  }, 


  makeSelectHandler: function(field) {
    return function(type, args, obj) {
      //unfortunately this gets triggered when the calendar is set via js
      //we'd like to be able to set the initial value of the calendar from 
      //the text box on popup without changing the textbox.  As a workaround
      //we'll only handle the event if the calendar is showing.
      var cal = this.m_cals[field];
      if(cal.isVisible()) {
        var d = args[0][0];
        var year = d[0];
        var month = d[1];
        var day = d[2];

        year = year % 100;
        if(year < 10) {
          year = "0" + year;
        }

        this.m_form[field].value = month + "/" + day + "/" + year;
        this.m_form[field].fireEvent("change");
        this.hideCal(field); 
      }
    }
  },

  makeTextClearDefaultHandler: function(element) {
    return function () {
      if(element.value == this.options.textDefault) {
        element.value = "";
      }
    }.bind(this);
  }

});

cCalendarFormController.implement(new Options);


/********************************** 
  City page specific functions.  
  If we open the availabilty search to other pages, then we'll need to reorg this.
**********************************/

var cAvailabilityController = new Class({
  //lookup for data formatting.  There's a better way to do this I'm sure
  monthNames : {
    1:"January", 2:"February", 3:"March", 4:"April", 5:"May", 6:"June",
    7:"July", 8:"August", 9:"September", 10:"October", 11:"November", 12:"December"
  },

  options: {
    format : "m/d/y",
    errorDiv : "availabilityFormError",
    summary: "availSummary",
    summaryCheckin : "availSummaryCheckin",
    summaryCheckout : "availSummaryCheckout",
    innRowPrefix: "row-",
    innDivPrefix: "div-",
    clearResult: "clearResult",
    cookie: ""
  },

  initialize: function(form, inns, options) {
    this.form = form;
    this.inns = inns;
    this.setOptions(options);
    this.availabilityProcessor = new cAvailability(this.processResults.bind(this));

    var checkinField = this.form.checkin;
    var checkoutField = this.form.checkout;
 
    $(checkinField).addEvent("keypress", this.processOnEnterPress.bind(this));
    $(checkoutField).addEvent("keypress", this.processOnEnterPress.bind(this));
    
    if(this.options.cookie) {
      var dString = this.getCookieField(0);
      if(dString) {
        checkinField.value = this.formatFieldDate(new Date(Number(dString)));
      }

      dString = this.getCookieField(1);
      if(dString) {
        checkoutField.value = this.formatFieldDate(new Date(Number(dString)));
      }

      var parser = new DateParser();
      var checkin = parser.parse(checkinField.value, this.options.format);
      var checkout = parser.parse(checkoutField.value, this.options.format);
      
      $(checkinField).addEvent("change", function() {this.updateCookie(0, checkinField);}.bind(this) );
      $(checkoutField).addEvent("change", function() {this.updateCookie(1, checkoutField);}.bind(this) );

      if(checkin && checkout) {
        this.process();
	    }
    }
  },

  process: function() {
    this.clearError();
    this.clearResults(this.inns);

 var okToProcess = false;
    for(index in this.inns) {
       eid=this.inns[index]['eid'];
       if(eid){
	 okToProcess=true;
	}
    }

    if(okToProcess === false){
	 this.setError("None of the inns in this region have provided us with availability information. Please contact the inns directly to find out if they have rooms available.");
      return;
	}
    else{

    var checkinField = this.form.checkin;
    var checkoutField = this.form.checkout;

    var parser = new DateParser();
    var checkin = parser.parse(checkinField.value, this.options.format);
    if(!checkin) {
      this.setError("Check In: " + parser.getErrorMessage());
      return;
    }

    var checkout = parser.parse(checkoutField.value, this.options.format);
    if(!checkout) {
      this.setError("Check Out: " + parser.getErrorMessage());
      return;
    }

    checkinField.value = this.formatFieldDate(checkin);
    checkoutField.value = this.formatFieldDate(checkout);
    
    var DateMath = YAHOO.widget.DateMath;

    //date math inexplicably clears to noon rather than midnight.
    var today = DateMath.clearTime(new Date());
    today.setHours(0)
    
    if(DateMath.before(checkin, today)) {
      this.setError("Check In date cannot be before today.");
      return;
    }

    if(!DateMath.before(checkin, checkout)) {
      this.setError("Check In date must be before Checkout date.");
      return;
    }

    if(!DateMath.before(checkout, DateMath.add(today, YAHOO.widget.DateMath.DAY, 365))) {
      var message = "We are currently unable to check availability more than a " + 
        "year in advance. Please try again with nearer dates."
      this.setError(message);
      return;
    }

    numberOfNights = this.getNumberOfNights(checkin, checkout);
    if(numberOfNights <= 0) {
      setError("Stay must be for at least one night.");
      return;
    }
    
    if(numberOfNights >= 31) {
      setError("Stay can be for no more than 31 nights.");
      return;
    }
   
    $(this.options.summaryCheckin).innerHTML = this.formatSummaryDate(checkin); 
    $(this.options.summaryCheckout).innerHTML = this.formatSummaryDate(checkout);
    this.availabilityProcessor.load(this.inns, checkin, numberOfNights);
  }  
},

  setError : function(message) {
    $(this.options.errorDiv).innerHTML = message
    $(this.options.errorDiv).className = "errorText";
  },

  clearError : function() {
    $(this.options.errorDiv).innerHTML = "";
    $(this.options.errorDiv).className = "nodisplay";
  },

  clearResults : function(data) {
    for(var id in data) {
      $(this.options.innRowPrefix+id).className = "nodisplay";
    }
  },

  processResults : function(isSuccessful, data) {
    if(!isSuccessful) {
      var message = "One or more of our partners may not have responded to this request" +
        "for information. You may want to try your request again in a few minutes.";
      this.setError(message);
    }
    
    $(this.options.summary).setStyle("display", "block");
    
    var text = "";
    for(var id in data) {
      var row = $(this.options.innRowPrefix+id);
      var div = $(this.options.innDivPrefix+id);
      switch (data[id].getAvailabilityStatus()) {
        case 'Y':
          row.className = "availabilityYes";        
          div.innerHTML = "Rooms Available!";
          break;
        default :
          row.className = "availabilityMaybe";
          div.innerHTML = "Contact Inn for Availability";        
          break;
      }
    }
  },
  
  processOnEnterPress : function(event) {
    var event = new Event(event);
    if(event.key == "enter") {
      this.process();
    }
  },

  updateCookie : function (index, field) {
    var parser = new DateParser();
    var dObj = parser.parse(field.value, this.options.format);
    if(!dObj) {
      this.setCookieField(index, ""); 
    }
    else {
      this.setCookieField(index, dObj.getTime()); 
    }
  },

  setCookieField : function(index, value) {
    var cookieArray = new Array();
    var cookieStr = Cookie.get(this.options.cookie);
    if(cookieStr) {
      cookieArray = cookieStr.split("-");
    }
    cookieArray[index] = value;
    Cookie.set(this.options.cookie, cookieArray.join("-"), {path:"/"});
  },

  getCookieField : function(index, value) {
    var cookieArray = new Array();
    var cookieStr = Cookie.get(this.options.cookie);
    if(cookieStr) {
      cookieArray = cookieStr.split("-");
    }
    return cookieArray[index];
  },

  formatSummaryDate : function(d) {
    var month = this.monthNames[d.getMonth() + 1];
    var day = d.getDate();
    var year = d.getFullYear();
    return month + " " + day + ", " + year; 
  },

  formatFieldDate : function(d) {
    var year = d.getFullYear() % 100;
    if(year < 10) {
      year = "0" + year;
    }
    return (d.getMonth() + 1) + "/" + d.getDate() + "/" + year; 
  },

  getNumberOfNights : function(start, end) {
    var DateMath = YAHOO.widget.DateMath;
    var referenceYear = start.getFullYear();
    var count = DateMath.getDayOffset(end, referenceYear) -
      DateMath.getDayOffset(start, referenceYear);
    return count;
  }
});
cAvailabilityController.implement(new Options);

var cRoomAvailabilityController = new Class({
  //lookup for data formatting.  There's a better way to do this I'm sure
  monthNames : {
    1:"January", 2:"February", 3:"March", 4:"April", 5:"May", 6:"June",
    7:"July", 8:"August", 9:"September", 10:"October", 11:"November", 12:"December"
  },

 options: {
    format : "m/d/y",
    errorDiv : "availabilityFormError",
    summary: "availSummary",
    summaryCheckin : "availSummaryCheckin",
    summaryCheckout : "availSummaryCheckout",
    innRowPrefix: "row-",
    innDivPrefix: "div-",
    clearResult: "clearResult",
    cookie: ""
  },


  initialize: function(form, id, eid, type, options) {
    this.form = form;
    this.id = id;
    this.eid = eid;
    this.type = type; 
    this.setOptions(options);
    this.availabilityProcessor = new cRoomAvailability(this.processResults.bind(this));

    var checkinField = this.form.checkin;
    var checkoutField = this.form.checkout;
 
    $(checkinField).addEvent("keypress", this.processOnEnterPress.bind(this));
    $(checkoutField).addEvent("keypress", this.processOnEnterPress.bind(this));
    
    if(this.options.cookie) {
      var dString = this.getCookieField(0);
      if(dString) {
        checkinField.value = this.formatFieldDate(new Date(Number(dString)));
      }

      dString = this.getCookieField(1);
      if(dString) {
        checkoutField.value = this.formatFieldDate(new Date(Number(dString)));
      }

      var parser = new DateParser();
      var checkin = parser.parse(checkinField.value, this.options.format);
      var checkout = parser.parse(checkoutField.value, this.options.format);

      $(checkinField).addEvent("change", function() {this.updateCookie(0, checkinField);}.bind(this) );
      $(checkoutField).addEvent("change", function() {this.updateCookie(1, checkoutField);}.bind(this) );

      if(checkin && checkout) {
	if(!this.validateDate(checkin, checkout, false)){
		this.process();
        }
	    }
    }
  },

  validateDate: function(checkin, checkout, display) {
    var DateMath = YAHOO.widget.DateMath;
    //date math inexplicably clears to noon rather than midnight.
    var today = DateMath.clearTime(new Date());
    today.setHours(0)
    
    if(DateMath.before(checkin, today)) {
      if(display){
      	this.setError("Check In date cannot be before today.");
      }
      return true;
    }

    if(!DateMath.before(checkin, checkout)) {
      if(display){
      	this.setError("Check In date must be before Checkout date.");
      }
      return true;
    }

    if(!DateMath.before(checkout, DateMath.add(today, YAHOO.widget.DateMath.DAY, 365))) {
      if(display){
         var message = "We are currently unable to check availability more than a " + 
           "year in advance. Please try again with nearer dates."
         this.setError(message);
       }
       return true;
    }

    numberOfNights = this.getNumberOfNights(checkin, checkout);
    if(numberOfNights <= 0) {
      if(display){
        this.setError("Stay must be for at least one night.");
      }
      return true;
    }
    
    if(numberOfNights >= 31) {
      if(display){
        this.setError("Stay can be for no more than 31 nights.");
      }
      return true;
    }
   
    return;
    
  },

  process: function() {
    this.clearError();
    this.clearResults();

    var checkinField = this.form.checkin;
    var checkoutField = this.form.checkout;

    var parser = new DateParser();
    var checkin = parser.parse(checkinField.value, this.options.format);
    if(!checkin) {
      this.setError("Check In: " + parser.getErrorMessage());
      return;
    }

    var checkout = parser.parse(checkoutField.value, this.options.format);
    if(!checkout) {
      this.setError("Check Out: " + parser.getErrorMessage());
      return;
    }

    checkinField.value = this.formatFieldDate(checkin);
    checkoutField.value = this.formatFieldDate(checkout);
    
    if(this.validateDate(checkin, checkout, true)){
	return;
    }
 
    $(this.options.summaryCheckin).innerHTML = this.formatSummaryDate(checkin); 
    $(this.options.summaryCheckout).innerHTML = this.formatSummaryDate(checkout);
    this.availabilityProcessor.loadOneInn(this.id, this.eid, this.type, checkin, numberOfNights);
  },

  setError : function(message) {
      $(this.options.summary).setStyle("display", "none");
    $(this.options.errorDiv).innerHTML = message
    $(this.options.errorDiv).className = "errorText";
  },

  clearError : function() {
    $(this.options.errorDiv).innerHTML = "";
    $(this.options.errorDiv).className = "nodisplay";
  },

  clearResults : function() {
      $(this.options.summary).setStyle("display", "none");
      $(this.options.clearResult).setStyle("display", "none"); 
  },

  processResults : function(isSuccessful, data) {
    if(!isSuccessful) {
      var message = "One or more of our partners may not have responded to this request" +
        "for information. You may want to try your request again in a few minutes.";
      this.setError(message);
    }
    else{  
    var htmlString = ""; 
    var text = "";
    htmlString = '<table width="100%" border="0" cellspacing="0" cellpadding="1"><tr><th class="roomHeaderL">Room</th><th class="roomHeaderR">Cost / night (avg)</th><th class="roomHeaderR">Total Cost</th><th></th></tr>';
    
    var div = $(this.options.innDiv);

    totalRoom=0;
    numRooms=0;
    for(var id in data) {
      //var row = $(this.options.innRow);
      var bookingUrl = "";
      var numNights = "";
      var availableRoom=0;
      roomData=data[id];
      numRooms=roomData[0].days;
      for(i=0; i<roomData.length; i++){
  	if(roomData[i].getIsAvailable()) {
		availableRoom=availableRoom+1;
                bookingUrl = roomData[i].getBookingUrl('') ;
        	name = roomData[i].roomName;
        	totalCost = roomData[i].getTotal();
        	displayCost = "$"+totalCost;
		days = roomData[i].days;
		averageRate = parseInt(totalCost/days);
 		displayRate="$"+averageRate;
		if(roomData[i].showRates == 'N'){
			htmlString = htmlString + '<tr><td class="roomName">' + name +  '</td><td class="roomDetail" colspan="2">Contact us for pricing info</td><td class="greenlink">';
		}
		else{
			htmlString = htmlString + '<tr><td class="roomName">' + name +  '</td><td class="roomDetail">' + displayRate + '</td><td class="roomDetail">' + displayCost + '</td><td class="greenlink">';
		}
		if(roomData[i].reservations == 'Y'){
			htmlString = htmlString + '<a class="booknow_link" target="_self" href="' + bookingUrl + '">Book Now!</a>';
		}
		htmlString = htmlString + '</td></tr>';
	  }
	  totalRoom=totalRoom+1;	
	}
      }
      if(totalRoom>0){
	if(availableRoom>0){ 
    		$(this.options.summary).setStyle("display", "block");
    		$(this.options.clearResult).setStyle("display", "block");
      		//row.className = "availabilityYes";        
      		div.innerHTML = htmlString;
		YAHOO.util.Event.addListener([YAHOO.util.Dom.getElementsByClassName("booknow_link")],"click",trackingCallBack);
	}
      	else{
      		if(this.id != "20622") {
                    if(numRooms==1) {
			var message="We don't have any rooms available for that night. Please check and see if we have rooms available for other nights on which you might visit us.";
    		    }
		    else {
                        var message="We don't have any single rooms available for the period of time you searched for, but if you contact us we may be able to accomodate your dates using multiple rooms.";
                    }
		}
		else{
			var message="No rooms available at this time.";
		}
		this.setError(message);
      	}
      }
      else{
		var message="Room availability information for our inn is coming soon! In the mean time, please contact us with inquiries about specific rooms and/or specific dates.";
		this.setError(message);
      }
    }	 
  },
  
  processOnEnterPress : function(event) {
    var event = new Event(event);
    if(event.key == "enter") {
      this.process();
    }
  },

  deleteCookie :function (){
    this.setCookieField(0, "");
    this.setCookieField(1, "");
    
    var checkinField = this.form.checkin;
    var checkoutField = this.form.checkout;
    checkoutField.value = "mm/dd/yy";
    checkinField.value = "mm/dd/yy";
  },

  updateCookie : function (index, field) {
    var parser = new DateParser();
    var dObj = parser.parse(field.value, this.options.format);
    if(!dObj) {
      this.setCookieField(index, ""); 
    }
    else {
      this.setCookieField(index, dObj.getTime()); 
    }
  },

  setCookieField : function(index, value) {
    var cookieArray = new Array();
    var cookieStr = Cookie.get(this.options.cookie);
    if(cookieStr) {
      cookieArray = cookieStr.split("-");
    }
    cookieArray[index] = value;
    Cookie.set(this.options.cookie, cookieArray.join("-"), {path:"/"});
  },

  getCookieField : function(index, value) {
    var cookieArray = new Array();
    var cookieStr = Cookie.get(this.options.cookie);
    if(cookieStr) {
      cookieArray = cookieStr.split("-");
    }
    return cookieArray[index];
  },

  formatSummaryDate : function(d) {
    var month = this.monthNames[d.getMonth() + 1];
    var day = d.getDate();
    var year = d.getFullYear();
    return month + " " + day + ", " + year; 
  },

  formatFieldDate : function(d) {
    var year = d.getFullYear() % 100;
    if(year < 10) {
      year = "0" + year;
    }
    return (d.getMonth() + 1) + "/" + d.getDate() + "/" + year; 
  },

  getNumberOfNights : function(start, end) {
    var DateMath = YAHOO.widget.DateMath;
    var referenceYear = start.getFullYear();
    var count = DateMath.getDayOffset(end, referenceYear) -
      DateMath.getDayOffset(start, referenceYear);
    return count;
  }
});
cRoomAvailabilityController.implement(new Options);

