<?php
/**
 * Provides the user class, an interface to the user table in the database
 *
 * @author Camil Staps
 *
 * BusinessAdmin: administrative software for small companies
 * Copyright (C) 2015 Camil Staps (ViviSoft)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * An interface to the user table in the database
 */
class user {
	/**
	 * @var pdo $pdo                    The PDO class for database communication
	 * @var int $id                     The id of the user
	 * @var string $username            The username of the user
	 * @var string $password            The (hashed) password of the user
	 */
	protected $pdo, $id, $username, $password;

	/**
	 * Generate a random password
	 *
	 * @return string The password
	 */
	public static function generateRandomPassword() {
		return preg_replace('/[^\w]/', '',
			base64_encode(bin2hex(openssl_random_pseudo_bytes(4))));
	}

	/**
	 * Hash a password
	 *
	 * @param string $password          The password to be hashed
	 * @param int $cost                 The password cost
	 *
	 * @return string The hashed password
	 */
	public static function hash($password, $cost=null) {
		return password_hash(
			$password,
			constants::password_algo,
			['cost' => is_null($cost) ? constants::password_cost : $cost]
		);
	}

	/**
	 * Create a new instance
	 *
	 * @param PDO $pdo                  The PDO class, to access the database
	 * @param int $id                   The id of the user to fetch
	 *
	 * @throws PDOException             If something went wrong with the database
	 * @throws Exception                If the user could not be found
	 */
	public function __construct($pdo, $id) {
		$this->pdo = $pdo;

		$stmt = $this->pdo->prepare("SELECT * FROM `".constants::db_prefix."user` WHERE `id`=?");
		$stmt->execute(array($id));
		if ($stmt->rowCount() == 0) {
			throw new Exception("The user with id '$id' could not be found.");
		}
		$user = $stmt->fetch(PDO::FETCH_ASSOC);

		$this->id = $user['id'];
		$this->username = $user['username'];
		$this->password = $user['password'];
	}

	//------------------------------------------------------------------------------
	// Getters and setters
	//------------------------------------------------------------------------------

	/**
	 * Get the ID of the user
	 *
	 * @return int                      The ID
	 */
	public function getId() {
		return $this->id;
	}

	/**
	 * Get the username of the user
	 *
	 * @return string The username
	 */
	public function getUsername() {
		return $this->username;
	}

	/**
	 * Set the username of the user
	 *
	 * @param string $username          The new username for the user
	 *
	 * @throws PDOException             If something went wrong with the database
	 *
	 * @return bool                     True on succes, false on failure
	 */
	public function setName($username) {
		$stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."user` SET `username`=? WHERE `id`=?");
		$stmt->execute(array($username, $this->id));
		if ($stmt->rowCount() == 1) {
			$this->username = $username;
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Set the password of the user
	 *
	 * @param string $password          The new password for the user
	 *
	 * @throws PDOException             If something went wrong with the database
	 *
	 * @return bool                     True on succes, false on failure
	 */
	public function setPassword($password) {
		$password = self::hash($password);
		$stmt = $this->pdo->prepare("UPDATE `".constants::db_prefix."user` SET `password`=? WHERE `id`=?");
		$stmt->execute(array($password, $this->id));
		if ($stmt->rowCount() == 1) {
			$this->password = $password;
			return true;
		} else {
			return false;
		}
	}

	//------------------------------------------------------------------------------
	// Other functions
	//------------------------------------------------------------------------------

	/**
	 * Check if a user has administrator rights
	 *
	 * @return bool                     True iff the user has administrator rights
	 */
	public function isAdmin() {
		return in_array($this->getId(), constants::user_admins);
	}

	/**
	 * Verify a password
	 *
	 * @param string $password          The password to verify
	 *
	 * @return bool                     True iff the password can be accepted
	 */
	public function verifyPassword($password) {
		if (!password_verify($password, $this->password)) {
			return false;
		}
		if (password_needs_rehash($this->password, constants::password_algo,
				['cost' => constants::password_cost])) {
			$this->setPassword($password);
		}
		return true;
	}

	/**
	 * Remove this user from the database
	 *
	 * If this doesn't succeed (i.e. false is returned), that means the user was removed manually or by another instance of this class
	 *
	 * @throws PDOException             If something went wrong with the database
	 *
	 * @return bool                     True on success, false on failure
	 */
	public function delete() {
		$stmt = $this->pdo->prepare("DELETE FROM `".constants::db_prefix."user` WHERE `id`=?");
		$stmt->execute(array($this->id));
		if ($stmt->rowCount() != 1) {
			return false;
		} else {
			return true;
		}
	}
}