package com.tootsville.joshua.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
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.*;
import com.tootsville.joshua.client.create.CreateUser;
import com.tootsville.joshua.client.details.ParentDetails;
import com.tootsville.joshua.client.details.UserDetails;
import com.tootsville.joshua.client.except.InvalidCredentialsException;
import com.tootsville.joshua.client.except.PermissionRequiredException;
import com.tootsville.joshua.client.service.JoshuaService;
import com.tootsville.joshua.client.service.JoshuaServiceAsync;
import com.tootsville.joshua.client.util.Parent;
import com.tootsville.joshua.client.util.Person;
import com.tootsville.joshua.client.util.User;

/**
 * 
 * TODO: Document this type. Dec 7, 2009 : theys
 * 
 * @author <a href="mailto:theys@resinteractive.com">Tim Heys</a>
 * 
 */
public class Joshua implements EntryPoint {

	/**
	 * 
	 */
	private static final int H_SPACING = 2;

	/**
	 * 
	 */
	private static JoshuaServiceAsync joshuaSvc = GWT
	.create (JoshuaService.class);

	/**
	 * 
	 */
	private static final String joshuaTitle = "Tootsville - Joshua Spy";

	/**
	 * 
	 */
	public static final int PUBLIC = 0;

	/**
	 * 
	 */
	private static final String searchToolTip = "Search for User ID, User Name, Mail, Cookie, and Enrolment Codes";

	/**
	 * 
	 */
	public static final int STAFF_ACCOUNT_SERVICE = 3;

	/**
	 * 
	 */
	public static final int STAFF_DESIGNER = 4;

	/**
	 * 
	 */
	public static final int STAFF_DEVELOPER = 8;

	/**
	 * 
	 */
	public static final int STAFF_MEMBER = 1;

	/**
	 * 
	 */
	public static final int STAFF_MODERATOR = 2;

	/**
	 * 
	 */
	private static final int V_SPACING = 10;

	/**
	 * 
	 */
	protected static final int VERSION_CHECK_INTERVAL = 60 * 1000;

	/**
	 * Initialized the Asynchronous service if it needs to be and returns it.
	 * 
	 * @return the ASynchronous service
	 */
	public static JoshuaServiceAsync initService () {
		// Initialize the service proxy.
		if (null == Joshua.joshuaSvc) {
			Joshua.joshuaSvc = GWT.create (JoshuaService.class);
		}
		return Joshua.joshuaSvc;
	}

	/**
	 * 
	 */
	private final Button chatFiltersButton = new Button ("Chat Filters");

	/**
	 * 
	 */
	private Panel contentPanel = new FlowPanel ();

	/**
	 * 
	 */
	private final Label createLabel = new Label ("Accounts");

	/**
	 * 
	 */
	private final VerticalPanel createPanel = new VerticalPanel ();

	/**
	 * 
	 */
	private final Button createParentButton = new Button (
	"Create new Parent");

	/**
	 * 
	 */
	private final Button createStaffButton = new Button (
	"Create new Staff");
	/**
	 * 
	 */
	private final Button createUserButton = new Button (
	"Create new User");

	/**
	 * 
	 */
	private final Label designerLabel = new Label ("Designer");

	/**
	 * 
	 */
	private final VerticalPanel designerPanel = new VerticalPanel ();

	/**
	 * 
	 */
	private final VerticalPanel divBarPanel = new VerticalPanel ();

	/**
	 * 
	 */
	private final Button errorButton = new Button ("close");

	/**
	 * 
	 */
	final Label errorMsgLabel = new Label ();

	/**
	 * 
	 */
	private final Label errorUrlLabel = new Label ();

	/**
	 * 
	 */
	private final Button eventsButton = new Button ("Events");

	/**
	 * 
	 */
	byte [] GWT_DES_KEY;

	/**
	 * 
	 */
	User lifeguard;

	/**
	 * 
	 */
	FlexTable loginBox = new FlexTable ();

	/**
	 * 
	 */
	VerticalPanel loginPanel = new VerticalPanel ();

	/**
	 * 
	 */
	private final HorizontalPanel mainPanel = new HorizontalPanel ();

	/**
	 * 
	 */
	private final Label miscLabel = new Label ("Miscellaneous");

	/**
	 * 
	 */
	private final VerticalPanel miscPanel = new VerticalPanel ();

	/**
	 * 
	 */
	PasswordTextBox passwordField = new PasswordTextBox ();

	/**
	 * 
	 */
	private final String SEARCH_TEXT = "Search...";

	/**
	 * 
	 */
	private final HorizontalPanel searchBarPanel = new HorizontalPanel ();

	/**
	 * 
	 */
	final TextBox searchBox = new TextBox ();

