// single instance of anonymous utils object
var signupform_utils = new function() {

    this.mapURL = "/map";
    this.defaultThankyouURL = "/thank-you";

    this.formValueCache = [];
    this.currentForm = "individual_stage1only";

    this.processingText = "SAVING&nbsp;<span>(the world)</span>&hellip;";

    this.weAreProcessing = function(parentObj) {
	processingTextObj = jQuery('<p class="processingText">' + this.processingText + '</p>');

	jQuery(parentObj).before(processingTextObj);
	processingTextObj.css("z-index","999");

	jQuery(parentObj).css("opacity","0.5");
    }

    // a jQuery selector that points to the country selection box
    this.countrySelector = "#form_country";

    /* postcode validation rules don't apply if Ireland is selected; this
     * function is use to determine if it is selected or not */
    this.signupCountryIsNotIreland = function (element) {
	return (jQuery(signupform_utils.countrySelector).val() != "roi");
    }

    /* undo the visual changes that occured to show we were processing the
     * form */
    this.weAreDoneProcessing = function(parentObj) {
	jQuery(parentObj).css("opacity","1");
	jQuery("p.processingText").remove();
    }

    /* replace the current 'flexible' form with another one, having
     * reading the new one in via AJAX (constructing the URL using the
     * arg).
     */
    this.replaceForm = function(formName, callback) {
	var url = "/signup/forms/" + formName + ".jsp";
	// duplicate the query string if present, as this may include
	// pc, prepop etc.
	if(window.location.href.indexOf("?") != -1) {
	    url += window.location.href.substr(window.location.href.indexOf("?"));
	}

	// before changing, save cache the current form data in case
	// the user comes back to this form
	this.formValueCache[this.currentForm] = this.saveForm("signupForm");
	this.currentForm = formName;

	// read the new data in
	jQuery.get(url,
		   function(data) {
		       // find the current form which will have the
		       // class signupForm_flexible, and replace it
		       jQuery(".signupForm_flexible").replaceWith(data);
		       // is there some cached form data?
		       if(typeof signupform_utils.formValueCache[signupform_utils.currentForm] != "undefined") {
			   // don't load the formchanger from cache, save it here and restore it
			   formChangerVal = jQuery("#form_formChanger").val();
			   signupform_utils.loadForm("signupForm",
						     signupform_utils.formValueCache[signupform_utils.currentForm]);
			   jQuery("#form_formChanger").val(formChangerVal);
		       }
			   
		       if(typeof callback == "function") {
			   callback();
		       }
		   });
    }

    /* table that links form names to the files(URLS) that should be
     * loaded. They don't need to appear here if the file is the same
     * as the form name (e.g. schools) */
    this.formNames = [];
    this.formNames["individual"] = "individual_stage1only";
    this.formNames["business"] = "business_stage1_only";
    this.formNames["organisation"] = "organisation_stage1_only";

    /* callback that is assigned to the formChanger element */
    this.formChangerChanged = function(value) {
	var newForm;
	if(typeof signupform_utils.formNames[value] != "undefined") {
	    newForm = signupform_utils.formNames[value];
	} else {
	    // not in the table, just use as is
	    newForm = value;	    
	}

	this.replaceForm(newForm, function() { 
	    eval("signupform_" + value + "()");
	    // allow other code (such as a lightbox implementation or
	    // similar) to hook into the form changing, for example to
	    // resize the light box after the contents have changed by
	    // assigning a value to this callback.
	    if(typeof signupform_utils.formChangeCallback == "function") {
		signupform_utils.formChangeCallback();
	    }
	});

    }

    this.emptyStringIfNotDefined = function(x) {
	if(typeof x == "undefined") {
	    return "";
	} else {
	    return x;
	}
    }

    // saves the contents of a form so it can be reloaded. Pass the
    // result to loadForm()
    this.saveForm = function(formId) {
	// the object that will be returned
	var ret = new Object();
	ret.checkboxes = [];
	ret.nonchecks = [];

	// for each form element, save it's value against its name
	jQuery("form#" + formId + " input,select").each(
	    function() {
		if(this.type != "checkbox") {
		    ret.nonchecks[this.name] = this.value;
		} else {
		    ret.checkboxes[this.name] = this.checked;
		}
	    });
	return ret;
    }

    this.loadForm = function(formId, contents) {
	// for each element of the form, look for a value in contents,
	// if it exists, load it
	jQuery("form#" + formId + " input,select").each(
	    function() {
		if(this.type != "checkbox") {
		    if(typeof contents.nonchecks[this.name] != "undefined")
			this.value = contents.nonchecks[this.name];
		} else {
		    if(typeof contents.checkboxes[this.name] != "undefined")
			this.checked = contents.checkboxes[this.name];
		}
	    });
    }
    
    this.trackPageView = function(pageName){
    	if (typeof(_gaq)=="object"){
    		_gaq.push(['_trackPageview','/signup_'+pageName])
    	}
	}
}
function signupform_individual() {
    // just call out to the standard function with a data object
    // describing the differences
    signupform_standardMultiStage({
	formCompleted_URL: signupform_utils.mapURL,

	stage2backref1: "firstName",
	stage2backref2: "postcode",

	stage2formName: "individual_stage2only",

	validationRulesStage1: {
	    firstName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    lastName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    email: {
		required: true,
		email: true
	    },
	    postcode: {
		required: {
		    depends: signupform_utils.signupCountryIsNotIreland
		},
		regex: {
		    depends: signupform_utils.signupCountryIsNotIreland,
		    param: "^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{0,2} {0,1}[0-9][A-Za-z]{2}$"
		}
	    },
	    mobile: {
		regexOptional: "^\\+{0,1}[- 0-9]+$"
	    }
	},
	validationMessages: {
	    firstName: {
		required: "Please enter your first and last name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    lastName: {
		required: "Please enter your first and last name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    mobile: "Please enter a valid mobile number",
	    postcode: "Please enter a valid postcode"
	}
    });
}

function signupform_business() {
    // just call out to the standard function with a data object
    // describing the differences
    signupform_standardMultiStage({
	formCompleted_URL: signupform_utils.mapURL,

	stage2backref1: "companyName",
	stage2backref2: "postcode",

	stage2formName: "business_stage2_only",

	validationRulesStage1: {
	    companyName: {
		required: true,
		minlength: 2,
		maxlength: 75
	    },
	    firstName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    lastName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    email: {
		required: true,
		email: true
	    },
	    postcode: {
		required: {
		    depends: signupform_utils.signupCountryIsNotIreland
		},
		regex: {
		    depends: signupform_utils.signupCountryIsNotIreland,
		    param: "^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{0,2} {0,1}[0-9][A-Za-z]{2}$"
		}
	    },
	    telephone: {
		regexOptional: "^\\+{0,1}[- 0-9]+$"
	    }
	},

	validationMessages: {
	    firstName: {
		required: "Please enter your first and last name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    lastName: {
		required: "Please enter your first and last name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    telephone: "Please enter a valid phone number",
	    postcode: "Please enter a valid postcode"
	}
    });
}

function signupform_organisation() {
    // just call out to the standard function with a data object
    // describing the differences
    signupform_standardMultiStage({
	formCompleted_URL: signupform_utils.mapURL,

	stage2backref1: "orgName",
	stage2backref2: "postcode",

	stage2formName: "organisation_stage2_only",

	validationRulesStage1: {
	    orgName: {
		required: true,
		minlength: 2,
		maxlength: 75
	    },
	    orgType: {
		required: true
	    },
	    orgTypeOther: {
		required: {
		    depends: function() { return jQuery("#form_orgType").val() == "other"; }
		},
		regex: {
		    depends: function() { return jQuery("#form_orgType").val() == "other"; },
		    param: "^[ 0-9A-Za-z'-.]+$"
		}
	    },
	    firstName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    lastName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    email: {
		required: true,
		email: true
	    },
	    postcode: {
		required: {
		    depends: signupform_utils.signupCountryIsNotIreland
		},
		regex: {
		    depends: signupform_utils.signupCountryIsNotIreland,
		    param: "^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{0,2} {0,1}[0-9][A-Za-z]{2}$"
		}
	    },
	    telephone: {
		regexOptional: "^\\+{0,1}[- 0-9]+$"
	    }
	},

	validationMessages: {
	    firstName: {
		required: "Please enter your first and last name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    lastName: {
		required: "Please enter your first and last name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    telephone: "Please enter a valid phone number",
	    postcode: "Please enter a valid postcode",
	    orgTypeOther: "Please enter a valid organisation type"
	}
    });

    /* organisation specific bits */
    
    // start off with 'other' disabled, and re-enable it when the user
    // selects the appropriate value from the type dropdown.

    // it might already be at other, e.g., if they filled in the form,
    // then closed it, then reopened it.
    if(jQuery("#form_orgType").val() != "other") {
	jQuery("#form_orgTypeOther").attr("disabled","yes");
    }

    jQuery("#form_orgType").change(function() {
	if(jQuery(this).val() == "other") {
	    jQuery("#form_orgTypeOther").removeAttr("disabled");
	} else {
	    jQuery("#form_orgTypeOther").attr("disabled","yes");
	}
    });

}

function signupform_school() {
    // does not required any initialisation
}

function signupform_individual_stage2skip() {
    // just call out to the standard function with a data object
    // describing the differences
    signupform_standardMultiStage({
	formCompleted_URL: signupform_utils.mapURL,

	stage2backref1: "firstName",
	stage2backref2: "postcode",

	stage2formName: "individual_stage2skip",
	 skipToStage2: true,
	mapName: jQuery("#form_firstName2").val(),
	mapAddress: jQuery("#form_postCode2").val(),
	validationRulesStage1: {
	    firstName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    postcode: {
		required: {
		    depends: signupform_utils.signupCountryIsNotIreland
		},
		regex: {
		    depends: signupform_utils.signupCountryIsNotIreland,
		    param: "^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{0,2} {0,1}[0-9][A-Za-z]{2}$"
		}
	    }
	},
	validationMessages: {
	    firstName: {
		required: "Please enter your first name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    postcode: "Please enter a valid postcode"
	}
    });
}

function signupform_business_stage2skip() {
    // just call out to the standard function with a data object
    // describing the differences
    signupform_standardMultiStage({
	formCompleted_URL: signupform_utils.mapURL,

	stage2backref1: "companyName",
	stage2backref2: "postcode",

	stage2formName: "business_stage2skip",
	skipToStage2: true,
	mapName: jQuery("#form_companyName").val(),
	mapAddress: jQuery("#form_postCode").val(),
	validationRulesStage1: {
	    companyName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    postcode: {
		required: {
		    depends: signupform_utils.signupCountryIsNotIreland
		},
		regex: {
		    depends: signupform_utils.signupCountryIsNotIreland,
		    param: "^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{0,2} {0,1}[0-9][A-Za-z]{2}$"
		}
	    }
	},
	validationMessages: {
	    companyName: {
		required: "Please enter your company name",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    postcode: "Please enter a valid postcode"
	}
    });
}

function signupform_organisation_stage2skip() {
    // just call out to the standard function with a data object
    // describing the differences
    signupform_standardMultiStage({
	formCompleted_URL: signupform_utils.mapURL,

	stage2backref1: "orgName",
	stage2backref2: "postcode",

	stage2formName: "organisation_stage2skip",
	skipToStage2: true,
	mapName: jQuery("#form_orgName").val(),
	mapAddress: jQuery("#form_postCode").val(),
	validationRulesStage1: {
	    orgName: {
		required: true,
		minlength: 2,
		maxlength: 40
	    },
	    postcode: {
		required: {
		    depends: signupform_utils.signupCountryIsNotIreland
		},
		regex: {
		    depends: signupform_utils.signupCountryIsNotIreland,
		    param: "^[A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]{0,2} {0,1}[0-9][A-Za-z]{2}$"
		}
	    }
	},
	validationMessages: {
	    orgName: {
		required: "Please enter the name of your organisation",
		maxlength: "Sorry, that name is too long for us handle"
	    },
	    postcode: "Please enter a valid postcode"
	}
    });
}

/**
 * this function handles the normal multi-stage form type, such as
 * business, individual and organisation, where the user first fills
 * in some information about themselves and the user is passed to an
 * optional second stage in which they fill in information about what
 * they are actually doing for Earth Hour.
 *
 * It takes a data object which contains the following configuration
 * values:
 *
 * formCompleted_URL: the location that the user will be fowarded to
 * once both stages of the form have been successfully completed
 * (e.g. a thank you page).
 *
 * stage2backref1: the name of the first stage1 input field that is
 * re-referenced and duplicated into stage2 (e.g. company name)
 *
 * stage2backref2: the name of the second stage1 input field that is
 * re-referenced and duplicated into stage2 (e.g. postcode)
 *
 * stage2formName: the form that will be hot-swapped in once stage1
 * has been successfully been submitted.
 *
 * validationRulesStage1: the rules object that will be passed to the
 * validation plugin for stage1.
 *
 * validationMessages: again, will be passed to the validation plugin
 * to provide the messages that are output for validation errors.
 */

function signupform_standardMultiStage(multiStageFormData) {
    signupform_utils.trackPageView(signupform_utils.currentForm);
    
    var signup_thankyouURL = multiStageFormData.formCompleted_URL;

    /* set the current form file name by looking in the table */
    if(typeof signupform_utils.formNames[jQuery("#form_formChanger").val()] != "undefined") {
    	signupform_utils.currentForm = signupform_utils.formNames[jQuery("#form_formChanger").val()];
    } else {
    	signupform_utils.currentForm = jQuery("#form_formChanger").val();
    }

    function processResponse(data) {
	if(!data.valid) {
	    processServerSideInvalid(data);
	    return;
	}

	if(data.success) {
	    if(jQuery("input#stageParam").val() == "1") {
		showSecondStage();
	    } else {
		formCompleted();
	    }
	} else {
	    handleAjaxError();
	}
    }

    /* generate the URL query string to produce the example placemark
     * for this signup. Does not include the '?' at the start (so
     * caller can choose ? or & as appropriate. */
    function examplePlacemark() {
	var example_name, example_postcode, example_desc, iconIndex;

	// source for name differs depending on type
	var whatAmI = jQuery("form#signupForm [name=whatami]").val();
	if(typeof whatAmI != "string") return "";
	if(whatAmI == "individual") {
	    example_name = jQuery("form#signupForm [name=firstName]").val();
	    iconIndex = "0";
	} else if(whatAmI == "business") {
	    example_name = jQuery("form#signupForm [name=companyName]").val();
	    iconIndex = "1";
	} else if(whatAmI == "organisation") {
	    example_name = jQuery("form#signupForm [name=orgName]").val();
	    iconIndex = "2";
	} else {
	    // unknown value
	    return "";
	}
	example_postcode = jQuery("form#signupForm [name=postcode]").val();
	example_desc = jQuery("form#signupForm [name=whatDoing]").val()
	// sanity check
	if(typeof example_name != "undefined" &&
	   typeof example_postcode != "undefined" &&
	   typeof example_desc != "undefined") {
	    return "ehxname=" + encodeURIComponent(example_name) +
		"&ehxpostcode=" + encodeURIComponent(example_postcode) +
		"&ehxdesc=" + encodeURIComponent(example_desc) +
		"&ehxico=" + iconIndex;
	} else {
	    return "";
	}
    }

    function formCompleted() {
	/* called after the second stage of the signup form has been
	 * successfully submitted. Forwards to a thank-you URL */

	/* if we are forwarding to the map, create example
	 * placemark */
	if(signup_thankyouURL == signupform_utils.mapURL) {
	    window.location = signup_thankyouURL + "?" +
		examplePlacemark();
	} else {
	    window.location = signup_thankyouURL;
	}
    }

    /* called when the server said the submitted data did not pass
     * validation checks */
    function processServerSideInvalid(data) {
	var errorString = "Please fix the following errors:\n";
	for(i = 0; i < data.invalidFields.length; i++) {
            errorString += data.invalidFields[i][1] + "\n";
	}
	alert(errorString);
	restoreForm();
    }

    function initialiseStage2(stage1vals) {
    
    signupform_utils.trackPageView(signupform_utils.currentForm);

	jQuery("#multistageform1hidden input").each(function() {
	    if(typeof stage1vals.nonchecks[this.name] != "undefined") {
		this.value = stage1vals.nonchecks[this.name];
	    }
	});

	for(name in stage1vals.checkboxes) {
	    if(stage1vals.checkboxes[name] == true) // checked
		jQuery("div#multistageform1hidden").append('<input type="hidden" name="' + name + '" value="yes"/>');
	}
	jQuery("input[name=stage]").val("2");
	jQuery("input[name=pc]").val(stage1vals.nonchecks['pc']);

	// handle stage1 backrefs 
	jQuery("input[name=" + multiStageFormData.stage2backref1 + "2]").
	    val(stage1vals.nonchecks[multiStageFormData.stage2backref1]);
	jQuery("input[name=" + multiStageFormData.stage2backref2 + "2]").
	    val(stage1vals.nonchecks[multiStageFormData.stage2backref2]);

	jQuery("form#signupForm").ajaxForm({
	    data: { ajax: 'true' },
	    success: processResponse,
	    beforeSubmit: function() { signupform_utils.weAreProcessing(jQuery("div#signupLightBox")); },
	    datatype: "json" });

	// initialise the example map
	initialiseMap(stage1vals.nonchecks[multiStageFormData.stage2backref2],
		      stage1vals.nonchecks[multiStageFormData.stage2backref1]);
    }

    function showSecondStage() {

	stage1vals = signupform_utils.saveForm("signupForm");

	// undim form etc
	signupform_utils.weAreDoneProcessing(jQuery("div#signupLightBox"));

	// need to do the rest of the stuff in callback once the AJAX load has completed
	signupform_utils.replaceForm(multiStageFormData.stage2formName, 
				     function() {
					 initialiseStage2(stage1vals);
					 if(typeof signupform_utils.formChangeCallback
					    == "function") {
					     signupform_utils.formChangeCallback();
					 }
				     });
    }    

    /* on signup form stage 2, show a marker at the postcode they have entered, as an example */
    function initialiseMap(address, name) {
	// show the map div
	jQuery("div#signupLightBox div#mapExample").show();
	// start off centered on London while we are geocoding
	// asynchronously
	var latlng = new google.maps.LatLng(51.507222, -0.1275);
	var mapOptions = {
	    zoom: 8,
	    center: latlng,
	    mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	var map = new google.maps.Map(document.getElementById("mapExample"),
				      mapOptions);

	var geocoder = new google.maps.Geocoder();
	var markTitle;
	if(name != "") {
	    markTitle = name + " is switching off the lights for Earth Hour";
	} else {
	    markTitle = "Switching off the lights for Earth Hour";
	}
	geocoder.geocode({ address: address }, function(results, status) {
	    if(status == google.maps.GeocoderStatus.OK) {
		var latlng = results[0].geometry.location;
		map.setCenter(latlng);
		map.setZoom(11);
		var marker = new google.maps.Marker({
		    position: latlng,
		    map: map,
		    title: markTitle
		});
	    }
	});
    }

    function handleAjaxError() {
	alert("We are sorry, there was an error submitting the form. " + 
	      "Please try again later.");
	restoreForm();
    }

    /* bring the form back after we have faded it out (e.g. if there was
     * an error) */
    function restoreForm() {
	signupform_utils.weAreDoneProcessing(jQuery("div#signupLightBox"));
	// bring the buttons back
	jQuery("input#signupButton1,input#signupButton2").css("opacity","100");
    }

    /**
     * general utility stuff for the form
     */

    // make sure stage 1 is set in the beginning
    jQuery("input#stageParam").val("1");

    var ajaxFormOpts = {
     	dataType: 'json',
    	success: processResponse,
    	data: { ajax: 'true' }
    };

    var validateFormOptsStage1 = {
	// submit the form via AJAX once it is ready (valid)
	submitHandler: function(form) {
	    // give visual indication that we are doing stuff
	    signupform_utils.weAreProcessing(jQuery("div#signupLightBox"));
	    // hide the button but keep the space
	    if(jQuery("input#stageParam").val() == "1") {
		jQuery("input#signupButton1").css("opacity", "0");
	    } else {
		jQuery("input#signupButton2").css("opacity", "0");
	    }

	    // set up a generic error handler in case something goes
	    // very wrong on the server
	    jQuery(form).ajaxSubmit(ajaxFormOpts);
	},

	errorPlacement: function(error, element) {
	    // locate the next sibling that has the errorMessage
	    // class, this will be the hidden element that is
	    // designated as the error message container for this
	    // element.
	    
	    // is there a specific error message location for this element?
	    errorElement = jQuery("#errorMessage_" + element.attr("id")).first();
	    // if that wasn't found, just use a neighbour
	    if(errorElement.length < 1) {
		errorElement = element.nextAll(".errorMessage").first();
	    }

	    // delete what's there currently and add the error in its
	    // place
	    errorElement.empty();
	    errorElement.append(error);
	    errorElement.show();
	},

	groups: {
	    membername: "firstName lastName"
	},

	messages: multiStageFormData.validationMessages,
	rules: multiStageFormData.validationRulesStage1
    }

    // customised validation methods (mostly based on regexes)
    jQuery.validator.addMethod("regex", function(value, element, regex) {
	return new RegExp(regex).test(value);
    });

    jQuery.validator.addMethod("regexOptional", function(value, element, regex) {
	if(value == "") {
	    return true;
	} else {
	    return new RegExp(regex).test(value);
	}
    });

    jQuery(document).ajaxError(handleAjaxError);

    jQuery("form#signupForm").validate(validateFormOptsStage1);

    if(typeof multiStageFormData.skipToStage2 != "undefined" &&
      multiStageFormData.skipToStage2 == true) {
	initialiseMap(multiStageFormData.mapAddress, 
		      multiStageFormData.mapName);
    }
}






