<?php 

class dashboard {
	
	const class_name = __CLASS__;
	
	protected static $config = array(
	
		"sidebar" => true, // show the sidebar
		
		"footer" => true, // show the footer
		
		"blank" => false, // hide everything! show a blank page
		
		/*
			permit user into the dashboard panel!
			Probably after the user has logged in
		*/
		
		"auth" => false, 
		
		/*
			If a user is not permitted into the dashboard panel!
			What page should be displayed instead?
			
			[ The default is dashboard login page ]
			
			-----------------------------------------------------------------------------------------
			
			NOTE: "auth-page" index accepts an absolute file path.
			
			EXAMPLE:
			
				dashboard::config( 'auth-page', "C:/USS-V2/MODULES/MY-PROJECT/custom-login.php" );
			
			-------------------------------------------------------------------------------------------
			
			However, the dashboard modules also allow simple text 
			which can be used to link to the default authentication pages of the dashboard:
			
			Such As:
				
				dashboard::config( 'auth-page', 'login' );
				dashboard::config( 'auth-page', 'register' ); ...
				
		*/
		
		"auth-page" => null
		
	);
	
	
	/*
		-	The private `$calls` property holds all the callables
		
			which are executed by `dashboard::call()` method
	*/
	
	private static $calls;
	
	
	/*
		Dashboard Configuration or initialization!
		-------------------------
		
		The method saves data into the self::$config property 
		
		Which is futher used to determine the outcome of the dashboard interface;
		
		
	*/
	
	public static function config( $data, $value = null ) {
		if( is_array($data) ) {
			foreach( $data as $key => $value ) 
				self::$config[ $key ] = $value;
		} else {
			if( func_num_args() === 1 ) return self::$config[ $data ] ?? null;
			else self::$config[ $data ] = $value;
		}
	}
	
	
	/*
		Display the dashboard
		----------------------
		This uses the `uss::view()` method to display user panel
		
	*/
	
	public static function view( callable $func ) {
		
		uss::console( '_ajax', core::url( DASHBOARD_MOD_DIR . '/ajax' ) );
		
		
		/*
			Before a user can see the dashboard,
			self::$config['auth'] must be set to `TRUE`
		*/
		
		if( !self::$config['auth'] ) { // If authentication is disapproved
			
			
			// Prepare a page blank;
			
			self::$config['blank'] = true;
			
			
			// Get the default AUTH directory;
			
			$auth_dir = DASHBOARD_MOD_DIR . "/view/AUTH";
			
			
			// Get the assigned authentication page;
			
			$auth_page = self::$config['auth-page'] ?? 'signin';
			
			
			// Reset ajax password;
			
			uss::console( '_ajax', core::url( DASHBOARD_MOD_DIR . '/ajax' ) );
			
			
			/*
				Check if `auth-page` matches a simple text syntax;
				And then, get the absolute path of the related file
			*/
			
			switch( str_replace(['_', '-'], ['', ''], $auth_page) ) {
				
				case 'signup':
				case 'register':
				
						// Get the Signup Reset Page!
						
						$auth_page = "{$auth_dir}/signup.php";
					
					break;
					
				case 'reset':
				case 'resetpassword':
						
						// Prepare the ajax file to manage password!
						
						uss::console( 'reset_url', core::url( DASHBOARD_MOD_DIR . '/ajax/change-password.php' ));
						
						// Get the file that will confirm if the password reset link is correct!
						
						require "{$auth_dir}/verify-reset.php";
						
						// Get the Password Reset Page!
						
						$auth_page = "{$auth_dir}/reset.php";
						
					break;
					
				case 'login':
				case 'signin':
						
						// Get the file that will confirm the email link!
						
						require "{$auth_dir}/verify-email.php";
						
						// Get the user registration page!
						
						$auth_page = "{$auth_dir}/signin.php";
						
					break;
					
			};
			
			/*
				Since the authentication is not approved!
				
				override the user defined `$func`;
				
				@overrides 
				
					PHP: Hey! This is not JAVA
					
					UCSCODE: OH! Sorry, I Forgot
			*/
			
			$func = function() use($auth_page) {
				if( is_file($auth_page) ) require $auth_page;
				else require VIEW_DIR . '/error-400.php';
			};
			
		} else {
			
			// However! If the authentication page is approved!
			
			uss::console( '_nonce', uss::nonce( $_SESSION['uss_session_id'] ) );
			
			/*
				The security level of the above `_nonce` being displayed in JAVASCRIPT has not really been confirmed!
				So! I suggest creating and using a custom nonce when developing your project!
			*/
			
		}
		
		// Great! Now let's modify the interface by adding some eventListeners
		
		
		# --------- [{ HEAD }] ----------
		
		events::addListener('@head::before', function() {
			require DASHBOARD_MOD_DIR . '/view/meta.php';
		}, EVENT_FLOAT);
			
		events::addListener('@head::after', function() {
			require DASHBOARD_MOD_DIR . '/view/header.php';
		}, EVENT_FLOAT);
		
		
		# -------- [{ BODY }] ----------
		
		events::addListener('@body::before', function() {
			if( self::$config['blank'] ) self::$config['sidebar'] = false;
			if( self::$config['sidebar'] ) require_once DASHBOARD_MOD_DIR . '/view/sidebar.php';
			require_once DASHBOARD_MOD_DIR . '/view/main-before.php';
		}, EVENT_FLOAT);
		
		events::addListener('@footer', function() {
			require_once DASHBOARD_MOD_DIR . '/view/main-after.php';
		}, EVENT_FLOAT);
		
		events::addListener('@body::after', function() {
			require DASHBOARD_MOD_DIR . '/view/footer.php';
		}, EVENT_FLOAT);
		
		
		# ---------- [{ NOW DISPLAY }] -----------
		
		events::addListener('dashboard::[[view]]', function() use($func) {
			uss::view($func);
		});
		
	}
	
	
	/*
		`self::call()` contains built-in functions that cannot by access directory from the `dashboard` class
		
		- Parameter 1 is the closure (function) that should be called.
		
		- Other parameters are arguments that will be passed to the closure!
		
		- Think of it like PHP's `call_user_func()` 
		
	*/
	
