<?php

/**
 * This file is part of Linfo (c) 2011 Joseph Gillotti.
 * 
 * Linfo 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.
 * 
 * Linfo 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 Linfo.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * Keep out hackers...
 */
defined('IN_INFO') or exit;

/**
 * Output in ncurses format for client side CLI functionality
 * @author Joseph Gillotti
 */
class out_ncurses {

	// Localize fun shit
	private 

		// Store our windows here
		$_windows = array(),
		$_max_dims = array(),
		
		// ncurses loaded?
		$loaded = true;

	// Start us off by checking important shit and starting it
	public function __construct() {

		// We obviously need this
		if (!extension_loaded('ncurses')) {
			echo "ncurses extension not loaded\n";
			$this->loaded = false;
			exit(1);
		}

		// Start ncurses
		ncurses_init();
		ncurses_timeout(0);
	}

	// Make sure ncurses_end() always gets called no matter what;
	// not doing so will leave the terminal messed up until the user 
	// runs 'reset'
	public function __destruct() {
		if ($this->loaded)
			ncurses_end();	
	}

	// Handle the shit
	public function work($info, $settings, $getter) {
		
		// Gain access to translations
		global $lang;

		// Say we're called more than once. Kill previous remnants
		if (count($this->_windows) > 0)
			$this->_kill_windows();
		
		// Get dimensions and give lovely header text
		$fullscreen = ncurses_newwin(0, 0, 0, 0);
		ncurses_wrefresh($fullscreen);
		ncurses_getmaxyx ($fullscreen, $x, $y);
		ncurses_mvwaddstr($fullscreen, 0, 0, 'Generated by '.AppName.' ('.VERSION.') on '.date('m/d/Y @ h:i:s A (T)'));
		ncurses_wrefresh($fullscreen);
		$this->_max_dims = array($x, $y);

		// Some important windows
		$core_wins = array(
			array(
				'name' => $lang['core'],
				'content' => array(
					array($lang['os'], $info['OS']),
					array_key_exists('Distro', $info) ? array($lang['distro'], $info['Distro']['name'] . ($info['Distro']['version'] ? ' '.$info['Distro']['version'] : '') ) : false,
					array($lang['kernel'], $info['Kernel']),
					array_key_exists('Model', $info) && !empty($info['Model']) ? array($lang['model'], $info['Model']) : false,
					array($lang['uptime'], str_ireplace(array(' ', 'days', 'minutes', 'hours', 'seconds'), array('', 'd', 'm', 'h', 's'), reset(explode(';', $info['UpTime'])))),
					array($lang['hostname'], $info['HostName']),

					array_key_exists('CPUArchitecture', $info) ? array($lang['cpu_arch'], $info['CPUArchitecture']) : false,

					array($lang['load'], implode(' ', (array) $info['Load']))
				)
			),
			array(
				'name' => $lang['memory'],
				'content' => array(
					array($lang['size'], byte_convert($info['RAM']['total'])),
					array($lang['used'], byte_convert($info['RAM']['total'] - $info['RAM']['free'])),
					array($lang['free'], byte_convert($info['RAM']['free'])),
				)
			)
		);

		// Show them
		$h = 1;
		foreach ($core_wins as $win) {
			list($width, $height) = $this->_window_with_lines($win['name'], $win['content'], $h, 0);
			$h += $height + 1;
		}

		// Makeshift event loop
		while (true) {

			// Die on input
			$getch = ncurses_getch();
			if ($getch > 0 && $getch == 113) {
				$this->__destruct();
				echo "\nEnding at your request.\n";
				exit(0);
			}

			// Stop temporariy
			ncurses_napms(1000);

			// Call ourselves
			$this->work($getter->getAll(), $settings, $getter);
		}
	}

	// Create a window with various lines as content
	private function _window_with_lines($name, $lines, $x = 5, $y = 5, $set_width = false) {

		// Need an array of lines. 
		$lines = (array) $lines;

		// Ignore disabled liens
		$lines = array_filter($lines);

		// Do we not have a specific set width? Calculate the longest line
		if (!is_numeric($set_width)) {
			$longest_line = strlen($name) + 10;
			foreach ($lines as $line) {
				$length = strlen(implode('', $line));
				$longest_line = $length > $longest_line ? $length : $longest_line;
			}
			$width = $longest_line + 4;
		}

		// Otherwise we do have a set with
		else
			$width = $set_width;

		// Calculate window hight
		$height = 3 + count($lines);

		// Create window
		$win =  ncurses_newwin($height, $width, $x, $y);

		// This character will be the side borders
		$side = ord('|');

		// Do the borders of the window
		ncurses_wborder($win, $side, $side, ord('-'), ord('-'), ord('/'), ord('\\'), ord('\\'), ord('/'));

		// Add window title string
		ncurses_mvwaddstr($win, 1, 1, $this->_charpad($name, $width, 'c', '='));

		// Keep track of vertical position for each line
		$v = 1;

		// Go through and output each line, while incrementing line position counter
		foreach ($lines as $line) {
			ncurses_mvwaddstr($win, $v + 1, 1, $this->_charpad($line[0] . $this->_charpad($line[1], $width - strlen($line[0]), 'r', '.'), $width, 'n'));
			$v++;
		}

		// Show it
		ncurses_wrefresh($win);

		// Store it so we can kill it later
		$this->_windows[] = &$win;

		// Return window dimensions
		return array($width, $height);
	}

	// Kill all windows
	private function _kill_windows() {
		foreach ($this->_windows as $win) {
			is_resource($win) &&
				ncurses_delwin($win);
		}
	}

	// Because I got tired of sprintf
	private function _charpad($string, $length, $direction, $filler = ' ') {
		
		// Keep length of string handy here
		$strlen = strlen($string);

		// Difference between max length and string length
		$difference = $length - $strlen;

		// If the string length is bigger than the max, just return string truncated to the max length
		if ($difference < 0)
			return substr($string, 0, $length);

		// Deal with direction
		switch ($direction) {

			// Right aligned (padded to the left)
			case 'r':
				return str_repeat($filler, $difference - 2) . $string;
			break;

			// Left aligned (padded to the right)
			case 'l':
				return $string . str_repeat($filler, $difference - 2);
			break;

			// Centered (padded left and right)
			case 'c':
				$cdiff = floor($difference / 2) - ($difference % 2 == 0 ? 1 : 0);
				return str_repeat($filler, $cdiff - 1) . $string . str_repeat($filler, $cdiff);
			break;

			// Not padded; returned as is (provided not longer than max, as tested above)
			case 'n':
				return $string;
			break;

			// Uhh not sure?
			default:
				return '';
			break;
		}
	}
}
