/**
 * description
 * @module co2calc
 * @requires jQuery, tbelt, json
*/

(function($j, $tb) {

    /**
    * A Widget extension allowing the user to calculate their CO2 output based on their vehicle data.
    * @namespace co2calc.widget
    * @class Driving
    * @extends co2calc.widget.Widget
    */
    co2calc.widget.Driving = (function() {

        /**
        * Constructor description.
        * @constructor
        * @param opts {Object}{required} An object whose properties are copied to the Household instance.
        */
        function Driving(opts) {
            var I = this;
            co2calc.widget.Widget.apply(I, arguments);

        }

        $j.extend(Driving.prototype, co2calc.widget.Widget.prototype,
        {
            name: "Driving"

        });

        return Driving;

    })();


    /**
    * Represents the Quick View of the Driving widget.
    * @namespace co2calc.widget
    * @class DrivingViewQuick
    * @extends co2calc.widget.WidgetView
    */
    co2calc.widget.DrivingViewQuick = (function() {

        /**
        * Constructor description.
        * @constructor
        * @param opts {Object} {required} An object whose properties are copied to the DrivingViewQuick instance.
        */
        function DrivingViewQuick(opts) {
            var I = this;
            co2calc.widget.WidgetView.apply(I, arguments);
            //console.log(I.calculate);
        }

        $j.extend(true, DrivingViewQuick.prototype, co2calc.widget.WidgetView.prototype,
        {
			/**
             * The name of this instance. 
             * @property name
             * @type String
             * @default "Quick"
             */
			name: "Quick",
			/**
			 * Contains all of the data for this widget view.
			 * @property data
			 * @type Object
			 * @default {zipCode, vehicles}
			 */
			data:{
				zipCode:new co2calc.widget.WidgetViewDataItem({
					user:function() {return co2calc.zipCode();},
					typical: function() { return co2calc.dataSet.TypicalValues.ZipCode; }	
				}),
				vehicles:new co2calc.widget.WidgetViewDataItem({					
					user:function() {
						var vehicles = this.view.vehicleRepeater.data,
						userVs = [];
						for(var v=0; v<vehicles.length;v++){
							userVs.push(vehicles[v].user(vehicles[v], this.view.vehicleRepeater));
						}
						return userVs;
					},
					
					typical: function() { 
						var vehicles = this.view.vehicleRepeater.data,
						typVs = [];
						for(var v=0; v<vehicles.length;v++){
							typVs.push(vehicles[v].typical(vehicles[v], this.view.vehicleRepeater))
						}
						
						return typVs;
					},
					
					toJSON:function(key){
						var I = this,
						obj={
							user:I.user(),
							typical:I.typical()
						},
						vehicle,
						field;
						//loop through the user vehicles
						for(var v=0; v<obj.user.length; v++){
							vehicle = obj.user[v];
							//check each property for a matching field. delete the property if the field has no value
							for(var propName in vehicle){
								//find the field
								field = I.view.vehicleRepeater.itemsContainer.find("*[data='{$"+propName+"}']");
								if(field.length>v){
									field=$j(field[v]);
									if(field.val()==null||field.val()=="") delete vehicle[propName];
								}
								
							}
						}
								
						return obj;
					},
					
					fromJSON:function(obj){
						var I = this,
						sessionData = JSON.parse(co2calc.sessionData()),
						tmpSpeed = I.view.vehicleRepeater.accordion.speed;				
						
						/*console.dir({
							widget:I.view.widget.name,
							view:I.view.name,
							me:I.name
						})*/
						
						if(obj==null && sessionData[I.view.widget.name][I.view.name][I.name])
							obj = sessionData[I.view.widget.name][I.view.name][I.name];
						else return;						
						//console.dir(obj)
						
						//reset the vehice repeater's data
						I.view.vehicleRepeater.data=obj.user;
						I.view.vehicleRepeater.bind();
						I.view.vehicleRepeater.accordion.speed=0;
						I.view.vehicleRepeater.accordion.expand(0);
						I.view.vehicleRepeater.accordion.speed=tmpSpeed;
							
						return I;
					}
					
				})
				
					
			},
			
			/**
			 * Initializes this DrivingViewQuick instance.
			 * Employs co2calc.widget.WidgetView.prototype.init().
			 * @method init
			 * @return {DrivingViewQuick} This DrivingViewQuick instance.
			 */
			init:function(){
				var I = this;
				co2calc.widget.WidgetView.prototype.init.apply(I, arguments);
				
				I.vehicleRepeater.view=I;
				return I;
			},
			
			/**
			 * Calculates the total or average for this view.
			 * @method calculate
			 * @param {boolean} isAverage When true, the typical/default values are used for the calculation, returning the average the current zip code area.
			 * @return {float} The total of the typical or user values.
			 */
            calculate: function(isAverage) {
                var I = this,
				co2Total = 0.00;
				
				if(isAverage && I.staticAverage>-1) return I.staticAverage;
				
				try{
					var carDrivingCo2 = co2calc.dataSet.TypicalValues.CarDrivingCO2lbs,
					typMilesDriving = co2calc.dataSet.TypicalValues.DrivingMiles,
					vehicles = (isAverage) ? I.data.vehicles.typical() : I.data.vehicles.user();
					
					var vTypeFieldVal = I.vehicleRepeater.items().find("[data='{$vehicleType}']").val(),
					anMileFieldVal = I.vehicleRepeater.items().find("[data='{$annualMileage}']").val();
					
					/*console.dir({
						carDrivingCo2:carDrivingCo2,
						typMilesDriving:typMilesDriving,
						vehicles:vehicles,
						vTypeFieldVal:vTypeFieldVal,
						anMileFieldVal:anMileFieldVal
					})*/
		
					if (!isAverage && I.vehicleRepeater.data.length == 1 && (vTypeFieldVal + anMileFieldVal).length == 0) {
						//calculation is 0 if the user has not put in any data
					
					} else {
					
						for (var v = 0; v < vehicles.length; v++) {
							var vehicle = vehicles[v],
							adjValue = co2calc.dataSet.Adjustments.CarTypeAdj["double"][vehicle.vehicleType];

							//(1+CarTypeFuelAdj[carType])*(CarDrivingCO2lbs/TypicalMilesDriving)*"ThisCarDrivingMiles"	                
							co2Total += (1 + adjValue) * (carDrivingCo2 / typMilesDriving) * vehicle.annualMileage;
							
						}
						
						//Convert to tonnes and return
						co2Total = co2calc.toTons(co2Total);
					}
					
				}catch(err){
					trace(I.className+".calculate("+(isAverage||false)+"): "+err);
				};
				
				if(co2Total==null||co2Total==undefined) co2Total = (isAverage) ? I.average : I.total;
				else if(isAverage) I.average = co2Total;
				else I.total=co2Total;
				
				I.jI.trigger(I.className+".calculate", (isAverage)?true:false);	
				
	            return co2Total;
	            
	
            }
        });

        return DrivingViewQuick;

    })();



    /**
    * Represents the Detail View of the Driving widget.
    * @namespace co2calc.widget
    * @class DrivingViewDetailed
    * @extends co2calc.widget.WidgetView
    */
    co2calc.widget.DrivingViewDetailed = (function() {

        /**
        * Constructor description.
        * @constructor
        * @param {Object} opts {required} An object whose properties are copied to the DrivingViewDetailed instance.
        */
        function DrivingViewDetailed(opts) {
            var I = this;
            co2calc.widget.WidgetView.apply(I, arguments);
            //console.log(I.calculate);
        }

        $j.extend(true, DrivingViewDetailed.prototype, co2calc.widget.WidgetView.prototype,
        {
            /**
             * The name of this instance. 
             * @property name
             * @type String
             * @default "Detailed"
             */
			name: "Detailed",
			/**
			 * Contains all of the data for this widget view.
			 * @property data
			 * @type Object
			 * @default {zipCode, vehicles}
			 */
			data:{
				zipCode:new co2calc.widget.WidgetViewDataItem({					
					user:function() {return co2calc.zipCode();},
					typical: function() { return co2calc.dataSet.TypicalValues.ZipCode; }	
				}),
				vehicles:new co2calc.widget.WidgetViewDataItem({					
					user:function() {
						var vehicles = this.view.vehicleRepeater.data,
						userVs = [];
						for(var v=0; v<vehicles.length;v++){
							userVs.push(vehicles[v].user(vehicles[v], this.view.vehicleRepeater));
						}
						return userVs;
					},
					typical: function() { 
						var vehicles = this.view.vehicleRepeater.data,
						typVs = [];
						for(var v=0; v<vehicles.length;v++){
							typVs.push(vehicles[v].typical(vehicles[v], this.view.vehicleRepeater))
						}
						
						return typVs;
					},
					toJSON:co2calc.widget.DrivingViewQuick.prototype.data.vehicles.toJSON,
					fromJSON:co2calc.widget.DrivingViewQuick.prototype.data.vehicles.fromJSON
				})
            },	
			/**
			 * Initializes the widget.
			 * Employs co2calc.widget.WidgetView.prototype.init.
			 * @method init
			 * @return {DrivingViewDetailed} This DrivingViewDetailed instance.
			 */		
			init:function(){
				var I = this;
				co2calc.widget.WidgetView.prototype.init.apply(I, arguments);
				
				I.vehicleRepeater.view=I;
				
			},
			
			/**
			 * Calculates the total or average for this view.
			 * @method calculate
			 * @param {boolean} isAverage When true, the typical/default values are used for the calculation, returning the average the current zip code area.
			 * @return {float} The total of the typical or user values.
			 */
            calculate: function(isAverage) {
                var I = this,
				co2Total=0.00;
				
				if(isAverage && I.staticAverage>-1) return I.staticAverage;
				
				try{
										
					var totalMileage=0.00,
					averageMPG=0.00,
					vehicles = (isAverage) ? I.data.vehicles.typical() : I.data.vehicles.user(),
					carDrivingCo2 = co2calc.dataSet.TypicalValues.CarDrivingCO2lbs,
					typMilesDriving = co2calc.dataSet.TypicalValues.DrivingMiles;
					
					var mpgFieldVal = I.vehicleRepeater.items().find("[data='{$mpg}']").val(),
					anMileFieldVal = I.vehicleRepeater.items().find("[data='{$annualMileage}']").val();
					//console.dir(sessionView)
		
					if (!isAverage && I.vehicleRepeater.data.length==1 && (mpgFieldVal+anMileFieldVal).length==0) {
						//calculation is 0 if the user has not put in any data
						
					} else {
					
						for (var v = 0; v < vehicles.length; v++) {
							var vehicle = vehicles[v];
							//trace(vehicle.mpg + " : "+vehicle.annualMileage);						               
							totalMileage += vehicle.annualMileage;
							averageMPG += vehicle.mpg;
						}
						averageMPG = averageMPG / vehicles.length;
						
						//.00881 * ( [Vehicle Mileage]/ [MPG] / .971)
						co2Total = .00881 * (totalMileage / averageMPG / .971);
					}
				
				}catch(err){
					trace(I.className+".calculate("+(isAverage||false)+"): "+err);
				};
				
				if(co2Total==null||co2Total==undefined) co2Total = (isAverage) ? I.average : I.total;
				else if(isAverage) I.average = co2Total;
				else I.total=co2Total;
				
				I.jI.trigger(I.className+".calculate", (isAverage)?true:false);	
				
	            return co2Total;

            }
        });

        return DrivingViewDetailed;

    })();






	/**
    * DrivingVehicleRepeater Class Description
    * @namespace co2calc.widget
    * @class DrivingVehicleRepeater
    * @extends tbelt.ui.Repeater
    */
    co2calc.widget.DrivingVehicleRepeater = (function() {

        /**
        * Constructor description.
        * @constructor
        * @param {Object} opts {required} An object whose properties are copied to the DrivingViewDetailed instance.
        */
        function DrivingVehicleRepeater(opts) {
            var I = this;

			if(!opts.accordion) opts.accordion = new $tb.ui.Accordion();

            $tb.ui.Repeater.apply(I, arguments);
            //console.log(I.calculate);				
			
			I.jI.bind(I.className+".bind", I.onBind);
			I.jI.bind(I.className+".addItem", I.onAddItem);
			I.jI.bind(I.className+".removeItem", I.onRemoveItem);
			
        }

        $j.extend(true, DrivingVehicleRepeater.prototype, $tb.ui.Repeater.prototype,
        {
			/**
			 * The view containing this DrivingVehicleRepeater instance.
			 * @property view
			 * @type DrivingViewQuick|DrivingViewDetailed
			 */
			view:null,
			/**
			 * The data template for each item in this.data.
			 * @property dataTemplate
			 * @type Object
			 * @default {mpg, vehicleType, annualMileage, user, typical, sectionClass}
			 */
			dataTemplate:{
				mpg:null,
				vehicleType:null,
				annualMileage:null,
				user:function(d, r){
					var item = $j(r.items()[d.index]),
					typical = d.typical(d,r),
					vehicle = {
						vehicleType:item.find("*[data='{$vehicleType}']").val(),
						annualMileage:parseFloat(item.find("*[data='{$annualMileage}']").val()),
						mpg:parseFloat(item.find("*[data='{$mpg}']").val())
					},
					propName,
					prop;
					for(propName in vehicle){
						prop = vehicle[propName]
						if(prop==null||isNaN(prop)||(typeof(prop)=="string"&&prop==""))
							vehicle[propName]=typical[propName];
					}
					//console.dir(vehicle)
					return vehicle;
				},
				typical:function(d, r){
					if(co2calc.dataSet!=null)
					return {
						vehicleType:co2calc.dataSet.TypicalValues.CarType, 
						annualMileage:co2calc.dataSet.TypicalValues.DrivingMiles,
						mpg:24
					}
				},
				sectionClass:function(d, r){return "section-"+d.index}
			},
			/**
			 * Elements that, when clicked, call this.addItem().
			 * @property btnAdd
			 * @type jQuery selector|HTML Element
			 */
			btnAdd:null, //jQuery selector
			/**
			 * Elements that, when clicked, call this.removeItem().
			 * @property btnRemove
			 * @type jQuery selector|HTML Element
			 */
			btnRemove:null, //jQuery selector
			/**
			 * The class name of this object.
			 * @property className
			 * @type String
			 */
			className:"DrivingVehicleRepeater",
			/**
			 * The Accordion instance this repeater should use.
			 * @property accordion
			 * @type tbelt.ui.Accordion
			 */
			accordion:null,
			/**
			 * Initializes this instance. Employs tbelt.ui.Repeater.prototype.init().
			 * @method init
			 * @return {DrivingVehicleRepeater} This DrivingVehicleRepeater instance.
			 */
			init:function(){
				var I = this;
				$tb.ui.Repeater.prototype.init.apply(I, arguments);
				
				I.btnAdd = $j(I.btnAdd);					
				I.btnAdd.click(function(evt){
					if(I.data.length < I.maxItems) I.addItem();	
				});
				
				I.btnRemove = $j(I.btnRemove);
				
				I.onBind(null);
				
				return I;
			},
			/**
			 * Employs tbelt.ui.Repeater.prototype.removeItem().
			 * @method remoteItem
			 * @param {Object|number} itemOrIdx The item in this.data or index to remove.
			 * @return {DrivingVehicleRepeater} This DrivingVehicleRepeater instance.
			 */
			removeItem:function(itemOrIdx){
				var I = this,
				args=arguments;
				if (I.data.length > I.minItems) {
					var items = I.items(), item = $j(items[itemOrIdx]);
					
					function onClose(){
						//call original remove function
						$tb.ui.Repeater.prototype.removeItem.apply(I, args);
					}
					if ($j.browser.msie && $j.browser.version.indexOf("6.") == 0) {
						onClose();
					} else {
						item.height(item.height());
						item.animate({
							height: 0,
							opacity: 0
						}, I.accordion.speed, onClose);
					}						
					//expand the nearest item
					var sectionToExp = I.accordion.sections[itemOrIdx+1] || I.accordion.sections[itemOrIdx-1] || I.accordion.sections[0];
					//trace(sectionToExp)
					
					I.accordion.expand(sectionToExp);
					I.accordion.lastExpanded=itemOrIdx;
				}
				return I;
			},
			/**
			 * Bound to this.bind event.
			 * @method onBind
			 * @param {jQuery Event} evt
			 */
			onBind:function(evt){
				var I = this, 
				tmpSpeed = I.accordion.speed;
				
				//reassert the triggers/sections for the accordion and re-initialize
				I.accordion.triggers=I.itemsContainer.find("h4.section-title");
				I.accordion.sections=I.itemsContainer.find("div.section-content");
				I.accordion.sections.stop();
				
				//I.accordion.lastExpanded is a number if an item was removed. See I.removeItem().
				if (!isNaN(I.accordion.lastExpanded)) {
					I.accordion.lastExpanded = I.accordion.sections[I.accordion.lastExpanded];
					if(!I.accordion.lastExpanded)I.accordion.lastExpanded=I.accordion.sections.last();
				}
				//set the speed to 0 and initialize the accordion
				I.accordion.speed=0;				
				I.accordion.init();
				//update the view's form handler and the widget total
				if (I.view) {
					I.view.updateFormHandler();
					I.view.widget.update();
				}
				//assign typical values and field event handlers
				for(var d=0; d<I.data.length; d++){
					var dItem = I.data[d], 
					typVals = dItem.typical();
					//assign typical values to new vehicles
					if (dItem.mpg == null) {
						$j.extend(dItem, typVals);
					}
					//bind input fields to their data values and set field initial value
					for(var propName in dItem){
						var prop = dItem[propName],
						item = $j(I.items()[d]),
						field = item.find("*[data='{$"+propName+"}']");
						if (field.length > 0) {
							field.data("repeaterIndex", d);
							field.unbind("change");
							field.change(function(evt){
								field = $j(evt.target);
								propName = field.attr("data").replace(/\{\$(.*)\}/, "$1");
								dItem = I.data[field.data("repeaterIndex")];
								dItem[propName] = field.val();
							});
							
							if (typVals && typVals[propName] != null && typVals[propName] != dItem[propName] && prop!=null) {
								field.val(prop);
							}
						}
					}
				}
				//reset accordion speed
				I.accordion.speed=tmpSpeed;
				
				//set the remove button actions and toggle add/remove buttons
				I.btnRemove = $j(I.btnRemove.selector);
				
				I.btnRemove.each(function(i, b){
					$j(b).unbind("click");
					$j(b).click(function(evt){
						I.removeItem(i);
					});
				});
				
				I._toggleButtons();
				
				
			
			},
			/**
			 * Bound to this.addItem event.
			 * @method onAddItem
			 * @param {jQuery Event} evt The event triggering this function.
			 * @param {Object} data The data item that was added to this.data.
			 */
			onAddItem:function(evt, data){
				var I = this;
				//expand the new item
				I.accordion.expand(data.index);
				
				
			},
			/**
			 * Bound to this.removeItem event.
			 * @method onRemoveItem
			 * @param {jQuery Event} evt The event triggering this function.
			 * @param {Object} data The data item that was removed from this.data.
			 */
			onRemoveItem:function(evt, data){
				var I = this;
				
			},
			/**
			 * Enables or disables add/remove buttons based on this.maxItems and this.minItems.
			 * @method _toggleButtons
			 * @private
			 */
			_toggleButtons:function(){
				var I = this;
				//toggle add button
				if (I.data.length >= I.maxItems) {
					I.btnAdd.css({opacity: .5,cursor:"default"});						
				}else{
					I.btnAdd.css({opacity: "", cursor:""});
				}
				//toggle remove buttons
				if(I.data.length<=I.minItems){
					I.btnRemove.css({opacity: .5,cursor:"default"});
				}else{
					I.btnRemove.css({opacity: "", cursor:""});
				}
			}
		});
			
		return DrivingVehicleRepeater;
				
	})();
		
	
	

		
		
	
	
})(jQuery, tbelt);





