Ext.ns('Ext.pv.form');

Ext.pv.form.DateCombo = Ext.extend(Ext.Container, {
	layout: 'hbox',
	width: 180,
	height: 22,
	background: '#FFFFFF',

    includeMonth: true,
    includeDay: true,
    yearEnabled: true,
    showClear: true,
    
	monthCombo: null,
	dayCombo: null,
	yearField: null,
	hiddenField: null,
	
	invalidMonthText : 'You must select a month.',
	invalidDayText : 'You must select a day.',
	invalidYearText : 'You must enter a 4 digit year.',
	
    /**
     * @cfg {String} format
     * The default date format string which can be overriden for localization support.  The format must be
     * valid according to {@link Date#parseDate} (defaults to 'm/d/Y').
     */
    format : "m/d/Y",
    /**
     * @cfg {String} altFormats
     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
     * format (defaults to 'm/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d').
     */
    altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d",
	
    // Localized strings
    shortMonthNames : [
       "Jan",
       "Feb",
       "Mar",
       "Apr",
       "May",
       "Jun",
       "Jul",
       "Aug",
       "Sep",
       "Oct",
       "Nov",
       "Dec"
    ],
    
	// private
	initComponent : function(){
	
		// Setup the date components
		this.items = new Array();
		
		if (this.includeMonth) {
			// Add the Month select box
			this.items[this.items.length] = this.createMonthComponent();
			// Add a spacer
			this.items[this.items.length] = this.createSpacer();
		}
		
		if (this.includeDay) {
			// Add the Day select box
			this.items[this.items.length] = this.createDayComponent();
			// Add a spacer
			this.items[this.items.length] = this.createSpacer();
		}
		
		if (this.yearEnabled) {
			// Add the year field
			this.items[this.items.length] = this.createYearComponent();
			// Add a spacer
			this.items[this.items.length] = this.createSpacer();
		}
		
		if (this.showClear && !this.readOnly) {
			// Add a button to clear the date 
			this.items[this.items.length] = this.createClearButton();
		}
		
		// Add a hidden field to contain the whole date value
		// This is used to submit the full date in the format m/d/Y
		this.hiddenField = new Ext.form.Hidden({
			name: this.name
		});
		this.items[this.items.length] = this.hiddenField;
		
		Ext.pv.form.DateCombo.superclass.initComponent.call(this);
		
		// Initialize data
		if (this.value!='') {
			this.setValue(this.value);
		}
	},
	
	createSpacer : function() {
		return {
			width: 5,
			xtype: 'label', 
			html: '<div style="height: 30px; background: ' + this.background + ';">&nbsp;</div>' 
		};
	},
	
	createClearButton : function() {
		return new Ext.Button({
			text : 'x',
			width : 15,
			toolTip : 'Clear',
			maxBoxWidth: 20,
			dateCombo : this, // pass reference to date combo
			handler : function (btn,evt) {
				btn.dateCombo.setValue('');
			},
			style: {marginBottom: '0px', marginTop: '0px'}
		});
	},
	
	// private
	createMonthComponent : function() {
		this.monthStore = new Ext.data.JsonStore({
	        root: 'months',
	        idProperty: 'id',
	        fields: ['id', 'label', 'numDays'],
			data: {
				months: [
		            {id: '0',  label: this.shortMonthNames[0],  numDays: 31},
		            {id: '1',  label: this.shortMonthNames[1],  numDays: 28},
		            {id: '2',  label: this.shortMonthNames[2],  numDays: 31},
		            {id: '3',  label: this.shortMonthNames[3],  numDays: 30},
		            {id: '4',  label: this.shortMonthNames[4],  numDays: 31},
		            {id: '5',  label: this.shortMonthNames[5],  numDays: 30},
		            {id: '6',  label: this.shortMonthNames[6],  numDays: 31},
		            {id: '7',  label: this.shortMonthNames[7],  numDays: 31},
		            {id: '8',  label: this.shortMonthNames[8],  numDays: 30},
		            {id: '9',  label: this.shortMonthNames[9],  numDays: 31},
		            {id: '10', label: this.shortMonthNames[10], numDays: 30},
		            {id: '11', label: this.shortMonthNames[11], numDays: 31}
				]
			}
		});
		
		this.monthCombo = new Ext.form.ComboBox({
			width : 50,
			hiddenName: this.name + '_month',
			allowBlank : this.allowBlank,
			readOnly : this.readOnly,
			disabled : this.readOnly || this.disabled,
			mode: 'local',
	    	store: this.monthStore,
	    	valueField: 'id',
	    	displayField: 'label', 
			lazyInit : false,
			editable : false,
			forceSelection : true,
		    triggerAction : 'all',
		    validationEvent: false,
		    blankText: this.invalidMonthText,
		    invalidText: this.invalidMonthText,
		    style: {marginBottom: '0px', marginTop: '0px'}
		});
		
		this.monthCombo.on('change', function() { this.onChange(); }, this);
		this.monthCombo.on('collapse', function() { this.onChange(); }, this);
		
		return this.monthCombo;
	},
	
	// private
	getNumDaysInMonth : function(month,year) {
		var record = this.monthStore.getAt(month);
		if (record==null) {
			return 31;
		} else if (year>0 && year%4==0 && month==1) {
			// Handle February in a leap year
			return record.data.numDays + 1;
		} else {
			return record.data.numDays;
		}
	},
	
	// private
	createDayArray : function(numDaysInMonth) {
		var dayArray = new Array();
		for (var i=1; i<=numDaysInMonth; i++) {
			dayArray[dayArray.length] = i;
		}
		return dayArray;
	},
	
	// private
	createDayComponent : function() {		
		this.dayCombo = new Ext.form.ComboBox({
			width : 50,
			name: this.name + '_day',
			allowBlank : this.allowBlank,
			readOnly : this.readOnly,
			disabled : this.readOnly || this.disabled,
			mode: 'local',
	    	store: this.createDayArray(31),
			lazyInit : false,
			editable : false,
			forceSelection : true,
		    triggerAction : 'all',
		    validationEvent: false,
		    blankText: this.invalidDayText,
		    invalidText: this.invalidDayText,
		    style: {marginBottom: '0px', marginTop: '0px'}
		});
		
		this.dayCombo.on('change', function() { this.onChange(); }, this);
		this.dayCombo.on('collapse', function() { this.onChange(); }, this);
		
		return this.dayCombo;
	},
	
	// private
	createYearComponent : function() {
		
		// Setup a VType for the year field
		Ext.form.VTypes['dateYearVal'] = /^\d\d\d\d$/;
		Ext.form.VTypes['dateYearMask'] = /[0-9]/; 
		Ext.form.VTypes['dateYearText'] = this.invalidYearText; 
		Ext.form.VTypes['dateYear'] = function(arg) {
		    return Ext.form.VTypes['dateYearVal'].test(arg);
		};
		
		this.yearField = new Ext.form.NumberField({
			width : 40,
			name : this.name + '_year',
			allowBlank : this.allowBlank,
			readOnly : this.readOnly,
			disabled : this.readOnly || this.disabled,
			blankText : Ext.form.VTypes['dateYearText'],
			invalidText : Ext.form.VTypes['dateYearText'],
			vtype : 'dateYear',
			style: {marginBottom: '0px', marginTop: '0px'}
		});
		
		this.yearField.on('change', function() { this.onChange(); }, this);
		
		return this.yearField;
	},
	
	// public
	isValid : function() {
		var valid = true;
		if (this.includeMonth) {
			valid = valid && this.monthCombo.isValid();
		}
		if (this.includeDay) {
			valid = valid && this.dayCombo.isValid();
		}
		if (this.yearEnabled) {
			valid = valid && this.yearField.isValid();
		}
		return valid;
	},
	
	// public
	validate : function() {
		var valid = true;
		if (this.includeMonth && !this.monthCombo.validate()) {
			valid = false;
			this.monthCombo.markInvalid();
		}
		if (this.includeDay && !this.dayCombo.validate()) {
			valid = false;
			this.dayCombo.markInvalid();
		}
		if (this.yearEnabled && !this.yearField.validate()) {
			valid = false;
			this.yearField.markInvalid();
		}
		if (!valid) {
			this.isDirty();
		}
		return valid;
	},
	
	// public
	markInvalid : function() {
		if (this.includeMonth) {
			this.monthCombo.markInvalid();
		}
		if (this.includeDay) {
			this.dayCombo.markInvalid();
		}
		if (this.yearEnabled) {
			this.yearField.markInvalid();
		}
	},
	
	// public
	clearInvalid : function() {
		if (this.includeMonth) {
			this.monthCombo.clearInvalid();
		}
		if (this.includeDay) {
			this.dayCombo.clearInvalid();
		}
		if (this.yearEnabled) {
			this.yearField.clearInvalid();
		}
	},
	
	// public
	setDisabled : function(disabled) {
		if (this.includeMonth) {
			this.monthCombo.setDisabled(disabled);
		}
		if (this.includeDay) {
			this.dayCombo.setDisabled(disabled);
		}
		if (this.yearEnabled) {
			this.yearField.setDisabled(disabled);
		}
	},
	
	// public
	enable : function() {
		this.setDisabled(false);
	},
	
	// public
	disable : function() {
		this.setDisabled(true);
	},
	
	// Will need to wait for Ext 3.0 for this
	// public
//	setReadOnly : function(readOnly) {
//		if (this.includeMonth) {
//			this.monthCombo.setReadOnly(readOnly); 
//		}
//		if (this.includeDay) {
//			this.dayCombo.setReadOnly(readOnly);
//		}
//		if (this.yearEnabled) {
//			this.yearField.setReadOnly(readOnly);
//		}
//	},
	
	// public
	focus : function() {
		if (this.includeMonth) {
			this.monthCombo.focus();
			return;
		}
		if (this.includeDay) {
			this.dayCombo.focus();
			return;
		}
		if (this.yearEnabled) {
			this.yearField.focus();
			return;
		}
	},
	
	// public
	isDirty : function() {
		return (this.value != this.hiddenField.getValue());
	},
	
	// public
	setValue : function(newValue) {
		var dte = this.parseDate(newValue);
		if (Ext.isDate(dte)) {
			this.monthCombo.setValue(dte.getMonth());
			this.dayCombo.setValue(dte.getDate());
			this.yearField.setValue(dte.getFullYear());
			this.setHiddenValue(dte);
		} else {
			this.monthCombo.setValue('');
			this.dayCombo.setValue('');
			this.yearField.setValue('');
			this.setHiddenValue('');
		}
		this.onChange();
	},
	
    // public
    onChange : function() {
    	var month = this.monthCombo.getValue();
    	var day = this.dayCombo.getValue();
    	var year = this.yearField.getValue();
    	
    	// Update the days store based on the month and year (if required).
    	if (month>=0) {
    		var numDaysInMonth = this.getNumDaysInMonth(month,year);
    		if (numDaysInMonth!=this.dayCombo.getStore().getCount()) {
    			var dayArray = this.createDayArray(numDaysInMonth);
    			this.dayCombo.getStore().loadData(dayArray);
    			if (day>dayArray.length) {
    				this.dayCombo.setValue(dayArray.length);
    				
    				month = this.monthCombo.getValue();
    		    	day = this.dayCombo.getValue();
    		    	year = this.yearField.getValue();
    			}
    		}
    	}
    	
    	// Update the hidden field
    	var dte = null;
    	if (month>=0 && day>0 && year>0) {
    		dte = new Date(year, month, day);
    		this.setHiddenValue(dte);
    	}
    },
	
	// public
	setHiddenValue : function(newValue) {
		this.hiddenField.setValue(this.formatDate(newValue));
	},
	
	// public
	setRawValue : function(newValue) {
		this.setValue(newValue);
	},
	
	// public
	getValue : function() {
		return this.hiddenField.getValue();
	},
	
	// public
	getDateValue : function() {
		return this.parseDate(this.hiddenField.getValue());
	},
	
	// public
	getRawValue : function() {
		return {
			month : (this.includeMonth)?this.monthCombo.getValue():'',
			day : (this.includeDay)?this.dayCombo.getValue():'',
			year : (this.includeYear)?this.yearField.getValue():''
		};
	},
	
	// public
	reset : function() {
		this.setValue(this.value);
	},
	
    // private
	// Given a date or a string, returns a JS Date object
    parseDate : function(value){
        if(!value || Ext.isDate(value)){
            return value;
        }
        var v = Date.parseDate(value, this.format);
        if(!v && this.altFormats){
            if(!this.altFormatsArray){
                this.altFormatsArray = this.altFormats.split("|");
            }
            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
                v = Date.parseDate(value, this.altFormatsArray[i]);
            }
        }
        return v;
    },

    // private
    // Given a JS Date object, returns a string containing the formatted date.
    formatDate : function(date){
        return Ext.isDate(date) ? date.dateFormat(this.format) : date;
    }
    
});
Ext.reg('datecombo', Ext.pv.form.DateCombo);  
