(function($) {
  jQuery.fn.rangepicker = function(settings) {
   	settings = jQuery.extend({
  		clientMode: true
  	}, settings);
 
    var picker = this;
    
    // YUI functions
    function IntervalCalendar(container, cfg) {
        /**
        * The interval state, which counts the number of interval endpoints that have
        * been selected (0 to 2).
        * 
        * @private
        * @type Number
        */
        this._iState = 0;

        // Must be a multi-select CalendarGroup
        cfg = cfg || {};
        cfg.multi_select = true;

        // Call parent constructor
        IntervalCalendar.superclass.constructor.call(this, container, cfg);

        // Subscribe internal event handlers
        this.beforeSelectEvent.subscribe(this._intervalOnBeforeSelect, this, true);
        this.selectEvent.subscribe(this._intervalOnSelect, this, true);
        this.beforeDeselectEvent.subscribe(this._intervalOnBeforeDeselect, this, true);
        this.deselectEvent.subscribe(this._intervalOnDeselect, this, true);
    }

    IntervalCalendar._DEFAULT_CONFIG = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG;

    YAHOO.lang.extend(IntervalCalendar, YAHOO.widget.CalendarGroup, {

        _dateString : function(d) {
            var a = [];
            a[this.cfg.getProperty(IntervalCalendar._DEFAULT_CONFIG.MDY_MONTH_POSITION.key)-1] = (d.getMonth() + 1);
            a[this.cfg.getProperty(IntervalCalendar._DEFAULT_CONFIG.MDY_DAY_POSITION.key)-1] = d.getDate();
            a[this.cfg.getProperty(IntervalCalendar._DEFAULT_CONFIG.MDY_YEAR_POSITION.key)-1] = d.getFullYear();
            var s = this.cfg.getProperty(IntervalCalendar._DEFAULT_CONFIG.DATE_FIELD_DELIMITER.key);
            return a.join(s);
        },

        _dateIntervalString : function(l, u) {
            var s = this.cfg.getProperty(IntervalCalendar._DEFAULT_CONFIG.DATE_RANGE_DELIMITER.key);
            return (this._dateString(l)
                    + s + this._dateString(u));
        },

        getInterval : function() {
            // Get selected dates
            var dates = this.getSelectedDates();
            if(dates.length > 0) {
                // Return lower and upper date in array
                var l = dates[0];
                var u = dates[dates.length - 1];
                return [l, u];
            }
            else {
                // No dates selected, return empty array
                return [];
            }
        },

        setInterval : function(d1, d2) {
            // Determine lower and upper dates
            var b = (d1 <= d2);
            var l = b ? d1 : d2;
            var u = b ? d2 : d1;
            // Update configuration
            this.cfg.setProperty('selected', this._dateIntervalString(l, u), false);
            this._iState = 2;
        },

        resetInterval : function() {
            // Update configuration
            this.cfg.setProperty('selected', [], false);
            this._iState = 0;
        },

        _intervalOnBeforeSelect : function(t,a,o) {
            // Update interval state
            this._iState = (this._iState + 1) % 3;
            if(this._iState == 0) {
                // If starting over with upcoming selection, first deselect all
                this.deselectAll();
                this._iState++;
            }
        },

        _intervalOnSelect : function(t,a,o) {
            // Get selected dates
            var dates = this.getSelectedDates();
            if(dates.length > 1) {
                /* If more than one date is selected, ensure that the entire interval
                    between and including them is selected */
                var l = dates[0];
                var u = dates[dates.length - 1];
                this.cfg.setProperty('selected', this._dateIntervalString(l, u), false);
            }
            // Render changes
            this.render();
        },

        _intervalOnBeforeDeselect : function(t,a,o) {
            if(this._iState != 0) {
                /* If part of an interval is already selected, then swallow up
                    this event because it is superfluous (see _intervalOnDeselect) */
                return false;
            }
        },

        _intervalOnDeselect : function(t,a,o) {
            if(this._iState != 0) {
                // If part of an interval is already selected, then first deselect all
                this._iState = 0;
                this.deselectAll();

                // Get individual date deselected and page containing it
                var d = a[0];
                var date = YAHOO.widget.DateMath.getDate(d[0], d[1] - 1, d[2]);
                var page = this.getCalendarPage(date);
                if(page) {
                    // Now (re)select the individual date
                    page.beforeSelectEvent.fire();
                    this.cfg.setProperty('selected', this._dateString(date), false);
                    page.selectEvent.fire([d]);
                }
                // Swallow up since we called deselectAll above
                return false;
            }
        }
    });

    YAHOO.namespace("tickstart.calendar");
    YAHOO.tickstart.calendar.IntervalCalendar = IntervalCalendar;
    
    var startDate, endDate, interval, rangePicker;

    var rangePicker = new YAHOO.tickstart.calendar.IntervalCalendar("cal1Container", { pages:4, pagedate: Date.parse("t - 90d")});
    rangePicker.select([Date.parse(picker.find('.start-date').val()), Date.today() ]);
    rangePicker.selectEvent.subscribe(function() {
      interval = this.getInterval();

      if (interval.length == 2) {
        startDate = interval[0];
        picker.find('.start-date').val(startDate.toString('MM/dd/yyyy'));

        if (interval[0].getTime() != interval[1].getTime()) {
         endDate = interval[1];
         picker.find('.end-date').val(endDate.toString('MM/dd/yyyy'));

        } else {
          picker.find('.end-date').val('');
        }
      }
     }, rangePicker, true);

     rangePicker.render();

 	  
 	  // handle the custom button 
    picker.find('li.custom a').click(function() {
      $(this).parents('ul').find('li').removeClass('selected');
        $(this).parents('li').toggleClass('selected');
      picker.find('.form').slideToggle();
      return false;
    });
  
    
    // update the header with new dates
    picker.find(".header").bind("range-change", function (event, start, end) {
      $(this).children(':header').html(start.toString("MMM d, yyyy") + " - " + end.toString("MMM d, yyyy"));
      picker.find('.form').slideUp();
      if(typeof(settings.onChange) == "function"){
          settings.onChange(start, end);
      }
    });
    
    // handle the 'done' button on the form
    picker.find('a.done').click(function() {
      rangePicker.clear();
      rangePicker.subtractMonths(3);
      rangePicker.select([Date.parse(picker.find('.start-date').val()), Date.parse($('.end-date').val())]);
      picker.find('.form').slideUp('slow');
      var start = Date.parse(picker.find('.start-date').val());
      var end = Date.parse(picker.find('.end-date').val());
      if(settings.clientMode) {
        picker.find('.header').trigger("range-change", [start, end]);
        return false;
      }
      else {
        var href = $(this).attr('href') + '/{start_year}/{start_yday}/{end_year}/{end_yday}'
        var url = href.template({
          start_year: start.getFullYear(),
          start_yday: start.getOrdinalNumber(),
          end_year: end.getFullYear(),
          end_yday: end.getOrdinalNumber()
        });
        window.location.href = url;
        return false;
      }
    });

    
    picker.find('.cancel').click(function() {
      var dates = picker.find('.header :header').text().split('-');
      var start = Date.parse(jQuery.trim(dates[0]));
      var end = Date.parse(jQuery.trim(dates[1]));
      picker.find('.form').hide();
      picker.find('.start-date').val(start.toString('M/dd/yyyy'));
      picker.find('.end-date').val(end.toString('M/dd/yyyy'));
      rangePicker.clear();
      rangePicker.subtractMonths(3);
      rangePicker.select([start,end]);
      return false;
    });
  

    
    // mark custom preset as selected if not selected
    picker.find('ul.presets').each(function(){
      items = $(this).children();
      selected = items.filter('.selected');
      if (selected.size() < 1) {
        $(this).children('.custom').eq(0).addClass('selected');
      }
    });
  
  
    if(settings.clientMode) {
      picker.find('li a').click(function() {
        $(this).parents('ul').find('li').removeClass('selected');
        $(this).parents('li').toggleClass('selected');
        return false;
      });

      picker.find('li:not(.custom) a').click(function() {
    
        var text = $(this).text();
        switch(text){
          case 'max':
            picker.find('.form').hide();
            rangePicker.clear();
            rangePicker.subtractMonths(3);
            rangePicker.select([Date.parse('t - 3y'), Date.today()]);
            picker.find('.header').trigger("range-change", [Date.parse('t - 3y'), Date.today()]);
            break;
          default:
            var date = Date.parse('t - ' + text);
            if(date){
              picker.find('.form').hide();
              rangePicker.clear();
              rangePicker.subtractMonths(3);
              picker.find('.end-date').val(Date.today().toString('M/dd/yyyy'));
              picker.find('.start-date').val(date.toString('M/dd/yyyy'));
              rangePicker.select([Date.parse(picker.find('.start-date').val()), Date.parse(picker.find('.end-date').val())]);
              picker.find('.header').trigger("range-change", [date, Date.today()]);
            }
        }
        return false;
      });
    

    
    } // end clientMode 
  };
})(jQuery);