package zall.bean;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import zall.Zallery;
import zutil.Hasher;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import zutil.db.bean.DBBeanSQLResultHandler;
import zutil.db.bean.DBBean.*;
import zutil.db.handler.SimpleSQLResult;

@DBTable("User")
public class User extends DBBean{
	public enum AuthType{
		USER_INPUT, COOKIE
	}
	public static final long SESSION_TIMEOUT = 1000*60*60*24*3; // 3day ttl

	protected String name;
	protected String email;
	protected boolean emailVerified;
	protected String password;
	// Date
	protected Timestamp loginDate;
	protected transient Timestamp prevLoginDate;
	// security
	protected transient AuthType authBy;
	protected String sessionId;
	protected String ipHost;
	protected String sessionHash;

	protected boolean superUser;
	protected boolean enabled;


	public static User load(DBConnection db, Long id) throws SQLException{
		return load(db, User.class, id);
	}

	public static List<User> load(DBConnection db) throws SQLException{
		PreparedStatement sql = db.getPreparedStatement("SELECT * FROM User");
		return DBConnection.exec(sql, DBBeanSQLResultHandler.createList(User.class, db));
	}


	/**
	 * Uses normal user and password to get user object,
	 * this function will save the bean
	 * 
	 * @param 		db				is the DB connection
	 * @param 		email			is the email of the user
	 * @param		password		is the password of the user
	 * @param 		request			is the HTTP request object
	 * @return						The user object or null if non where found
	 * @throws SQLException
	 */
	public static User load(HttpServletRequest request, HttpServletResponse response, DBConnection db, String email, String password ) throws SQLException{
		if( password==null || password.isEmpty() || password.equalsIgnoreCase("null"))
			return null;
		PreparedStatement sql = db.getPreparedStatement(
			"SELECT * FROM User WHERE email=? AND password=? LIMIT 1");
		sql.setString(1, email);
		sql.setString(2, Hasher.MD5( password ));

		User user = DBConnection.exec(sql, DBBeanSQLResultHandler.create(User.class, db));
		if( user != null ){
			user.registerOnHost(request, response, db, true );
			user.save(db);
			user.setAuthBy( AuthType.USER_INPUT );
		}
		return user;
	}
	
	public static List<User> loadSuperUsers(DBConnection db) throws SQLException {
		PreparedStatement sql = db.getPreparedStatement(
			"SELECT * FROM User WHERE superUser=1");
		return DBConnection.exec(sql, DBBeanSQLResultHandler.createList(User.class, db));
	}

	/**
	 * Uses a cookie value to get the user object,
	 * this function will save the bean
	 * 
	 * @param 		db				is the DB connection
	 * @param 		hash			is the cookie hash
	 * @param 		request			is the HTTP request object
	 * @return						The user object or null if non where found
	 * @throws SQLException
	 */
	public static User loadByCookie(HttpServletRequest request, DBConnection db, String hash ) throws SQLException{
		PreparedStatement sql = db.getPreparedStatement(
			"SELECT * FROM User WHERE sessionHash=? LIMIT 1");
		sql.setString(1, hash);

		User user = DBConnection.exec(sql, DBBeanSQLResultHandler.create(User.class, db));
		if( user != null &&
				user.ipHost.equals( request.getLocalName() ) &&
				user.loginDate.getTime()+SESSION_TIMEOUT > System.currentTimeMillis() ){
			user.prevLoginDate = user.loginDate;
			user.loginDate = new Timestamp( System.currentTimeMillis() );
			user.save(db);
			user.setAuthBy( AuthType.COOKIE );
			return user;
		}
		return null;
	}

	public static boolean emailExists(String email, DBConnection db) throws SQLException{
		PreparedStatement sql = db.getPreparedStatement(
			"SELECT email FROM User WHERE email=? LIMIT 1");
		sql.setString(1, email);
		
		String tmp = DBConnection.exec(sql, new SimpleSQLResult<String>());
		return tmp != null;
	}
	

	public User(){
		// Default values
		emailVerified = false;
		superUser = false;
		enabled = false;
	}

