<?php 
// namespace models;
// use helpers\session as Session;
// use helpers\cookie as Cookie;
// use \PDO; //uncomment for PDOStatement::fetchObjects
// may be need in  Facebook lib...

/**
* LoginModel : The Database object
* Handles the user's login / logout / registration stuff
*/
class Login_Model extends Model{
	/**
	* Constructor, expects a Database connection
	**/
	public function __construct(){
		parent::__construct();
	}

	/**
	* Login process (for DEFAULT user accounts), this procedure also initialize all session variables and cookies
	* Users who login with Facebook etc. are handled with loginWithFacebook()
	* @param STRING email and password INT remember me
	* @return BOOL success state, and session negative feedback by need
	**/
	public function get_Login($p_user_email,$p_user_password,$p_user_rememberme){
		// firstly negative-first checks here
		if(empty($p_user_email) OR empty($p_user_password)){
			// Session::set('feedback_negative', FEEDBACK_EMAIL_FIELD_EMPTY);
			return false;
		}
		// get user's data
		// (we check if the password fits the password_hash via password_verify() some lines below)
		// DEFAULT is the marker for "normal" accounts (that have a password etc.)
		// There are other types of accounts that don't have passwords etc. (FACEBOOK)
		// $DFSSD =  PDO::FETCH_OBJ;
		$query = $this->_db->selectOne("SELECT user_id,
										user_gender,
										user_email,
										user_password_hash,
										user_active,
										user_account_type,
										user_failed_logins,
										user_last_failed_login,
										user_creation_hash_salt
									FROM ".PREFIX."usr
									WHERE user_email = :user_email
										AND user_provider_type = :provider_type"
										,array(':user_email' => $p_user_email
										,':provider_type' => 'DEFAULT'));//,$DFSSD);
		// if there's NOT one result
		if(!$query){
			// here LOGIN_FAILED to prevent potential attackers showing if the user exists
			Session::set("feedback_negative", 'LOGIN_FAILED');
			return false;
		}
		// block login attempt if somebody has already failed 3 times and the last login attempt is less than 2 minutes ago
		if(($query->user_failed_logins >= 3) AND ($query->user_last_failed_login > (time()-120))) {
			Session::set("feedback_negative", 'PASSWORD_WRONG_3_TIMES');
			return false;
		}
		//echo $query->user_last_failed_login;
		// check if hash of provided password matches the hash in the database
		// crypt(p_user_password, BLOWFISH_HASH_COST.user_creation_hash_salt)
		// crypt using current (BLOWFISH_HASH_COST) Blowfish + only 04 iterations the salt those the magic
		if(crypt($p_user_password, BLOWFISH_HASH_COST.$query->user_creation_hash_salt) == $query->user_password_hash){
			if ($query->user_active != 1) {
				Session::set("feedback_negative", 'ACCOUNT_NOT_ACTIVATED_YET');
				return false;
			}
			// login process, write the user data into session
			Session::init();
			Session::set('user_logged_in', true);
			Session::set('user_id', $query->user_id);
			Session::set('user_gender', $query->user_gender);
			//Session::set('user_name', $query->user_name);
			Session::set('user_email', $query->user_email);
			Session::set('user_account_type', $query->user_account_type);
			Session::set('user_provider_type', 'DEFAULT');
			// reset the failed login counter for that user (if necessary)
			if ($query->user_last_failed_login > 0) {
				$this->_db->update(PREFIX."usr"
									,array('user_failed_logins' => 0,'user_last_failed_login' => null)
									,array('user_id' => $query->user_id));
			}
			// generate integer-timestamp for saving of last-login date
			$user_last_login_timestamp = time();
			// write time stamp of this login into database (we only write "real" logins via login form into the
			// database, not the session-login on every page request
			$this->_db->update(PREFIX."usr"
								,array('user_last_login_timestamp' => $user_last_login_timestamp)
								,array('user_id' => $query->user_id));
			// If user has checked the "remember me" check-box, then write cookie
			if(!empty($p_user_rememberme) AND $p_user_rememberme == 1){
				// add cookies table parameters to query
				$query = $this->_db->selectOne("SELECT user_remember,user_cookie_id 
										FROM ".PREFIX."usr 
										WHERE user_id = :user_id",
										array(':user_id' => $query->user_id));
				// get users actual and real browser + current salt
				$current_xcst = hash('sha1',SALT_TOKEN.$_SERVER['HTTP_USER_AGENT']);
				// get all user combinations for tokens and their creation time
				$runner = explode(',',$query->user_remember);
				// current time for deletion of expired cookies
				$cookie_time = $user_last_login_timestamp + COOKIE_EXPIRES;
				// updated triplet for DB to clean from string the COOKIE_EXPIRES and push to head currently new remember me
				$new_user_remembers = $cookie_time.'|'.$current_xcst.',';
				// extend another two weeks for cookie (not checking for successful cookie update)
				Cookie::set_cookie('rmbm[xcsc]',$cookie_time);
				// add users token
				Cookie::set_cookie('rmbm[xcst]',$current_xcst);
				// add users cookie unique identification that was generated once user was approved
				Cookie::set_cookie('rmbm[xcsi]',$query->user_cookie_id);
				// run on array of xcst and xcsc
				// NOTE that we ignoring the check for already saved agent like our because i assume that it's outdated
				foreach($runner as $combinations){
					// get at comb[0]->xcst and at comb[1]->xcsc
					$comb = explode('|',$combinations);
					// check if cookie is still relevant if not 
					// ignore it we don't need to hold expired cookies nor duplicater xcst's
					if($comb[0] >= $user_last_login_timestamp AND $comb[1] != $current_xcst){
						// OK,add this for new user remember reconstruct
						$new_user_remembers .= $comb[0].'|'.$comb[1].',';
					}
				}
				// check for string allowed size before pushing to DB for max of 8 remember me so chop the string 
				// * Browsers allowed | xcsc xcst * 8 and , * 7 = total of 463 :)
				$final_remembers = strlen($new_user_remembers);
				// chop ',' at the end of string
				if($final_remembers >= 10)
					$new_user_remembers = substr($new_user_remembers,0,$final_remembers-1);
				// update DB with new remember me string, we not interesting in success or failure ???
				$dsfdf = $query->user_cookie_id;
				$ddd =  $this->_db->update(PREFIX."usr",
									array('user_remember' => $new_user_remembers),
									array('user_cookie_id' => $query->user_cookie_id));
			}
			// no more is need to check login with cookie, user will not re-login
			Session::set("user_logged_in_cooked",true);
			// return true to make clear the login was successful
			return true;
		}else{
			// increment the failed login counter for that user
			// the update is done only by email!!!
			$query = $this->_db->update(PREFIX."usr"
										,array('user_failed_logins' => $query->user_failed_logins+1,'user_last_failed_login' => time())
										,array('user_email' => $query->user_email));
			Session::set("feedback_negative", 'LOGIN_FAILED');
			return false;
		}
		/* default return */
		return false;
	}

