$(function(){

  var month_abbrs = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(/ /);

  // We can figure out who the siblings date select
  // fields are by stripping off the last part of their name.
  var base_input_name = function(input) {
    return $(input).attr("name").replace(/^(day|month|year)_of_/,"");
  };

  // We can find out how many days are in a given month/year
  // by pushing the day-or-the-month value in a date
  // constructor to 32 and watching for the resulting offset
  // in the next month.  **HACK!**
  var number_of_days_for_month = function(month, year) {
    if((month=parseInt(month)) && (year=parseInt(year))) {
      return 32 - new Date(year, month-1, 32).getDate();
    }
    return 31; // default to 31.
  };

  // The sibling select of a field named birthday[day] is
  // birthday[year] -- so we use this function by passing
  // a reference of the birthday[day] select field and a
  // value of 'year' to get the related 'year' field.
  // Sibling select must be contained within the same form
  // element.
  var sibling_select = function(input, part) {
    return $(input).parents("form").find("select[name='"+part+"_of_"+base_input_name(input)+"']");
  };
  
  // Dynamically removes or appends options to the day
  // select based on the number of days passed in as
  // new_last_day.
  var update_day_options = function(input, new_last_day) {
    var old_last_day = parseInt($(input).find("option:last").attr("value"));
    if(old_last_day < new_last_day) {
      for(var d=old_last_day+1; d<=new_last_day; d++) {
        $(input).append('<option value="'+d+'">'+d+'</option>');
      }
    }
    if(old_last_day > new_last_day) {
      for(var d=old_last_day; d>new_last_day; d--) {
        $(input).find("option[value="+d+"]").remove();
      };
    }
  };

  var update_hidden_input = function(input) {
    var hidden_input = $(input).parents("form").find("input[name='"+base_input_name(input)+"']");
    var year=sibling_select(hidden_input, "year").val();
    var month=sibling_select(hidden_input, "month").val();
    var day=sibling_select(hidden_input, "day").val();
    if(year && month && day) hidden_input.val(month+"/"+day+"/"+year);
    else hidden_input.val("");
    $(hidden_input).change();
  };
  
  // If we find a text input for a date, lets convert it to
  // a hidden input, create the sibling select boxes for the
  // month, day, and year, and then set them based on the
  // value in the hidden input.
  $("input.date").hide().each(function(){
    var today=new Date();
    var input=$(this);
    var id=input.attr("id");
    var name=input.attr("name");
    var currClass = input.attr('class');
    var errorClass = currClass.indexOf("error") > -1?" error ":"";
    input.after(
      '<select class="month_select required' + errorClass + '" name="month_of_'+name+'" id="month_of_'+id+'" reason_required="Please select a month.">'+
      '<option value="" selected="selected">Month:</option>'+
      (function(){
        var html='';
        for(var m=0;m<12;m++){
          html += '<option value="'+(m+1)+'">'+month_abbrs[m]+'</option>';
        }
        return html;
      })()+
      '</select>'+
      '<select class="day_select required' + errorClass + '" name="day_of_'+name+'" id="day_of_'+id+'" reason_required="Please select a day.">'+
      '<option value="" selected="selected">Day:</option>'+
      (function(){
        var html='';
        for(var d=1;d<32;d++){
          html += '<option value="'+(d)+'">'+(d)+'</option>';
        }
        return html;
      })()+
      '</select>'+
      '<select class="year_select required' + errorClass + '" name="year_of_'+name+'" id="year_of_'+id+'" reason_required="Please select a year.">'+
      '<option value="" selected="selected">Year:</option>'+
      (function(){
        var year=today.getFullYear();
        var html='';
        for(var y=year;y>1900;y--){
          html += '<option value="'+(y)+'">'+y+'</option>';
        }
        return html;
      })()+
      '</select>'
    );
    var cur_parts = input.val().toString().split("/");
    var cur_month = cur_parts[0];
    var cur_day   = cur_parts[1];
    var cur_year  = cur_parts[2];
    $("#year_of_"+id).val(cur_year);
    $("#month_of_"+id).val(cur_month);
    $("#day_of_"+id).val(cur_day);
  });

  // Lets ensure we have the correct number of days for
  // the currently selected month.
  $("select.day_select").each(function(){
    update_day_options(this, number_of_days_for_month(
      sibling_select(this, 'month').val(),
      sibling_select(this, 'year').val()
    ));
  });

  // Update the hidden input whenever a new day value
  // is selected.
  $("select.day_select").change(function(){
    update_hidden_input(this);
  });
  
  // Update the number of options for the day select
  // based on the newly selected month and update the
  // hidden input.
  $("select.month_select").change(function(){
    update_day_options(
      sibling_select(this, 'day'),
      number_of_days_for_month(
        $(this).val(),
        sibling_select(this, 'year').val()
      )
    );
    update_hidden_input(this);
  });

  // Update the number of options for the day select
  // based on newly selected year (re:leapyear) and
  // update the hidden input.
  $("select.year_select").change(function(){
    update_day_options(
      sibling_select(this, 'day'),
      number_of_days_for_month(
        sibling_select(this, 'month').val(),
        $(this).val()
      )
    );
    update_hidden_input(this);
  });

});

