<?php
require_once('Login.class.php');
require_once('Auth.class.php');

Router::register('users/new'  , 'Users', 'new_user');
Router::register('users/index', 'Users', 'index_file');
Router::register('users'      , 'Users', 'index_dir');
Router::register('users/*'    , 'Users', 'individual');

class Users extends Controller {
	public static $illegal_names = array('', 'new', 'index');

	// Index Views ///////////////////////////////////////////////
		
	public function index($routed, $remainder) {
		return $this->index_dir($routed, $remainder);
	}
	
	/**
	 * Handle POSTing a new user, or GETing the index.
	 */
	public function index_dir($routed, $remainder) {
		$method = $_SERVER['REQUEST_METHOD'];
		switch ($method) {
		case 'POST':
			// We're POSTing a new user.
			$this->create_user();
			break;
		case 'HEAD': // fall-through to GET
		case 'GET':
			// We're GETing the index.
			$this->show_index($routed, $remainder);
			break;
		}
	}
	
	/**
	 * Handle PUTing an updated user index, or GETing the index.
	 */
	public function index_file($routed, $remainder) {
		$method = $_SERVER['REQUEST_METHOD'];
		switch ($method) {
		case 'PUT': $_POST = $_PUT;
		case 'POST':
			// We're PUTing an updated user index.
			$this->update_users();
			break;
		}
		$this->show_index($routed, $remainder);
	}

	// Other Views ///////////////////////////////////////////////

	/**
	 * Handle GETing the new user form.
	 * 
	 * I would have named this `new', but that's a keyword.
	 */
	public function new_user($routed, $vars) {
		// since there will never be a remainder to `users/new', we can
		// use that parameter to pass in some data.
		if (!isset($vars['errors'])) $vars['errors'] = array();
		$this->showView('users/new', $vars);
	}
	
	public function individual($routed, $remainder) {
		$username = implode('/', $remainder);
		
		global $mm; // also used for pluginmanager
		$db = $mm->database();
		$uid = $db->getUID($username);
		$user = Auth::getObj($uid);
		
		if ($user->isGroup()) $uid = false; // ignore groups.
		
		if ($uid===false) {
			$this->http404($routed, $remainder);
		} else {
			if (!$user->canRead()) {
				$this->http401($routed, $remainder);
				exit();
			}
			
			$vars = array();
			$method = $_SERVER['REQUEST_METHOD'];
			
			switch ($method) {
			case 'PUT': $_POST = $_PUT;
			case 'POST':
				// We're PUTing updated user info.
				if ($user->canEdit()) {
					$vars = $this->update_user($user);
				}
				break;
			}
			
			$config_options = array();
			$mm->pluginManager()->callHook('userConfig', &$config_options);
			
			$vars['config_options'] = $config_options;
			$vars['user'] = $user;
			$vars['groups'] = $db->listGroupNames();
			require_once('ContactMethod.class.php');
			$this->showView('users/individual', $vars);
		}
	}
	
	public function http404($routed, $rnemainder) {
		$username = implode('/', $remainder);
		$this->showView('users/404',
		                array('username'=>$username));
	}
	
	public function http401($routed, $remainder) {
		$this->showView('users/401', array('uid'=>Login::isLoggedIn()));
	}
	
	// Other Functions ///////////////////////////////////////////
	
	/**
	 * This will parse POST data to create a new user.
	 * If successfull it will show a message saying so.
	 * If not successfull, it will re-show the new-user form with errors
	 * explained.
	 */
	private function create_user() {
		$vars = array();
		@$vars['username' ] = $_POST['auth_name'];
		@$vars['password1'] = $_POST['auth_password'       ];
		@$vars['password2'] = $_POST['auth_password_verify'];
		
		global $mm; $db = $mm->database();
		
		$vars['errors'] = array();
		if ($db->getUID($vars['username'])!==false)
			$vars['errors'][] = 'user exists';
		if (in_array($vars['username'], self::$illegal_names))
			$vars['errors'][] = 'illegal name';
		$matches = ($vars['password1'] == $vars['password2']);
		if (!$matches) {
			$vars['errors'][] = 'pw mixmatch';
		}
		if ($matches && $vars['password2'] == '') {
			$vars['errors'][] = 'no pw';
		}
		
		if (count($vars['errors']) > 0) {
			$this->new_user($routed, $vars);
		} else {
			$username = $vars['username'];
			$password = $vars['password1'];
			$uid = $db->addUser($username, $password);
			if ($uid===false) {
				$this->showView('users/500');
			} else {
				Login::login($username, $password);
				$this->showView('users/created',
				                array('username'=>$username));
			}
		}
	}

