package com.tootsville.joshua.server;

import java.math.BigDecimal;
import java.sql.Date;
import java.util.Calendar;

import org.starhope.appius.except.AlreadyExistsException;
import org.starhope.appius.except.AlreadyUsedException;
import org.starhope.appius.except.ForbiddenUserException;
import org.starhope.appius.except.GameLogicException;
import org.starhope.appius.except.PrivilegeRequiredException;
import org.starhope.appius.game.AppiusClaudiusCaecus;
import org.starhope.appius.mb.Enrolment;
import org.starhope.appius.mb.UserEnrolment;
import org.starhope.appius.messaging.Mail;
import org.starhope.appius.user.Parent;
import org.starhope.appius.user.Person;
import org.starhope.appius.user.User;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.tootsville.joshua.client.except.ForbiddenException;
import com.tootsville.joshua.client.except.PermissionRequiredException;
import com.tootsville.joshua.client.service.JoshuaService;
import com.tootsville.joshua.server.util.ServerClientTranslator;
import com.tootsville.user.Toot;

/**
 * 
 * @author Tim Heys
 * 
 *         Server side class that implements all the calls from the
 *         client side.
 * 
 */
@SuppressWarnings ("serial")
public class JoshuaServiceImpl extends RemoteServiceServlet implements
		JoshuaService {

	/**
	 * 
	 */
	private static final String KEY = "|9nk4o>mf_d@VRLTjt(TS}`-";

	/**
	 * 
	 */
	private static final String VersionID = "0.02.001a";

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#approve(int)
	 */
	@Override
	public void approve (final int userID) {
		final User serverUser = User.getByID (userID);
		if (null == serverUser)
			return;
		serverUser.parentApprovedAccount (true);
	}

	/**
	 * 
	 */
	@Override
	public void associateChild (final String mail,
			final String newUserName) throws NotFoundException,
			InvalidCredentialsException {
		final Parent serverParent = Parent.getByMail (mail);
		final User serverUser = User.getByLogin (newUserName);

		if (null == serverParent || null == serverUser)
			throw new NotFoundException ();

		try {
			serverUser.setParent (serverParent);
		} catch (final GameLogicException e) {
			throw new InvalidCredentialsException (
					"Cannot give that child to this parent!");
		} catch (final ForbiddenUserException e) {
			throw new InvalidCredentialsException (
					"Cannot give that child to this parent!");
		}
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#banOrUnban(int,
	 *      int, String)
	 */
	@Override
	public void banOrUnban (final int userID, final int lifeguardID,
			final String reason) {
		User serverUser = null;
		User lifeguard = null;
		try {
			serverUser = User.getByID (userID);
			lifeguard = User.getByID (lifeguardID);
		} catch (final Exception e) {
			AppiusClaudiusCaecus
					.reportBug ("User not found by Joshua for user ID#"
							+ userID, e);
			return;
		}

		if (null == serverUser || null == lifeguard)
			return;

		if (!serverUser.isBanned ()) {
			try {
				serverUser.ban (lifeguard, reason);
			} catch (final PrivilegeRequiredException e) {
				// Default catch action, report bug (theys, Oct 16,
				// 2009)
				AppiusClaudiusCaecus
						.reportBug (
								"Lifeguard account does not contain sufficient priviledges",
								e);
				return;
			}
		} else
			// FIXME: create method in User to unban users.
			return;
	}

	/**
	 * 
	 */
	@Override
	public void cancelUser (final int userID) {
		User serverUser = null;
		try {
			serverUser = User.getByID (userID);
		} catch (final Exception e) {
			AppiusClaudiusCaecus
					.reportBug ("User not found by Joshua for user ID#"
							+ userID, e);
			return;
		}

		if (null == serverUser)
			return;

		if (!serverUser.isBanned ()) {
			serverUser.setCanceled (true);
		} else {
			serverUser.setCanceled (false);
		}
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#createNewParent(com.tootsville.gwt.joshua.client.util.Parent)
	 */
	@Override
	public void createNewParent (
			final com.tootsville.gwt.joshua.client.util.Parent parent) {
		// TODO Auto-generated method stub (theys, Oct 2, 2009)

	}

	/**
	 * @deprecated
	 * 
	 */
	@Deprecated
	@Override
	public void createNewStaff (
			final com.tootsville.gwt.joshua.client.util.User person) {
		// TODO Auto-generated method stub (theys, Oct 2, 2009)

	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#createOrEditUser(com.tootsville.gwt.joshua.client.util.User,
	 *      boolean)
	 */
	public void createOrEditUser (
			final com.tootsville.gwt.joshua.client.util.User clientUser,
			final boolean isNewAccount) {
		Toot serverUser = null;
		if (isNewAccount) {
			try {
				// serverUser = new User (
				// java.sql.Date.valueOf (clientUser
				// .getBirthDate ()),
				// SQLPeerEnum.getString (clientUser.getBasic8 ()),
				// clientUser.getUserName ());
			} catch (final Exception e) {
				// Default catch action, report bug (theys, Oct 8, 2009)
				AppiusClaudiusCaecus.reportBug (e);
			}
		} else {
			serverUser = (Toot) User.getByID (clientUser.getUserID ());
		}

		if (null == serverUser) {
			try {
				throw new Exception ();
			} catch (final Exception e) {
				AppiusClaudiusCaecus.reportBug (e);
				return;
			}
		}

		try {
			serverUser.setUserName (clientUser.getUserName ());
			serverUser.setForgotPasswordQuestion (clientUser
					.getQuestion ());
			serverUser.setForgotPasswordAnswer (clientUser
					.getQuestion ());
			serverUser.setBasic8Choice (clientUser.getBasic8 ());
			if (0 != clientUser.getStaffLevel ()) {
				serverUser.setStaffLevel (clientUser.getStaffLevel ());
			}
			try {
				serverUser.setMail (clientUser.getMail ());
			} catch (final GameLogicException e) {
				try {
					serverUser.setParent (Parent
							.getOrCreateByMail (clientUser.getMail ()));
				} catch (final GameLogicException e1) {
					// Default catch action, report bug (theys, Oct 8,
					// 2009)
					AppiusClaudiusCaecus.reportBug (e1);
					return;
				}
			}
		} catch (final AlreadyUsedException e) {
			// Default catch action, report bug (theys, Oct 8, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		} catch (final ForbiddenUserException e) {
			// Default catch action, report bug (theys, Oct 8, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}

	}

	// private String decryptString (final String string) {
	// final TripleDesCipher cipher = new TripleDesCipher ();
	// cipher.setKey (getKey ());
	//
	// String decodedString = string;
	// // Decode username and password
	// try {
	// decodedString = cipher.decrypt (string);
	// } catch (final DataLengthException e) {
	// AppiusClaudiusCaecus.reportBug (e);
	// } catch (final IllegalStateException e) {
	// AppiusClaudiusCaecus.reportBug (e);
	// } catch (final InvalidCipherTextException e) {
	// AppiusClaudiusCaecus.reportBug (e);
	// }
	//
	// return decodedString;
	// }

	/**
	 * 
	 */
	@Override
	public byte [] getKey () {
		System.out.println (String.valueOf (JoshuaServiceImpl.KEY
				.getBytes ()));
		return JoshuaServiceImpl.KEY.getBytes ();
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#getPerson(java.lang.String)
	 */
	@Override
	public com.tootsville.gwt.joshua.client.util.Person getPerson (
			final String searchParam) throws NotFoundException {
		Toot tmpUser = null;
		org.starhope.appius.user.Parent tmpParent = null;
		System.out.println ("Retrieving a person: " + searchParam);

		/**
		 * Search for a User by their ID
		 */
		try {
			tmpUser = (Toot) User.getByID (Integer
					.parseInt (searchParam));
			if (null != tmpUser) {
				System.out.println ("found: "
						+ tmpUser.getDisplayName ());
			}
			return ServerClientTranslator.getClientUser (tmpUser);
		} catch (final NumberFormatException e) {
			// do nothing, this is okay
		}

		/*
		 * Search for a Parent by their ID
		 */
		try {
			tmpParent = org.starhope.appius.user.Parent
					.getByID (Integer.parseInt (searchParam));
			if (null != tmpParent) {
				System.out.println ("found: "
						+ tmpParent.getDisplayName ());
			}
			return ServerClientTranslator.getClientParent (tmpParent);
		} catch (final NumberFormatException e) {
			// do nothing, this is okay
		}

		/**
		 * Search for a User by their Login
		 */
		tmpUser = (Toot) User.getByLogin (searchParam);
		if (null != tmpUser) {
			System.out.println ("found: " + tmpUser.getDisplayName ());
			return ServerClientTranslator.getClientUser (tmpUser);
		}

		/**
		 * Search for a Parent by their Mail
		 */
		tmpParent = Parent.getByMail (searchParam);
		if (null != tmpParent) {
			System.out
					.println ("found: " + tmpParent.getDisplayName ());
			return ServerClientTranslator.getClientParent (tmpParent);
		}

		/**
		 * Search for a User by their approval cookie
		 */
		try {
			tmpUser = Toot.getByApprovalCookie (searchParam);
			if (null != tmpUser) {
				System.out.println ("found: "
						+ tmpUser.getDisplayName ());
				return ServerClientTranslator.getClientUser (tmpUser);
			}
		} catch (final Exception e) {
			// Do nothing, this is OK
		}

		/**
		 * Search for a Parent by their approval cookies
		 */
		try {
			tmpParent = Parent.getByApprovalCookie (searchParam);
			if (null != tmpParent) {
				System.out.println ("found: "
						+ tmpParent.getDisplayName ());
				return ServerClientTranslator
						.getClientParent (tmpParent);
			}
		} catch (final Exception e) {
			// Do nothing, this is OK
		}

		/**
		 * Search for a User by their orderSource and orderCode split on
		 * the '-' character
		 */
		try {
			System.out.println ("Searching for source: "
					+ searchParam.substring (0, searchParam
							.indexOf ('-'))
					+ " and code: "
					+ searchParam
							.substring (searchParam.indexOf ('-') + 1));
			tmpUser = (Toot) UserEnrolment.getBySourceAndCode (
					searchParam
							.substring (0, searchParam.indexOf ('-')),
					searchParam
							.substring (searchParam.indexOf ('-') + 1))
					.getUser ();
			return ServerClientTranslator.getClientUser (tmpUser);
		} catch (final Exception e) {
			// Do nothing, this is OK
		}

		/**
		 * If the method gets this far without returning, then no User
		 * or Parent was found. Throwing this exception will cause the
		 * client to display an error message.
		 */
		throw new NotFoundException (searchParam);
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#getVersion()
	 */
	@Override
	public String getVersion () {
		System.out.println ("Checking version");
		return JoshuaServiceImpl.VersionID;
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see com.tootsville.gwt.joshua.client.service.JoshuaService#giftTime(int,
	 *      int)
	 */
	@Override
	public void giftTime (final int userID, final int months) {
		final User serverUser = User.getByID (userID);
		if (null == serverUser)
			return;
		UserEnrolment subscription;
		try {
			if (0 < months) {
				subscription = new UserEnrolment ("gift", Enrolment
						.getByID (8).getProductID (), serverUser
						.getUserID ());
				final Calendar cal = Calendar.getInstance ();
				subscription.activate (false);
				cal.setTimeInMillis (subscription.getBegins ()
						.getTime ());
				cal.add (Calendar.MONTH, months);
				subscription.setExpires (new Date (cal
						.getTimeInMillis ()));
				subscription.flush ();
			} else if (-1 == months) {
				subscription = new UserEnrolment ("life", Enrolment
						.getByID (6).getProductID (), serverUser
						.getUserID ());
				subscription.activate (false);
			} else if (-2 == months) {
				subscription = new UserEnrolment ("staf", Enrolment
						.getByID (7).getProductID (), serverUser
						.getUserID ());
				subscription.activate (false);
			}
		} catch (final org.starhope.appius.except.NotFoundException e) {
			// Default catch action, report bug (theys, Oct 6, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}
	}

	/**
     * 
     */
	@Override
	public String givePeanuts (final long numNuts, final int userID)
			throws InvalidCredentialsException {
		final Toot serverUser = (Toot) User.getByID (userID);
		try {
			serverUser
					.addPeanuts (new BigDecimal (numNuts), "givenuts");
		} catch (final NumberFormatException e) {
			AppiusClaudiusCaecus.reportBug (e);
			throw new InvalidCredentialsException ();
		} catch (final AlreadyExistsException e) {
			AppiusClaudiusCaecus.reportBug (e);
			throw new InvalidCredentialsException ();
		}
		return serverUser.getPeanuts ().toPlainString ();
	}

	/**
     * 
     */
	@Override
	public com.tootsville.gwt.joshua.client.util.User loginStaff (
			final String username, final String password)
			throws PermissionRequiredException,
			InvalidCredentialsException {
		System.out.println ("Logging in " + username);

		final Toot lifeguard = (Toot) User.getByLogin (/**
		 * decryptString
		 * (username)
		 */
		username);
		if (null == lifeguard)
			throw new InvalidCredentialsException ();
		if (!lifeguard.checkPassword (/** decryptString (password) */
		password))
			throw new InvalidCredentialsException ();
		if (lifeguard.hasStaffLevel (1))
			return ServerClientTranslator.getClientUser (lifeguard);

		throw new PermissionRequiredException (username);
	}

	/**
     * 
     */
	@Override
	public void remindPassword (final int userID, final boolean isUser) {
		Person serverUser;
		if (isUser) {
			serverUser = User.getByID (userID);
			if (null == serverUser)
				return;
		} else {
			serverUser = Parent.getByID (userID);
			if (null == serverUser)
				return;
		}
		// Send a reminder e-mail
		serverUser.forgotPassword (serverUser
				.getForgotPasswordQuestion (), serverUser
				.getForgotPasswordAnswer ());
	}

	/**
     * 
     */
	@Override
	public void resetPassword (final int userID, final boolean isUser) {
		// do nothing yet
	}

	/**
     * 
     */
	@Override
	public void setMail (final int userID, final String newMail)
			throws InvalidCredentialsException {
		final User serverUser = User.getByID (userID);
		if (null == serverUser)
			return;
		try {
			serverUser.setMail (newMail);
		} catch (final GameLogicException e) {
			throw new InvalidCredentialsException (
					"An email address cannot be assigned to this account.");
		}
	}

	/**
     * 
     */
	@Override
	public void setParent (final int userID, final String newMail)
			throws InvalidCredentialsException {
		final User serverUser = User.getByID (userID);
		if (null == serverUser)
			return;
		try {
			serverUser.setParent (Parent.getOrCreateByMail (newMail));
		} catch (final GameLogicException e) {
			throw new InvalidCredentialsException (
					"A parent cannot be assigned to this account.");
		} catch (final ForbiddenUserException e) {
			throw new InvalidCredentialsException (
					"A parent cannot be assigned to this account.");
		}
	}

	/**
     * 
     */
	@Override
	public void verifyMail (final String mail)
			throws InvalidCredentialsException {
		try {
			Mail.isValidMail (mail);
		} catch (final Exception e) {
			throw new InvalidCredentialsException ();
		}
	}

	/**
     * 
     */
	@Override
	public void verifyUserName (final String userName)
			throws InvalidCredentialsException, ForbiddenException {
		try {
			User.assertUserNameAvailable (userName);
		} catch (final AlreadyUsedException e) {
			throw new InvalidCredentialsException ();
		} catch (final ForbiddenUserException e) {
			throw new ForbiddenException ();
		}

	}
}
