package com.tootsville.joshua.client.create;

import java.util.Date;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.tootsville.gwt.joshua.client.Joshua;
import com.tootsville.gwt.joshua.client.except.ForbiddenException;
import com.tootsville.gwt.joshua.client.except.InvalidCredentialsException;
import com.tootsville.gwt.joshua.client.service.JoshuaServiceAsync;
import com.tootsville.gwt.joshua.client.util.User;

public class CreateUser extends FormPanel {

	Button addStaffLevel = new Button ("Add Staff Level");
	TextBox answerBox = new TextBox ();
	Button cancel = new Button ("Cancel");

	ListBox dayBox = new ListBox ();
	final Grid detailsPane = new Grid (6, 3);
	TextBox emailBox = new TextBox ();
	final Grid emailPane = new Grid (1, 3);
	RadioButton free = new RadioButton ("premium", "Free");
	@SuppressWarnings ("unused")
	private boolean isChild = true;
	boolean isMailGood = false;
	boolean isNameGood = false;
	boolean isNewAccount = true;

	Joshua main;

	ListBox monthBox = new ListBox ();
	RadioButton paid = new RadioButton ("premium", "Paid");
	TextBox passwordBox = new TextBox ();
	TextBox questionBox = new TextBox ();
	protected final int RPC_DELAY = 500;
	Timer rpcTimer;
	ListBox staffLevelBox = new ListBox ();
	Button submit = new Button ("Submit");
	final ListBox tootBox = new ListBox (false);
	final Grid tootPane = new Grid (3, 3);
	VerticalPanel uiBag = new VerticalPanel ();
	User user;
	TextBox userNameBox = new TextBox ();
	TextBox vitTimeBox = new TextBox ();
	TextBox yearBox = new TextBox ();

	/**
	 * 
	 * @param main1
	 */
	public CreateUser (final Joshua main1) {
		main = main1;
		loadForm (false);
	}

	/**
	 * 
	 * @param main1
	 * @param user1
	 */
	public CreateUser (final Joshua main1, final User user1) {
		main = main1;
		user = user1;
		if (!user1.hasStaffLevel (1)) {
			loadForm (true);
		} else {
			Window.alert ("You cannot edit a staff member's account.");
			main1.search (String.valueOf (user1.getUserID ()));
		}
	}