	/**
	 * 
	 */
	private final Button searchButton = new Button ("OK");

	/**
	 * 
	 */
	private final VerticalPanel searchPanel = new VerticalPanel ();

	/**
	 * 
	 */
	private final Button shiftJournalButton = new Button (
	"Shift Journal");

	/**
	 * 
	 */
	Button signInButton = new Button ("Sign In");

	/**
	 * 
	 */
	private final Button superTootButton = new Button (
	"Super Toot Bots");

	/**
	 * 
	 */
	private final Label titleLabel = new Label ("Joshua");

	/**
	 * 
	 */
	private final Button toolBarButton = new Button ("Hide Tools");

	/**
	 * 
	 */
	final VerticalPanel toolPanel = new VerticalPanel ();

	/**
	 * 
	 */
	TextBox usernameField = new TextBox ();

	/**
	 * 
	 */
	String VersionID;
	/**
	 * 
	 */
	private final Button weatherButton = new Button ("Weather Editor");

	/**
	 * TODO: Document this method. Dec 7, 2009 : theys
	 * 
	 * @param newPage WRITEME
	 */
	public void changePage (final Panel newPage) {
		contentPanel.removeFromParent ();
		contentPanel = newPage;
		mainPanel.add (contentPanel);
	}

	/**
	 * 
	 * TODO: Document this method. Dec 7, 2009 : theys
	 * 
	 */
	void checkVersion () {
		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <String> callback = new AsyncCallback <String> () {
			@Override
			public void onFailure (final Throwable caught) {
				Window
				.alert ("Error checking the version, please refresh your browser.");
			}

			@Override
			public void onSuccess (final String result) {
				if (!VersionID.equals (result)) {
					Window
					.alert ("You client version is out of date.  Please refresh your browser.");
				}
			}
		};

		// Make the call to the search service.
		service.getVersion (callback);
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void createParent () {
		Window.alert ("This feature is not supported yet.");
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void createStaff () {
		Window.alert ("This feature is not supported yet.");
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void createUser () {
		changePage (new CreateUser (this));
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void destroyLoginPrompt () {
		RootPanel.get ("main").remove (loginPanel);
	}

	/**
	 * 
	 * WRITEME
	 * 
	 * @param error
	 */
	@SuppressWarnings ("unused")
	private void displayError (final String error) {
		errorMsgLabel.setText ("Error: " + error);
		errorMsgLabel.setVisible (true);
	}

	/**
	 * 
	 * WRITEME
	 * 
	 * @param result
	 */
	protected void displayUserResults (final Person result) {
		if (result instanceof User) {
			changePage (new UserDetails (this, (User) result));
		} else

			if (result instanceof Parent) {
				changePage (new ParentDetails (this, (Parent) result));
			}

	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void editChatFilters () {
		Window.alert ("This feature is not supported yet.");
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void editEvents () {
		Window.alert ("This feature is not supported yet.");
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void editShiftJournal () {
		Window.alert ("This feature is not supported yet.");
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void editSuperToots () {
		Window.alert ("This feature is not supported yet.");

	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	protected void editWeather () {
		Window.alert ("This feature is not supported yet.");
	}

	// @SuppressWarnings ("unused")
	// private String encryptString (final String string) {
	// final TripleDesCipher cipher = new TripleDesCipher ();
	// cipher.setKey (GWT_DES_KEY);
	// String encodedString = string;
	// try {
	// encodedString = cipher.encrypt (string);
	// } catch (final DataLengthException e1) {
	// Window.alert ("Encryption fails");
	// e1.printStackTrace ();
	// } catch (final IllegalStateException e1) {
	// Window.alert ("Encryption fails");
	// e1.printStackTrace ();
	// } catch (final InvalidCipherTextException e1) {
	// Window.alert ("Encryption fails");
	// e1.printStackTrace ();
	// }
	// return encodedString;
	// }

	@SuppressWarnings ("unused")
	private void getKey () {
		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <byte []> callback = new AsyncCallback <byte []> () {
			@Override
			public void onFailure (final Throwable caught) {
				Window
				.alert ("Error checking the version, please refresh your browser.");
			}

			@Override
			public void onSuccess (final byte [] result) {
				GWT_DES_KEY = result;
				// Window.alert (String.valueOf (GWT_DES_KEY));
			}
		};

		// Make the call to the search service.
		service.getKey (callback);
	}

	public User getLifeguard () {
		// default getter (theys, Oct 6, 2009)
		return lifeguard;
	}

	/**
	 * retrieve the version from the server. This version ID is checked every 5
	 * minutes to verify the client is still updated with the latest version.
	 */
	private void getVersion () {
		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <String> callback = new AsyncCallback <String> () {
			@Override
			public void onFailure (final Throwable caught) {
				Window
				.alert ("Error checking the version, please refresh.\n"
						+ caught.getMessage ());
			}

			@Override
			public void onSuccess (final String result) {
				VersionID = result;
				final Timer refreshTimer = new Timer () {
					@Override
					public void run () {
						checkVersion ();
					}
				};

				refreshTimer
				.scheduleRepeating (Joshua.VERSION_CHECK_INTERVAL);
			}
		};

		// Make the call to the search service.
		service.getVersion (callback);
	}

	/**
	 * load the main application interface
	 */
	protected void loadJoshua () {

		// Assemble Error Dialog Box
		errorMsgLabel.setStyleName ("errorMessage");
		errorMsgLabel.setVisible (false);

		// Assemble Main panel.
		// kittyImg.setStyleName ("kittyImg");
		titleLabel.setStyleName ("toolBoxLabel");
		createLabel.setStyleName ("toolBoxLabel");
		designerLabel.setStyleName ("toolBoxLabel");
		miscLabel.setStyleName ("toolBoxLabel");

		// Construct the search box
		searchBox.setTitle (Joshua.searchToolTip);
		searchPanel.add (titleLabel);
		searchBarPanel.add (searchBox);
		searchBarPanel.add (searchButton);
		searchBarPanel.setSpacing (Joshua.H_SPACING);
		searchButton.setStyleName ("searchButton");
		searchPanel.add (errorUrlLabel);
		searchPanel.add (searchBarPanel);
		searchPanel.addStyleName ("toolBox");

		// Construct the account box
		createPanel.add (createLabel);
		createPanel.add (createUserButton);
		createPanel.add (createParentButton);
		createPanel.add (createStaffButton);
		createPanel.setSpacing (Joshua.V_SPACING);
		createPanel.addStyleName ("toolBox");

		if (lifeguard.hasStaffLevel (Joshua.STAFF_DESIGNER)) {
			// Construct the designer Panel
			designerPanel.add (designerLabel);
			designerPanel.add (eventsButton);
			designerPanel.add (superTootButton);
			designerPanel.add (weatherButton);
			designerPanel.setSpacing (Joshua.V_SPACING);
			designerPanel.addStyleName ("toolBox");
		}

		// Construct the miscellaneous Panel
		miscPanel.add (miscLabel);
		miscPanel.add (chatFiltersButton);
		miscPanel.add (shiftJournalButton);
		miscPanel.setSpacing (Joshua.V_SPACING);
		miscPanel.addStyleName ("toolBox");

		// Construct the Tool Bar
		toolPanel.add (searchPanel);
		toolPanel.add (createPanel);
		toolPanel.add (designerPanel);
		toolPanel.add (miscPanel);
		toolPanel.add (new Label ("Version: " + VersionID));
		toolPanel.setStyleName ("toolBar");
		// toolPanel.add (kittyImg);

		toolBarButton.setStyleName ("hideButton");
		divBarPanel.add (toolBarButton);

		// Construct the window
		mainPanel.setHeight ("100%");
		mainPanel.add (toolPanel);
		mainPanel.add (divBarPanel);
		mainPanel.add (contentPanel);
		mainPanel.add (errorMsgLabel);

		// Insert the dynamic content into the html
		RootPanel.get ("main").setHeight ("100%");
		RootPanel.get ("main").add (mainPanel);

		searchBox.setText (SEARCH_TEXT);
		searchBox.setStyleName ("preText");

		toolBarButton.addClickHandler (new ClickHandler () {
			@SuppressWarnings ("synthetic-access")
			@Override
			public void onClick (final ClickEvent event) {
				if (toolPanel.isVisible ()) {
					toolPanel.setVisible (false);
					toolBarButton.setText ("Show Tools");
					toolBarButton.setStyleName ("smallButton");
				} else {
					toolPanel.setVisible (true);
					toolBarButton.setText ("Hide Tools");
					toolBarButton.setStyleName ("hideButton");
				}
			}
		});

		// Manage text in search box
		searchBox.addFocusHandler (new FocusHandler () {
			@Override
			public void onFocus (final FocusEvent event) {
				final String text = searchBox.getText ();
				if (SEARCH_TEXT.equals (text)) {
					searchBox.setText ("");
					searchBox.setStyleName ("typeText");
				}
			}
		});

		searchBox.addBlurHandler (new BlurHandler () {
			@Override
			public void onBlur (final BlurEvent event) {
				final String text = searchBox.getText ();
				if (0 == text.length ()) {
					searchBox.setText (SEARCH_TEXT);
					searchBox.setStyleName ("preText");
				}
			}
		});

		// Add listeners to search box and button
		searchBox.addKeyPressHandler (new KeyPressHandler () {

			@Override
			public void onKeyPress (final KeyPressEvent event) {
				if (KeyCodes.KEY_ENTER == event.getCharCode ()) {
					search (searchBox.getText ());
				}
			}
		});

		searchButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				search (searchBox.getText ());
				searchBox.setFocus (false);
			}
		});

		// Add listener to error box
		errorButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				errorMsgLabel.setVisible (false);
			}
		});

		// Add listeners to account buttons
		createUserButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				createUser ();
			}
		});

		createParentButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				createParent ();
			}
		});

		createStaffButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				createStaff ();
			}
		});

		// Add listeners to Designer buttons
		eventsButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				editEvents ();
			}
		});

		superTootButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				editSuperToots ();
			}
		});

		weatherButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				editWeather ();
			}
		});

		// Add listeners to Misc Buttons
		chatFiltersButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				editChatFilters ();
			}
		});

		shiftJournalButton.addClickHandler (new ClickHandler () {
			@Override
			public void onClick (final ClickEvent event) {
				editShiftJournal ();
			}
		});
	}

	/**
	 * 
	 * WRITEME
	 * 
	 * @param username
	 * @param password
	 */
	protected void login (final String username, final String password) {
		if (null == username || "".equals (username)) {
			Window.alert ("Username field is blank!");
			return;
		}

		if (null == password || "".equals (password)) {
			Window.alert ("Password field is blank!");
			return;
		}

		final AsyncCallback <User> callback = new AsyncCallback <User> () {
			@Override
			public void onFailure (final Throwable caught) {
				if (caught instanceof PermissionRequiredException
						|| caught instanceof InvalidCredentialsException) {
					Window.alert (caught.getMessage ());
				} else {
					Window.alert (caught.getMessage ());
					caught.printStackTrace ();
				}
			}

			@Override
			public void onSuccess (final User result) {
				lifeguard = result;
				destroyLoginPrompt ();
				loadJoshua ();
			}
		};

		// Make the call to the search service.
		Joshua.initService ().loginStaff (username, password, callback);
	}

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad () {
		getVersion ();
		// getKey ();
		promptForLogin ();
	}

	/**
	 * 
	 * WRITEME
	 * 
	 */
	void promptForLogin () {
		Window.setTitle (Joshua.joshuaTitle);

		loginBox.setText (0, 0, "Sign in to your account.");
		loginBox.setText (1, 0, "Username: ");
		loginBox.setWidget (1, 1, usernameField);
		loginBox.setText (2, 0, "Password: ");
		loginBox.setWidget (2, 1, passwordField);
		loginBox.setWidget (3, 1, signInButton);
		loginBox.setStyleName ("loginBox");
		signInButton.addStyleName ("smallButton");

		loginPanel.add (loginBox);

		RootPanel.get ("main").add (loginPanel);

		signInButton.addClickHandler (new ClickHandler () {

			@Override
			public void onClick (final ClickEvent event) {
				login (usernameField.getText (), passwordField
						.getText ());
			}
		});

		usernameField.addKeyPressHandler (new KeyPressHandler () {
			@Override
			public void onKeyPress (final KeyPressEvent event) {
				if (KeyCodes.KEY_ENTER == event.getCharCode ()) {
					login (usernameField.getText (), passwordField
							.getText ());
				}
			}
		});

		passwordField.addKeyPressHandler (new KeyPressHandler () {
			@Override
			public void onKeyPress (final KeyPressEvent event) {
				if (KeyCodes.KEY_ENTER == event.getCharCode ()) {
					login (usernameField.getText (), passwordField
							.getText ());
				}
			}
		});
	}

	/**
	 * 
	 * WRITEME
	 * 
	 * @param searchParam
	 */
	public void search (final String searchParam) {
		// Don't search for the default text or an empty box
		if ("".equals (searchParam) || SEARCH_TEXT.equals (searchParam))
			return;

		final JoshuaServiceAsync service = Joshua.initService ();

		// Set up the callback object.
		final AsyncCallback <Person> callback = new AsyncCallback <Person> () {
			@Override
			public void onFailure (final Throwable caught) {
				Window.alert (caught.getMessage ());
				searchBox.setFocus (true);
				searchBox.selectAll ();
			}

			@Override
			public void onSuccess (final Person result) {
				searchBox.setText (SEARCH_TEXT);
				searchBox.setFocus (false);
				displayUserResults (result);
			}
		};

		// Make the call to the search service.
		service.getPerson (searchParam, callback);
	}
}