	/**
	* Log out process, deletes cookie, deletes session nothing to return
	**/
	public function set_Logout(){
		/* will set the remember-me-cookie to six minutes from last login if such was used */
		Cookie::del_cookie('rmbm[xcst]');
		// delete the session
		Session::destroy();
	}

	/**
	* Handles the entire registration process for DEFAULT, creates a new user in the database if everything is fine
	* Not for people who register with service like Facebook
	* @return ARRAY/BOOL success/failure status of the registration
	*/
	public function set_RegisterNewUser($p_user_name,$p_user_gender,$p_user_birth,$p_user_email,$p_user_password){
		/* Just checking that received all varietals .. */
		if(empty($p_user_gender) OR empty($p_user_birth) OR empty($p_user_email) OR empty($p_user_password) OR empty($p_user_name)){
			echo 'ERROR_UNKNOWN';
			return false;
		}else{
			// check if email already exists
			$query = $this->_db->selectOne("SELECT user_id FROM ".PREFIX."usr WHERE user_email = :user_email",array(':user_email' => $p_user_email));
			// email exists ?! return false! may be send to user email notification....?
			if($query){
				echo 'EMAIL_TAKEN';
				return false;
			}
			// generate random hash for email verification (40 char string)
			$user_activation_hash = sha1(uniqid(mt_rand(), true));
			// generate simple 20 chars long Hash using SHA1 from users birth day and creation time stamp for use as "salt" in login check
			$user_creation_hash_salt = substr(hash('sha1',$p_user_birth.time()),-20);
			// generate simple 40 chars long Hash for mobile UID using SHA1 from users email and creation hash
			$user_mobile_uid = hash('sha1',$p_user_email.$user_creation_hash_salt);
			// crypt the user's password using Blowfish as some salt the pass 60 chars long
			$user_password_hash = crypt($p_user_password, BLOWFISH_HASH_COST.$user_creation_hash_salt);
			// write new user into data into database
			//error_log(' new user with '.$p_user_email);
			$query = $this->_db->insert(PREFIX."usr"
										,array('user_gender' => $p_user_gender,
											'user_password_hash' => $user_password_hash,
											'user_email' => $p_user_email,
											'user_name' => $p_user_name,
											'user_creation_hash_salt' => $user_creation_hash_salt,
											'user_mobile_uid' => $user_mobile_uid,
											'user_activation_hash' => $user_activation_hash,
											'user_birth' => $p_user_birth,
											'user_provider_type' => 'DEFAULT'));
			// check if user successfully created in DB
			if($query != 1){
				return false;
			}
			// get unique user_id of the user that has been created
			$query = $this->_db->selectOne("SELECT * 
											FROM ".PREFIX."usr 
											WHERE user_email = :user_email"
											,array(':user_email' => $p_user_email));
			// unable to get the newly created user...?!
			//error_log(' new id '.$query->user_id);
			if(!$query){
				return false;
			}
			// this is finale return upon all procedures success
			return $query;
		}
		// default return, returns only true of really successful (see above)
		return false;
	}