	/**
	 * Registers the User to the Host machine that sent the request, 
	 * this method alters the bean, so a call to save() is recommended
	 * 
	 * @param 		db			is the DB connection
	 * @param 		request		is the request from the Host/Client
	 * @throws SQLException
	 */
	public void registerOnHost(HttpServletRequest request, HttpServletResponse response, DBConnection db, boolean cookie) throws SQLException{
		prevLoginDate = loginDate;
		loginDate = new Timestamp( System.currentTimeMillis() );
		sessionId = request.getSession().getId();
		ipHost = request.getRemoteAddr();
		sessionHash = Hasher.MD5( ""+sessionId+ipHost+loginDate+password );
		if( cookie ){
			Cookie c = new Cookie("sessionHash", sessionHash );
			c.setMaxAge(5*24*60*60); // 5 days
			response.addCookie( c );			
		}
	}

	public void logout(HttpServletResponse response) {
		Cookie cookie = new Cookie( "sessionHash", null);
		cookie.setMaxAge( 0 );
		response.addCookie( cookie );		
	}


	public boolean valid(HttpServletRequest request){
		if( !this.isEnabled() ) return false;
		switch( authBy ){
		case USER_INPUT:
			if( !this.isEmailVerified() ) return false;
		case COOKIE:
			return ( sessionHash.equals( Zallery.getCookieValue(request.getCookies(), "sessionHash")) ||
					loginDate.getTime()+1000 > System.currentTimeMillis() ) &&
					ipHost.equals( request.getRemoteAddr() ) &&
					loginDate.getTime()+SESSION_TIMEOUT > System.currentTimeMillis();
		}
		return false;
	}
	public boolean verifyEmail(String hash) {		
		return emailVerified = getEmailVerificationHash().equals(hash);
	}
	public String getEmailVerificationHash(){
		return Hasher.MD5( "##helloWorld-->2011"+email+name+password );
	}


	public Timestamp getLoginDate() {
		if( loginDate == null )
			loginDate = new Timestamp(0);
		return loginDate;
	}
	public void setLoginDate(Timestamp loginDate) {
		this.loginDate = loginDate;
	}
	public Timestamp getPrevLoginDate() {
		if( loginDate == null )
			loginDate = new Timestamp(0);
		return prevLoginDate;
	}
	public void setPrevLoginDate(Timestamp prevLoginDate) {
		this.prevLoginDate = prevLoginDate;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		if( this.email != null && this.email.equals(email) )
			return;
		emailVerified = false;
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = Hasher.MD5( password );
	}
	public boolean equalsPassword( String pass ){
		return Hasher.MD5( pass ).equals( password );
	}
	public String getSessionId() {
		return sessionId;
	}
	public void setSessionId(String sessionId) {
		this.sessionId = sessionId;
	}	
	public String getIpHost() {
		return ipHost;
	}
	public void setIpHost(String ipHost) {
		this.ipHost = ipHost;
	}
	public String getSessionHash() {
		return sessionHash;
	}
	public boolean isSuperUser(){
		return superUser;
	}
	public void setSuperUser(boolean superuser){
		this.superUser = superuser;
	}
	public boolean isEnabled(){
		return enabled;
	}
	public void setEnabled(boolean enabled){
		this.enabled = enabled;
	}
	public boolean isEmailVerified(){
		return emailVerified;
	}
	public void setEmailVerified(boolean verified){
		this.emailVerified = verified;
	}
	public void setAuthBy(AuthType authBy){
		this.authBy = authBy;
	}
	public AuthType getAuthBy(){
		return authBy;
	}
	
	public boolean equals(User u){
		return u != null && this.getId() == u.getId();		
	}
	
	
	/**
	 * @return true if the specified user can edit the media
	 */
	public boolean canEdit(Media target) {
		return target != null && (this.isSuperUser() || target.getUser().equals(this));
	}
	public boolean canEdit(Folder target) {
		return target != null && (this.isSuperUser() || this.equals( target.getUser() ));
	}
	public boolean canEdit(User target){
		return this.equals( target ) || this.superUser;
	}
}