	/**
	 * This will parse POST (really, PUT) data to update a single user
	 */
	private function update_user($user) {
		$vars = array();
		
		$username = $user->getName();
		// Change the username /////////////////////////////////////////
		if (isset($_POST['auth_name'])) {
			$new_name = $_POST['auth_name'];
			if ($new_name != $username) {
				if (!in_array($new_name, $this->illegal_names)) {
					$changed_name = $user->setName($new_name);
					$username = $user->getName();
					$vars['changed name'] = $changed_name;
				}
			}
		}
		
		// Change the password /////////////////////////////////////////
		@$password1 = $_POST['auth_password'          ];
		@$password2 = $_POST['auth_password'.'_verify'];
		
		// Check the verify box, not main box, so that we don't get
		// tripped by browsers annoyingly autocompleting the password.
		$is_set = ($password2 != '');
		
		if ($is_set) {
			$matches = ( $password1 == $password2 );
			if ($matches) {
				$user->setPassword($password1);
				$vars['pw updated'] = true;
			} else {
				$vars['pw mixmatch'] = true;
			}
		}
		
		// Change information //////////////////////////////////////////
		$config_options = array();
		global $mm;
		$mm->pluginManager()->callHook('userConfig', &$config_options);
		
		foreach ($config_options as $group=>$options) {
			foreach ($options as $option) {
				$this->confText($user, $option[0]);
			}
		}
		
		// Change contact info /////////////////////////////////////////
		global $CONTACT_METHODS;
		foreach ($CONTACT_METHODS as $method) {
			$this->confText($user, $method->addr_slug);
		}
		$this->confArray($user, 'use');
		
		// Change groups ///////////////////////////////////////////////
		$this->confArray($user, 'groups');

		return $vars;
	}

	private function confArray($user, $key) {
		if (isset($_POST[$key]) && is_array($_POST[$key])) {
			$user->setConfArray($key, $_POST[$key]);
		}
	}

	private function confText($user, $name) {
		if (isset($_POST["user_$name"])) {
			$user->setConf($name, $_POST["user_$name"]);
		}
	}


	/**
	 * This will parse POST (really, PUT) data to update multiple users.
	 */
	private function update_users() {
		$attribs = $this->getIndexAttribs();
		foreach ($attribs as $attrib) {
			$key = $attrib['key'];
			if (isset($_POST[$key]) && is_array($_POST[$key])) {
				foreach ($_POST[$key] as $uid => $value) {
					$this->setConf($uid, $key, $value);
				}
			}
		}
	}
	
	/**
	 * This will show the user index.
	 */
	private function show_index($routed, $remainder) {
		global $mm; $db = $mm->database();
		
		$logged_in_user = Auth::getObj(Login::isLoggedIn());
		if (!$logged_in_user->isUser()) {
			$this->http401($routed, $remainder);
			exit();
		}
		
		$vars = array();
		$vars['attribs'] = $this->getIndexAttribs();
		$vars['users'] = array();
		$uids = $db->listUsers();
		foreach ($uids as $uid) {
			$user = Auth::getObj($uid);
			$vars['users'][$uid] = array();
			foreach ($vars['attribs'] as $attrib) {
				$key = $attrib['key'];
				$props = $this->getConf($user, $key);
				$vars['users'][$uid][$key] = $props;
			}
		}
		$this->showView('users/index', $vars);
	}
	
	private function getConf($user, $key) {
		$logged_in_user = Auth::getObj(Login::isLoggedIn());
		$uid = $user->getUID();
		$post_key = $key."[$uid]";
		@$value = $_POST[$post_key];
		$editable = $user->canEdit();
		
		switch ($key) {
		case 'auth_name':
			$value = $user->getName();
			break;
		case 'auth_user':
			$editable = $editable && $logged_in_user->isAdmin();
			$value = $user->isUser();
			break;
		case 'auth_admin':
			$editable = $editable && $logged_in_user->isAdmin();
			$value = $user->isAdmin();
			break;
		case 'auth_delete':
			$editable = $editable && $logged_in_user->isAdmin();
			$value = false;
			break;
		default:
			$value = $user->getConf($key);
			if ($value===false) $value='';
			break;
		}
		
		return array('value'=>$value,
		             'post_key'=>$post_key,
		             'editable'=>$editable);
	}
	private function setConf($uid, $key, $value) {
		// So, this rocks because we don't have to check permissions,
		// the User object does that.
		$user = Auth::getObj($uid);
		
		switch ($key) {
		case 'auth_name':
			$user->setName($value);
			break;
		case 'auth_user':
			$user->setUser($value=='true');
			break;
		case 'auth_admin':
			$user->setAdmin($value=='true');
			break;
		case 'auth_delete':
			if ($value=='true') $user->delete();
		default: 
			$user->setConf($key, $value);
			break;
		}
	}

	function attrib($key, $name) {
		return array('key'=>$key, 'name'=>$name);
	}
	private function getIndexAttribs() {
		$attribs = array();
		$attribs[] = $this->attrib('auth_user', 'Active');
		if (Auth::getObj(Login::isLoggedIn())->isAdmin()) {
			$attribs[] = $this->attrib('auth_admin', 'Admin');
			$attribs[] = $this->attrib('auth_delete', 'Delete');
		}
		$attribs[] = $this->attrib('lastname','Last');
		$attribs[] = $this->attrib('firstname','First');
		$attribs[] = $this->attrib('hsclass','Class of');
		$attribs[] = $this->attrib('phone','Phone number');
		$attribs[] = $this->attrib('email','Email');
		$attribs[] = $this->attrib('auth_name', 'Username');
		return $attribs;
	}
}