	public static function call( ?string $method ) {
		
		/*
			Get all the hidden closures used by this methods
			
			The method should also be required once to avoid unnecessary errors
		*/
		
		require_once DASHBOARD_MOD_DIR . '/model/calls__.php';
		
		// sort in ascending order
		
		ksort(self::$calls);
		
		// get all methods or:
		
		if( is_null($method) ) return self::$calls;
		
		// get parameters;
		
		$args = func_get_args();
		array_shift($args);
		
		// get specific method;
		
		if( isset(self::$calls[ $method ]) )  {
			
			try {
				
				return call_user_func_array( self::$calls[ $method ], $args );
				
			} catch(TypeError $err) {
				
				/*
				
					- When the closure of this method throws a TypeError, 
					
					- It's very hard to trace because closures don't really have names.
					
					TO RESOLVE THIS, WE MUST:
					-------------------------
					
					- Catch the TypeError
					- Name the closure
					- Then re-throw the error;
					
				*/
				
				$closure_name = __CLASS__ . '::' . __FUNCTION__ . "('{$method}')";
				
				$error = str_replace( __CLASS__ . '::{closure}()', $closure_name, $err->getMessage() );
				
				throw new TypeError( $error );
				
			};
			
		} else {
		
			$class = __CLASS__;
			$func = __FUNCTION__;
			
			throw new Exception( "Call to unknown action `{$method}` in {$class}::{$func}() at parameter 1" );
			
		};
		
	}
	
	
	/*
		This is a short cut to add push notification for users!
		
		To update a notification, pass the notification ID to parameter 2
		
		Else, a new push notification will be created!
		
		-----------------------------------------------------------
		
		Note: Push notification uses `MARK DOWN` Language
			
			Don't use <b>Bold</b> to make a text bold!
			
			Use **Bold** to make a text bold!
	*/
	
	public static function notify( array $data, ?int $id = null ) {
		
		// presence of parameter 2 updates a notification with the given id
		
		$required = array('userid', 'message');
		$request = is_null($id) ? "deliver" : "update";
		
		foreach( $required as $key ) {
			if( !in_array($key, array_keys($data)) ) {
				throw new Exception( $key . " is required to {$request} notification" );
			};
		};
		
		/* 
			Remove `id` if it's part of array index
			
				- Yea! You still haven't told me the reason why you want to update the primary key!
		*/
		
		if( isset($data['id']) ) unset($data['id']);
		
		// sanitize data;
		
		foreach( $data as $key => $value ) {
			$value = trim( $value );
			$data[ $key ] = uss::$global['mysqli']->real_escape_string( $value );
		};
		
		// prepare data for database entry;
		
		$prefix = DB_TABLE_PREFIX;
		
		if( !is_null($id) ) {
			$SQL = sQuery::update( "{$prefix}_notifications", $data, "id = {$id}" );
		} else {
			$SQL = sQuery::insert( "{$prefix}_notifications", $data );
		};
		
		// save notification ;
		
		$status = uss::$global['mysqli']->query( $SQL );
		return $status;
		
	}
	
	
	/*
		Sometimes, you only need to fetch row of a single data!
		
		Like getting user by id!
		
		This is a short cut!
	*/
	
	public static function fetch_assoc( string $tablename, ?string $value, $column = 'id' ) {
		
		// fetch a single row in a result set;
		
		// escape special characters;
		
		$column = uss::$global['mysqli']->real_escape_string( $column );
		$value = uss::$global['mysqli']->real_escape_string( $value );
		$tablename = uss::$global['mysqli']->real_escape_string( $tablename );
		
		// get the column row;
		
		$resultant = is_null($value) ? " IS NULL " : " = '{$value}'";
		$SQL = sQuery::select( $tablename, "{$column} {$resultant}" );
		$result = uss::$global['mysqli']->query( $SQL )->fetch_assoc();
		
		return $result;
		
	}
	
	
}