<?php

  /**
   * Authentication level users manager
   *
   * @package angie.frameworks.authentication
   * @subpackage models
   */
  abstract class FwUsers extends BaseUsers {
    
    /**
     * Returns true if $user can create a new user account
     *
     * @param IUser $user
     * @return boolean
     */
    static function canAdd(IUser $user) {
      return $user instanceof User && $user->isAdministrator();
    } // canAdd
    
    // ---------------------------------------------------
    //  Finders
    // ---------------------------------------------------
    
    /**
     * Return ID-s of user accounts $user can see
     *
     * @param User $user
     * @param Company $company
     * @return array
     */
    static function findVisibleUserIds(IUser $user) {
      if($user instanceof User) {
        return DB::executeFirstColumn('SELECT id FROM ' . TABLE_PREFIX . 'users WHERE id != ?', $user->getId());
      } else {
        return null;
      } // if
    } // findVisibleUserIds
    
    /**
     * Return user display name by user ID
     * 
     * @param integer $id
     * @param boolean $short
     * @return string
     */
    static function getUserDisplayNameById($id, $short = false) {
      $user = DB::executeFirstRow('SELECT first_name, last_name, email FROM ' . TABLE_PREFIX . 'users WHERE id = ?', $id);
      
      if($user) {
        return Users::getUserDisplayName($user, $short);
      } else {
        return null;
      } // if
    } // getUserDisplayNameById
    
    /**
     * Return display name of user based on given parameters
     * 
     * @param array $params
     * @param boolean $short
     * @return string
     */
    static function getUserDisplayName($params, $short = false) {
      $full_name = isset($params['full_name']) && $params['full_name'] ? $params['full_name'] : null;
      $first_name = isset($params['first_name']) && $params['first_name'] ? $params['first_name'] : null;
      $last_name = isset($params['last_name']) && $params['last_name'] ? $params['last_name'] : null;
      $email = isset($params['email']) && $params['email'] ? $params['email'] : null;
      
  		if ($short) {
  		  if($full_name) {
  		    $parts = explode(' ', $full_name);
  		    
  		    if(count($params) > 1) {
  		      $first_name = array_shift($parts);
  		      $last_name = implode(' ', $parts);
  		    } else {
  		      $first_name = $full_name;
  		    } // if
  		  } // if
  		  
  			if($first_name && $last_name) {
  				return $first_name . ' ' . substr_utf($last_name, 0, 1) . '.';
  			} elseif($first_name) {
  				return $first_name;
  			} elseif($last_name) {
  				return $last_name;
  			} else {
  				return substr($email, 0, strpos($email, '@'));
  			} // if
  		} else {
  		  if($full_name) {
  		    return $full_name;
  		  } elseif($first_name && $last_name) {
  				return $first_name . ' ' . $last_name;
  			} elseif($first_name) {
  				return $first_name;
  			} elseif($last_name) {
  				return $last_name;
  			} else {
  				return substr($email, 0, strpos($email, '@'));
  			} // if
  		} // if
    } // getUserDisplayName
    
    /**
     * Return user ID name map
     *
     * @param array $ids
     * @param boolean $short
     * @return array
     */
    static function getIdNameMap($ids = null, $short = false) {
      if($ids) {
        $rows = DB::execute("SELECT id, first_name, last_name, email FROM " . TABLE_PREFIX . "users WHERE id IN (?) ORDER BY CONCAT(first_name, last_name, email)", $ids);
      } else {
        $rows = DB::execute("SELECT id, first_name, last_name, email FROM " . TABLE_PREFIX . "users ORDER BY CONCAT(first_name, last_name, email)");
      } // if
      
      if($rows) {
        $result = array();
        
        foreach($rows as $row) {
          $result[(integer) $row['id']] = Users::getUserDisplayName($row, $short);
        } // foreach
        
        return $result;
      } else {
        return array();
      } // if
    } // getIdNameMap
    
    /**
     * Return ID Details map
     * 
     * @param array $ids
     * @param array $fields
     * @return rray
     */
    static function getIdDetailsMap($ids = null, $fields) {
    	$fields = (array) $fields;
    	$fields[] = 'id';
    	
      if($ids) {
        $rows = DB::execute("SELECT " . implode(', ', $fields) . " FROM " . TABLE_PREFIX . "users WHERE id IN (?) ORDER BY id", $ids);
      } else {
        $rows = DB::execute("SELECT " . implode(', ', $fields) . " FROM " . TABLE_PREFIX . "users ORDER BY id");
      } // if
      
      if (!is_foreachable($rows)) {
      	return null;
      } // if
      
      $result = array();
      foreach ($rows as $row) {
      	$single_result = array();
      	
      	foreach ($fields as $field) {
      			$single_result[$field] = $row[$field];
      	} // foreach
      	
				$result[(integer) $row['id']] = $single_result;
      } // foreach

      return $result;
    } // getIdDetailsMap
    
    /**
     * Return users by ID-s
     *
     * @param array $ids
     * @return DBResult
     */
    static function findByIds($ids) {
      return Users::find(array(
        'conditions' => array('id IN (?)', $ids),
        'order' => 'CONCAT(first_name, last_name, email)',
      ));
    } // findByIds
  
    /**
     * Return user by email address
     *
     * @param string $email
     * @return User
     */
    static function findByEmail($email) {
      return $email ? Users::find(array(
        'conditions' => array('email = ? AND state >= ?', $email, STATE_TRASHED),
        'one' => true,
      )) : null;
    } // findByEmail
    
    /**
     * Get user by session ID
     *
     * @param string $session_id
     * @param string $session_key
     * @return User
     */
    static function findBySessionId($session_id, $session_key) {
      $users_table = TABLE_PREFIX . 'users';
      $user_sessions_table = TABLE_PREFIX . 'user_sessions';
      
      return Users::findBySQL("SELECT $users_table.* FROM $users_table, $user_sessions_table WHERE $users_table.id = $user_sessions_table.user_id AND $user_sessions_table.id = ? AND $user_sessions_table.session_key = ?", array($session_id, $session_key), true);
    } // findBySessionId
    
    /**
     * Return users by role
     *
     * @param Role $role
     * @param integer $min_state
     * @return DBResult
     */
    static function findByRole(Role $role, $min_state = STATE_VISIBLE) {
      return Users::find(array(
        'conditions' => array('role_id = ? AND state >= ?', $role->getId(), $min_state),
        'order' => 'CONCAT(first_name, last_name, email)',
      ));
    } // findByRole
    
    /**
     * Return number of users with a given role
     *
     * @param Role $role
     * @return integer
     */
    static function countByRole(Role $role, $min_state = STATE_VISIBLE) {
      return Users::count(array(
        'role_id = ? AND state >= ?', $role->getId(), STATE_ARCHIVED
      ));
    } // countByRole
    
    /**
     * Return all administrators in system
     * 
     * @param User $exclude_user
     * @return array
     */
    static function findAdministrators($exclude_user = null) {
      $administrators = array();
      
      foreach (Roles::find() as $system_role) {
        if ($system_role->isAdministrator() && is_foreachable($system_role->getUsers())) {
          foreach ($system_role->getUsers() as $admin) {
            if ($exclude_user instanceof User && $admin->is($exclude_user)) {
              continue;
            } // if
             
            $administrators[] = $admin;
          } // foreach
        } // if
      } // foreach
      
      return $administrators;
    } // findAdministrators
    
    /**
     * Return number of administrators
     *
     * @return integer
     */
    static function countAdministrators() {
      $administrators_count = 0;
      
      foreach(Roles::find() as $system_role) {
        if($system_role->isAdministrator()) {
          $administrators_count += $system_role->countUsers();
        } // if
      } // foreach
      
      return $administrators_count;
    } // countAdministrators
    
    /**
     * Prepare user for import
     *
     * @param array $vcard
     * @return array
     */
    static function prepareUser($vcard) {
    	// user data initialization
  		$first_name = $last_name = $email = '';
  		
  		if(array_key_exists('N', $vcard)) {
  			$first_name = trim($vcard['N'][0]['value'][1][0]);
  			$last_name = trim($vcard['N'][0]['value'][0][0]);
  		} // if

  		$email = trim($vcard['EMAIL'][0]['value'][0][0]);
  		
  		if(array_key_exists('REV', $vcard)) {
  			$updated_on = DateTimeValue::makeFromString($vcard['REV'][0]['value'][0][0]);
  		} // if
  		
  		return $user = array(
  			'object_type' => 'User',
  			'first_name' => $first_name,
  			'last_name' => $last_name,
  			'email' => $email,
  			'updated_on' => $updated_on
  		);
    } // prepareUser
    
    /**
     * Import user from vCard
     * 
     * @param array $user_data
     * @param array $imported_users
     * @return User
     */
    static function fromVCard($user_data, &$imported_users) {
    	try {
    		// check whether it's a new user
    		$is_new = array_var($user_data, 'is_new') == 'true';

    		// create an instance of User class appropriately
    		if($is_new) {
    			$user = new User();
    		} else {
    			$user = Users::findByEmail(array_var($user_data, 'old_email'));
    		} // if
    		
        $user->setFirstName(array_var($user_data, 'first_name'));
        $user->setLastName(array_var($user_data, 'last_name'));
        $user->setEmail(array_var($user_data, 'email'));
        
        if($is_new) {
        	$password = Authentication::getPasswordPolicy()->generatePassword();
        	
	    		$user->setRoleId(Roles::getDefault()->getId());
	    		$user->setPassword($password);
	    		$user->setState(STATE_VISIBLE);

	    		if(array_var($user_data, 'updated_on')) {
	    			$user->setUpdatedOn(DateTimeValue::makeFromString(array_var($user_data, 'updated_on')));
	    			$user->setUpdatedBy(Authentication::getLoggedUser());
	    		} // if
    		} // if
    		
    		// Collect imported users for updating object list
    		$imported_users[] = array(
    			'is_new' => $is_new,
    			'user' => $user,
    			'password' => $password
    		);
        
      	return $user;
      } catch(Exception $e) {
        throw $e;
      } // try
    } // fromVCard
    
    /**
     * Return users who were online in the past $minutes minutes
     *
     * @param User $user
     * @param integer $minutes
     * @return array
     */
    static function findWhoIsOnline($user, $minutes = 15) {
      $visible_user_ids = Users::findVisibleUserIds($user);
      if(is_foreachable($visible_user_ids)) {
        $users_table = TABLE_PREFIX . 'users';
        $reference = new DateTimeValue("-$minutes minutes");
        
        return Users::findBySQL("SELECT * FROM $users_table WHERE id IN (?) AND last_activity_on > ? ORDER BY CONCAT(first_name, last_name, email)", array($visible_user_ids, $reference));
      } // if
      return null;
    } // findWhoIsOnline
    
    /**
     * Return number of users who were online in the past $minutes minutes
     *
     * @param User $user
     * @param integer $minutes
     * @return array
     */
    static function countWhoIsOnline($user, $minutes = 15) {
      $visible_user_ids = Users::findVisibleUserIds($user);
      if(is_foreachable($visible_user_ids)) {
        $users_table = TABLE_PREFIX . 'users';
        $reference = new DateTimeValue("-$minutes minutes");
        
        return (integer) DB::executeFirstCell("SELECT COUNT(id) AS 'row_count' FROM $users_table WHERE id IN (?) AND last_activity_on > ?", $visible_user_ids, $reference);
      } // if
      return 0;
    } // countWhoIsOnline

    // ---------------------------------------------------
    //  Utility Methods
    // ---------------------------------------------------
    
    /**
     * Return list of users who can see private object
     * 
     * @param array $ids
     * @return array
     */
    static function whoCanSeePrivate($ids = null) {
    	$role_ids = Roles::whoCanSeePrivateIds();
    	
    	if (!is_foreachable($role_ids)) {
    		return false;
    	} // if
    	
    	if (is_foreachable($ids) && count($ids) > 0) {
    		return DB::executeFirstColumn('SELECT id FROM ' . TABLE_PREFIX . 'users WHERE role_id IN (?) AND id IN (?)', $role_ids, $ids);
    	} else {
    		return DB::executeFirstColumn('SELECT id FROM ' . TABLE_PREFIX . 'users WHERE role_id IN (?)', $role_ids);
    	} // if
    } // whoCanSeePrivate

    /**
     * Returns true if $address is used by any trashed, archived or visible user
     *
     * @param string $address
     * @param mixed $exclude_user
     * @return boolean
     */
    static function isEmailAddressInUse($address, $exclude_user = null) {
      if($exclude_user) {
        $exclude_user_id = $exclude_user instanceof User ? $exclude_user->getId() : $exclude_user;

        return (boolean) DB::executeFirstCell('SELECT COUNT(id) FROM ' . TABLE_PREFIX . 'users WHERE email = ? AND state > ? AND id != ?', $address, STATE_DELETED, $exclude_user_id);
      } else {
        return (boolean) DB::executeFirstCell('SELECT COUNT(id) FROM ' . TABLE_PREFIX . 'users WHERE email = ? AND state > ?', $address, STATE_DELETED);
      } // if
    } // isEmailAddressInUse

    /**
     * Update account when auto expiry is updated
     *
     * @param integer $value
     */
    static function autoExpiryUpdated($value) {
      if($value > 0) {
        DB::execute('UPDATE ' . TABLE_PREFIX . 'users SET password_expires_on = ? WHERE password_expires_on IS NULL', DateTimeValue::makeFromString("+$value months"));
      } else {
        DB::execute('UPDATE ' . TABLE_PREFIX . 'users SET password_expires_on = NULL');
      } // if
    } // autoExpirySet

    /**
     * Mass-mark user passwords as expired
     *
     * @param mixed $exclude
     */
    static function expirePasswords($exclude = null) {
      // password expired yesterday
      $yesterday = date("Y-m-d", time() - 24 * 60 * 60);

      if($exclude) {
        DB::execute('UPDATE ' . TABLE_PREFIX . 'users SET password_expires_on = ? WHERE id NOT IN (?)', $yesterday, $exclude);
      } else {
        DB::execute('UPDATE ' . TABLE_PREFIX . 'users SET password_expires_on = ?', $yesterday);
      } // if
    } // expirePasswords

  }