	/**
	* Checks the email/verification code combination and set the user's activation status to true in the database
	* copies the activated usr to usr_i (information) table as existing and activated account type
	* @param INT $user_id user id
	* @param STRING $user_activation_verification_code verification token
	* @return BOOL success status
	*/
	public function get_NewUserVerify($user_id, $user_activation_verification_code){
		// create cookie login entry option in DB usr.cookies for this user
		// NOTE : the user cookie id is generated here one but not passed to user until he selects remember me
		$cookie_id = hash('sha1',substr($user_id,-4).mt_rand().USER_TOKEN);
		$query_act = $this->_db->update(PREFIX."usr",
									array('user_activation_hash' => NULL,
											'user_active' => 1,
											'user_cookie_id' => substr($user_id,0,4).$cookie_id),
									array('user_id' => $user_id,
											'user_activation_hash' => $user_activation_verification_code));
		if($query_act == 1){
			// get newly activated user basic information and insert to usr_i table
			$query_select = $this->_db->selectOne("SELECT user_gender,user_birth,user_name 
											FROM ".PREFIX."usr 
											WHERE user_id = :user_id"
											,array(':user_id' => $user_id));
			// unable to get the newly created user...?!
			if(!$query_select){
				return false;
			}
			// insert/create instance of activated user in usr_i table
			// OLD WAY : 'user_name' => Language::getValue('reg_user_name_ph'),
			$query = $this->_db->insert(PREFIX."usr_i"
										,array('user_id' => $user_id,
												'user_name' => $query_select->user_name,
												'user_gender' => $query_select->user_gender,
												'user_birth' => $query_select->user_birth,
												'user_active' => 1));
			// check if user successfully created and added to information table
			if(!$query){
				return false;
			}
			return true;
		}else{
			return false;
		}
	}

	/**
	* Set the new password (for DEFAULT user, FACEBOOK-users don't have a password)
	* @param INT user id,STRING MAIL, STRING pass old and new 
	* @return BOOL success state of the password reset, and session negative feedback
	*/
	public function set_PasswordChange($userId,$userMail,$passOld,$passNew){
		// basic checks for emptiness... :)
		if(empty($userId) OR empty($userMail)){
			Session::set("feedback_negative", 'ERROR_UNKNOWN');
			return false;
		}
		if(empty($passOld) OR empty($passNew)){
			Session::set("feedback_negative", 'PASSWORD_SHORT');
			return false;
		}
		// Get password and creation hash for user
		$query = $this->_db->selectOne("SELECT user_creation_hash_salt,user_password_hash  
										FROM ".PREFIX."usr 
										WHERE user_id = :user_id 
										AND user_email = :user_email",
										array(':user_id' => $userId,
											':user_email' => $userMail));
		// Check if user id and email combination for this session is correctly exists...
		if($query){
			// check if hash of provided password matches the hash in the database
			// crypt(passOld, BLOWFISH_HASH_COST.user_creation_hash_salt)
			// crypt using Blowfish only 04 iterations the salt those the magic
			//error_log(crypt($passOld, BLOWFISH_HASH_COST.$query->user_creation_hash_salt).'        '.$query->user_password_hash);
			if(crypt($passOld, BLOWFISH_HASH_COST.$query->user_creation_hash_salt) == $query->user_password_hash){
				// crypt the new password
				$p_change_pass_new = crypt($passNew, BLOWFISH_HASH_COST.$query->user_creation_hash_salt);
				// try to set a new password for user
				$query_u = $this->_db->update(PREFIX."usr",
										array('user_password_hash' => $p_change_pass_new),
										array('user_id' => $userId,'user_email' => $userMail));
				if(sizeof($query_u) == 1)
					return true;
				else
					Session::set("feedback_negative", 'ERROR_RETRY');
				return false;
			}else{
				Session::set("feedback_negative", 'PASSWORD_WRONG');
				return false;
			}
		}
		Session::set("feedback_negative", 'ERROR_RETRY');
		return false;
	}