	/**
	 * 
	 * @param newAccount
	 */
	private void buildDetailsPane (final boolean newAccount) {
		isNewAccount = newAccount;
		buildMonthBox ();
		yearBox.setMaxLength (4);
		yearBox.setVisibleLength (6);

		if (isNewAccount) {
			final HorizontalPanel datePane = new HorizontalPanel ();

			datePane.add (monthBox);
			datePane.add (new Label (" / "));
			datePane.add (dayBox);
			datePane.add (new Label (" / "));
			datePane.add (yearBox);
		}

		detailsPane.setText (0, 0, "User Name: ");
		detailsPane.setWidget (0, 1, userNameBox);

		if (null == user || !user.hasStaffLevel (1)) {
			detailsPane.setText (1, 0, "Password: ");
			detailsPane.setWidget (1, 1, passwordBox);
		}

		questionBox.setText ("What is your favorite food?");
		answerBox.setText ("pizza");

		detailsPane.setText (3, 0, "Forgotten Password Question");
		detailsPane.setWidget (3, 1, questionBox);
		detailsPane.setText (4, 0, "Forgotten Password Answer");
		detailsPane.setWidget (4, 1, answerBox);
		detailsPane.setWidget (5, 1, addStaffLevel);

		final HorizontalPanel datePane = new HorizontalPanel ();

		datePane.add (monthBox);
		datePane.add (new Label ("/"));
		datePane.add (dayBox);
		datePane.add (new Label ("/"));
		datePane.add (yearBox);
		detailsPane.setText (2, 0, "Date of Birth: ");
		detailsPane.setWidget (2, 1, datePane);

		userNameBox.addKeyUpHandler (new KeyUpHandler () {
			@Override
			public void onKeyUp (final KeyUpEvent event) {
				rpcTimer.cancel ();
				rpcTimer = new Timer () {
					@Override
					public void run () {
						verifyUserName ();
					}
				};
				rpcTimer.schedule (RPC_DELAY);
			}
		});

		userNameBox.addBlurHandler (new BlurHandler () {
			@Override
			public void onBlur (final BlurEvent event) {
				verifyUserName ();
			}
		});

		passwordBox.addKeyUpHandler (new KeyUpHandler () {
			@Override
			public void onKeyUp (final KeyUpEvent event) {
				rpcTimer.cancel ();
				rpcTimer = new Timer () {
					@Override
					public void run () {
						verifyPassword ();
					}
				};
				rpcTimer.schedule (RPC_DELAY);
			}
		});

		passwordBox.addBlurHandler (new BlurHandler () {
			@Override
			public void onBlur (final BlurEvent event) {
				verifyPassword ();
			}
		});

		yearBox.addKeyUpHandler (new KeyUpHandler () {
			@Override
			public void onKeyUp (final KeyUpEvent event) {
				verifyDate ();
			}
		});

		addStaffLevel.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				staffLevelBox.addItem ("Public", String
						.valueOf (Joshua.PUBLIC));
				staffLevelBox.addItem ("Staff Member", String
						.valueOf (Joshua.STAFF_MEMBER));

				if (main.getLifeguard ().hasStaffLevel (
						Joshua.STAFF_MODERATOR)) {
					staffLevelBox.addItem ("Moderator", String
							.valueOf (Joshua.STAFF_MODERATOR));
				}

				if (main.getLifeguard ().hasStaffLevel (
						Joshua.STAFF_ACCOUNT_SERVICE)) {
					staffLevelBox.addItem ("Account Service", String
							.valueOf (Joshua.STAFF_ACCOUNT_SERVICE));
				}

				if (main.getLifeguard ().hasStaffLevel (
						Joshua.STAFF_DESIGNER)) {
					staffLevelBox.addItem ("Designer", String
							.valueOf (Joshua.STAFF_DESIGNER));
				}

				if (main.getLifeguard ().hasStaffLevel (
						Joshua.STAFF_DEVELOPER)) {
					staffLevelBox.addItem ("Developer", String
							.valueOf (Joshua.STAFF_DEVELOPER));
				}

				detailsPane.setText (5, 0, "Staff Level");
				detailsPane.setWidget (5, 1, staffLevelBox);
			}
		});

		detailsPane.setStyleName ("detailsBox");
	}

	/**
	 * 
	 */
	private void buildEmailPane () {
		emailPane.setText (0, 1, "User Email: ");
		emailPane.setWidget (0, 1, emailBox);

		emailBox.addChangeHandler (new ChangeHandler () {
			@Override
			public void onChange (final ChangeEvent event) {
				verifyEmail ();
			}
		});

		emailPane.setStyleName ("detailsBox");
		emailPane.setVisible (false);
	}

	/**
	 * 
	 */
	private void buildMonthBox () {
		monthBox.addItem ("January", String.valueOf (0));
		monthBox.addItem ("February", String.valueOf (1));
		monthBox.addItem ("March", String.valueOf (2));
		monthBox.addItem ("April", String.valueOf (3));
		monthBox.addItem ("May", String.valueOf (4));
		monthBox.addItem ("June", String.valueOf (5));
		monthBox.addItem ("July", String.valueOf (6));
		monthBox.addItem ("August", String.valueOf (7));
		monthBox.addItem ("September", String.valueOf (8));
		monthBox.addItem ("October", String.valueOf (9));
		monthBox.addItem ("November", String.valueOf (10));
		monthBox.addItem ("December", String.valueOf (11));

		monthBox.addChangeHandler (new ChangeHandler () {
			@Override
			public void onChange (final ChangeEvent event) {
				updateDays (monthBox.getValue (monthBox
						.getSelectedIndex ()));
			}
		});

		updateDays (monthBox.getValue (monthBox.getSelectedIndex ()));
	}

	/**
	 * 
	 */
	private void buildTootPane () {
		buildTootSelect ();
		free.setValue (true);

		tootPane.setText (0, 0, "Toot: ");
		tootPane.setWidget (0, 1, tootBox);

		tootPane.setWidget (1, 0, free);
		tootPane.setWidget (1, 1, paid);

		tootPane.setStyleName ("detailsBox");

		free
				.addValueChangeHandler (new ValueChangeHandler <Boolean> () {
					@Override
					public void onValueChange (
							final ValueChangeEvent <Boolean> event) {
						if (event.getValue ()) {
							tootPane.removeRow (2);
							tootPane.resizeRows (2);
						}

					}
				});
		paid
				.addValueChangeHandler (new ValueChangeHandler <Boolean> () {
					@Override
					public void onValueChange (
							final ValueChangeEvent <Boolean> event) {
						if (event.getValue ()) {
							tootPane.resizeRows (3);
							vitTimeBox.setText ("life");
							tootPane.setText (2, 0, "Number of Months");
							tootPane.setWidget (2, 1, vitTimeBox);
						}

					}
				});
	}

	/**
	 * 
	 */
	private void buildTootSelect () {

		tootBox.addItem ("Zap", "1");
		tootBox.addItem ("Flora", "8");
		tootBox.addItem ("Dottie", "2");
		tootBox.addItem ("Sparkle", "5");
		tootBox.addItem ("Moo", "6");
		tootBox.addItem ("Cupid", "7");
		tootBox.addItem ("Super Star", "3");
		tootBox.addItem ("Li'l MC", "4");

		tootBox.setVisibleItemCount (1);
	}

	public boolean isNameGood () {
		// default getter (theys, Oct 13, 2009)
		return isNameGood;
	}

	/**
	 * 
	 * @param createOrEdit
	 */
	private void loadForm (final boolean createOrEdit) {
		buildDetailsPane (createOrEdit);
		buildTootPane ();
		buildEmailPane ();

		if (createOrEdit) {
			userNameBox.setText (user.getUserName ());
			if (!user.hasStaffLevel (1)) {
				passwordBox.setText (user.getPassword ());
			}

			cancel.addClickHandler (new ClickHandler () {
				@Override
				public void onClick (final ClickEvent event) {
					main.search (String.valueOf (user.getUserID ()));
				}
			});
		} else {
			cancel.addClickHandler (new ClickHandler () {
				@Override
				public void onClick (final ClickEvent event) {
					main.changePage (new HorizontalPanel ());
				}
			});
		}

		final HorizontalPanel buttonBar = new HorizontalPanel ();
		buttonBar.add (cancel);
		buttonBar.add (submit);

		submit.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				submitRequest ();
			}
		});

		uiBag.add (detailsPane);
		uiBag.add (tootPane);
		uiBag.add (emailPane);
		uiBag.add (buttonBar);

		add (uiBag);
	}

	/**
	 * 
	 */
	private void promptForAdultEmail () {
		emailPane.setText (0, 0, "User Email: ");
		emailPane.setWidget (0, 1, emailBox);
		emailPane.setVisible (true);
		isChild = false;
	}

	/**
	 * 
	 */
	private void promptForParentEmail () {
		emailPane.setText (0, 0, "Parent Email: ");
		emailPane.setWidget (0, 1, emailBox);
		emailPane.setVisible (true);
		isChild = true;
	}

	public void setNameGood (final boolean newIsNameGood) {
		isNameGood = newIsNameGood;
	}

	/**
	 * 
	 * TODO: document this method (theys, Oct 12, 2009)
	 * 
	 */
	@SuppressWarnings ("deprecation")
	protected void submitRequest () {

		final String password = Window.prompt (
				"Not supported: enter password.", "");
		if (!"tim".equals (password))
			return;

		final String daysString = dayBox.getValue (dayBox
				.getSelectedIndex ());
		final String monthsString = monthBox.getValue (monthBox
				.getSelectedIndex ());
		final String yearString = yearBox.getText ();
		int days;
		int months;
		int years;

		try {
			days = Integer.parseInt (daysString);
			months = Integer.parseInt (monthsString);
			years = Integer.parseInt (yearString);
		} catch (final NumberFormatException e) {
			Window
					.alert ("An error occurred processing the date entered.  "
							+ "Please verify the date entered is valid and try submiting again.");
			return;
		}

		final Date birthDate = new Date ();
		birthDate.setDate (days);
		birthDate.setMonth (months);
		birthDate.setYear (years - 1900);

		final User newUser = new User ();
		newUser.setBirthDate (String.valueOf (birthDate));
		newUser.setUserName (userNameBox.getText ());
		newUser.setPassword (passwordBox.getText ());
		newUser.setQuestion (questionBox.getText ());
		newUser.setAnswer (answerBox.getText ());
		try {
			newUser.setBasic8 (Integer.parseInt (tootBox
					.getValue (tootBox.getSelectedIndex ())));
		} catch (final NumberFormatException e) {
			Window.alert ("Bug!");
			return;
		}
		if (!isNewAccount) {
			newUser.setUserID (user.getUserID ());
		}
		if (!"".equals (staffLevelBox.getValue (staffLevelBox
				.getSelectedIndex ()))) {
			try {
				newUser.setStaffLevel (Integer.parseInt (staffLevelBox
						.getValue (staffLevelBox.getSelectedIndex ())));
			} catch (final NumberFormatException e) {
				Window.alert ("Bug!");
				return;
			}
		} else {
			newUser.setStaffLevel (Joshua.PUBLIC);
		}

		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <Void> callback = new AsyncCallback <Void> () {
			@Override
			public void onFailure (final Throwable caught) {
				Window.alert ("Failed to create user: "
						+ caught.getMessage ());
			}

			@Override
			public void onSuccess (final Void result) {
				Window.alert ("User account created successfully");
				main.search (userNameBox.getText ());
			}
		};

		Window.alert ("Calling server");
		// Make the call to the search service.
		service.createOrEditUser (user, isNewAccount, callback);

		return;
	}

	/**
	 * 
	 * @param itemText
	 */
	protected void updateDays (final String itemText) {
		int numberOfDays = 31;
		int month = 0;
		try {
			month = Integer.parseInt (itemText);
			switch (month) {
			case 0:
			case 2:
			case 4:
			case 6:
			case 7:
			case 9:
			case 11:
				numberOfDays = 31;
				break;
			case 3:
			case 5:
			case 8:
			case 10:
				numberOfDays = 30;
				break;
			case 1:
				numberOfDays = 29;
				break;
			default:
				numberOfDays = 31;
				break;
			}
		} catch (final NumberFormatException e) {
			numberOfDays = 31;
		}
		dayBox.clear ();
		for (int i = 1; i <= numberOfDays; i++ ) {
			dayBox.addItem (String.valueOf (i));
		}
	}

	@SuppressWarnings ("deprecation")
	/*
	 * *
	 */
	protected boolean verifyDate () {
		detailsPane.setWidget (2, 2, new Label (""));
		final Label errLabel = new Label ("");
		errLabel.setStyleName ("errText");
		emailPane.setVisible (false);

		if (0 == yearBox.getText ().length ())
			return false;

		if (4 > yearBox.getText ().length ()) {
			errLabel.setText ("Too short!");
			detailsPane.setWidget (2, 2, errLabel);
			return false;
		}

		final String daysString = dayBox.getValue (dayBox
				.getSelectedIndex ());
		final String monthsString = monthBox.getValue (monthBox
				.getSelectedIndex ());
		final String yearString = yearBox.getText ();
		int days;
		int months;
		int years;

		try {
			days = Integer.parseInt (daysString);
			months = Integer.parseInt (monthsString);
			years = Integer.parseInt (yearString);
		} catch (final NumberFormatException e) {
			errLabel.setText ("Number Format");
			detailsPane.setWidget (2, 2, errLabel);
			return false;
		}

		final Date birthDate = new Date ();
		birthDate.setDate (days);
		birthDate.setMonth (months);
		birthDate.setYear (years - 1900);

		if (birthDate.after (new Date (System.currentTimeMillis ()))) {
			errLabel.setText ("Future Date");
			detailsPane.setWidget (2, 2, errLabel);
			return false;
		}

		final Date compareDate = new Date (System.currentTimeMillis ());

		// Set to 13 years ago to see if the user is a child
		compareDate.setYear (compareDate.getYear () - 13);
		if (birthDate.before (compareDate)) {
			promptForParentEmail ();
		} else {
			promptForAdultEmail ();
		}
		return true;
	}

	/**
	 * 
	 */
	protected void verifyEmail () {
		isMailGood = false;
		emailPane.setWidget (0, 2, new Label (""));
		final Label errLabel = new Label ("");
		errLabel.setStyleName ("errText");
		final String email = emailBox.getText ();

		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <Void> callback = new AsyncCallback <Void> () {
			@Override
			public void onFailure (final Throwable caught) {
				if (caught instanceof InvalidCredentialsException) {
					errLabel.setText ("Invalid E-mail address!");
					detailsPane.setWidget (0, 2, errLabel);
				}

			}

			@Override
			public void onSuccess (final Void result) {
				isMailGood = true;
			}
		};

		// Make the call to the search service.
		service.verifyMail (email, callback);

		return;
	}

	/**
	 * 
	 * @return
	 */
	protected boolean verifyPassword () {
		detailsPane.setWidget (1, 2, new Label (""));
		final Label errLabel = new Label ("");
		errLabel.setStyleName ("errText");
		final String password = passwordBox.getText ();

		if (0 == password.length ())
			return false;

		if (3 > password.length ()) {
			errLabel.setText ("Too short!");
			detailsPane.setWidget (1, 2, errLabel);
			return false;
		}
		return true;
	}

	/**
	 * 
	 */
	protected void verifyUserName () {
		setNameGood (false);
		detailsPane.setWidget (0, 2, new Label (""));
		final Label errLabel = new Label ("");
		errLabel.setStyleName ("errText");
		final String userName = userNameBox.getText ();

		if (0 == userName.length ())
			return;

		if (3 > userName.length ()) {
			errLabel.setText ("Too short!");
			detailsPane.setWidget (0, 2, errLabel);
			return;
		}

		if (null != user && userName.equals (user.getUserName ()))
			return;

		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <Void> callback = new AsyncCallback <Void> () {
			/**
			 * 
			 * @param caught
			 */
			@Override
			public void onFailure (final Throwable caught) {
				if (caught instanceof ForbiddenException) {
					errLabel.setText ("Can't use that name!");
					detailsPane.setWidget (0, 2, errLabel);
				} else if (caught instanceof InvalidCredentialsException) {
					errLabel.setText ("Name already taken!");
					detailsPane.setWidget (0, 2, errLabel);
				}
			}

			/**
			 * 
			 * @param result
			 */
			@Override
			public void onSuccess (final Void result) {
				setNameGood (true);
			}
		};

		// Make the call to the search service.
		service.verifyUserName (userName, callback);

		return;
	}

}
