<?php
/**
 * @copyright Copyright (c) 2010, The volkszaehler.org project
 * @package util
 * @license http://www.opensource.org/licenses/gpl-license.php GNU Public License
 */
/*
 * This file is part of volkzaehler.org
 *
 * volkzaehler.org 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
 * any later version.
 *
 * volkzaehler.org 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 volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
 */

namespace Volkszaehler\Util;

/**
 * Extensible PRNG
 *
 * @author Steffen Vogel <info@steffenvogel.de>
 */
class Random {
	protected static $func = 'twister';
	protected static $source = NULL;

	/**
	 * Initialize the PRNG
	 *
	 * Look for a system-provided source of randomness, which is usually crytographically secure.
	 * /dev/urandom is tried first simply out of bias for Linux systems.
	 */
	public static function init() {
		if (is_readable('/dev/urandom')) {
			self::$source = fopen('/dev/urandom', 'rb');
			self::$func = 'fRead';
		}
		else if (class_exists('COM', 0)) {
			try {
				self::$source = new COM('CAPICOM.Utilities.1');  // See http://msdn.microsoft.com/en-us/library/aa388182(VS.85).aspx
				self::$func = 'COM';
			}
			catch(\Exception $e) {}
		}
		return self::$func;
	}

	/**
	 *
	 * @param intger $bytes
	 * @todo check if initialized
	 */
	public static function getBytes($count) {
		return call_user_func(array('self', self::$func), $count);
	}

	/**
	 *
	 * @param array $chars charset for random string
	 * @param integer $count length of string
	 */
	public function getString(array $chars, $length) {
		$numbers = self::get(0, count($chars) - 1, $length);
		$string = '';

		foreach ($numbers as $number) {
			$string .= $chars[$number];
		}

		return $string;
	}

	/**
	 * Generate $count random numbers between $min and $max
	 *
	 * @param integer $min
	 * @param integer $max
	 * @param integer $count
	 * @return integer|array single integer if $count == 1 or array of integers if $count > 1
	 */
	public function get($min, $max, $count = 1) {
		$bytes = self::getBytes($count);

		$numbers = array();

		for ($i = 0; $i < $count; $i++) {
			$numbers[] = ord($bytes[$i]) % ($max - $min + 1) + $min;
		}

		return ($count == 1) ? $numbers[0] : $numbers;
	}

	/*
	 * Get the specified number of random bytes, using mt_rand().
	 * Randomness is returned as a string of bytes.
	 */
	protected static function twister($count) {
		$rand = '';
		for ($a = 0; $a < $count; $a++) {
			$rand .= chr(mt_rand(0, 255));
		}
		return $rand;
	}

	/**
	 * Get the specified number of random bytes using a file handle
	 * previously opened with Random::init().
	 * Randomness is returned as a string of bytes.
	 */
	protected static function fRead($count) {
		return fread(self::$source, $count);
	}

	/*
	 * Get the specified number of random bytes using Windows'
	 * randomness source via a COM object previously created by Random::init().
	 * Randomness is returned as a string of bytes.
	 */
	protected static function COM($count) {
		return base64_decode(self::$source->GetRandom($count, 0)); // straight binary mysteriously doesn't work, hence the base64
	}
}

?>