	/**
	* Setting in database password reset flags and time for given email and returning the array with user that requested
	* Don't forget to perform the necessary actions to send a password reset mail
	* @param STRING MAIL 
	* @return ARRAY on success BOOL on false
	*/
	public function set_PasswordForgot($userEmail){
		// simple check for email input
		if(empty($userEmail) OR strlen($userEmail) <= 6){
			return false;
		}
		// generate the MySQL DATETIME format (to see when exactly the user (or an attacker) requested the password reset mail)
		// i prefer the use of NOW() but fucking PDO will mess it... :(
		$temp_timestamp = date("Y-m-d H:i:s");
		// generate simple 40 chars long Hash using SHA1 from users email and current time stamp for use as "salt" in login check
		$user_password_reset_hash = hash('sha1',$userEmail.$temp_timestamp.SALT_TOKEN);
		$query_u = $this->_db->update(PREFIX."usr",
									array('user_password_reset_timestamp' => $temp_timestamp,
											'user_password_reset_hash' => $user_password_reset_hash),
									array('user_email' => $userEmail));
		// Check for successful result otherwise no such email exists
		// and return the generated hash and user id if composing email is needed
		if($query_u){
			$query_s = $this->_db->selectOne("SELECT user_id,user_password_reset_hash
												FROM ".PREFIX."usr
												WHERE user_email = :user_email
													AND user_provider_type = :provider_type"
													,array(':user_email' => $userEmail
													, ':provider_type' => 'DEFAULT'));
			if($query_s)
				return $query_s;
		}
		return false;
	}

	/**
	* Verifies the password reset request via the verification hash token (that's only valid for two hours)
	* @param STRING $user_id 
	* @param STRING $userHash Hash token
	* @return ARRAY on success with users hash or BOOL on false
	*/
	public function get_VerifyPasswordReset($user_id,$userHash){
		// check if user-provided userid + verification code combination exists
		$query = $this->_db->selectOne("SELECT user_password_reset_timestamp,user_creation_hash_salt
									FROM ".PREFIX."usr 
									WHERE user_id = :user_id AND user_password_reset_hash = :user_password_reset_hash",
									array(':user_id' => $user_id,
										':user_password_reset_hash' => $userHash));
		if(sizeof($query) == 0){
			return false;
		}
		/* 3600 seconds are 1 hour */
		$hourAgo = time() - 7200;
		/* if password reset request was sent within the last 2 hours (this timeout is for security reasons) */
		if(time($query->user_password_reset_timestamp) >= $hourAgo){
			/* verification was successful will return query */
			return $query;
		}else{
			/* clean such combination no matter what but leave the time stamp.. */
			$this->_db->update(PREFIX."usr",
							array('user_password_reset_hash' => NULL),
							array('user_id' => $user_id));
			return false;
		}
	}

	/**
	* Verifies the password reset request via the verification hash token (that's only valid for two hours)
	* @param INT users id 
	* @param STRING user new password already encrypted
	* @return BOOL success/failure status
	*/
	public function get_PasswordReset($userId,$userPass){
		if(empty($userPass) OR empty($userId)){
			return false;
		}
		$query_u =  $this->_db->update(PREFIX."usr",
							array('user_password_hash' => $userPass,
								'user_password_reset_hash' => NULL),
							array('user_id' => $userId));
		if($query_u)
			return true;
		return false;
	}

}