<?php
/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link          https://cakephp.org CakePHP(tm) Project
 * @since         2.8
 * @license       https://opensource.org/licenses/mit-license.php MIT License
 */
App::uses("BaseShellHelper", "Console/Helper");

/**
 * Create a progress bar using a supplied callback.
 */
class ProgressShellHelper extends BaseShellHelper {

/**
 * The current progress.
 *
 * @var int
 */
	protected $_progress = 0;

/**
 * The total number of 'items' to progress through.
 *
 * @var int
 */
	protected $_total = 0;

/**
 * The width of the bar.
 *
 * @var int
 */
	protected $_width = 0;

/**
 * Output a progress bar.
 *
 * Takes a number of options to customize the behavior:
 *
 * - `total` The total number of items in the progress bar. Defaults
 *   to 100.
 * - `width` The width of the progress bar. Defaults to 80.
 * - `callback` The callback that will be called in a loop to advance the progress bar.
 *
 * @param array $args The arguments/options to use when outputing the progress bar.
 * @return void
 * @throws RuntimeException
 */
	public function output($args) {
		$args += array('callback' => null);
		if (isset($args[0])) {
			$args['callback'] = $args[0];
		}
		if (!$args['callback'] || !is_callable($args['callback'])) {
			throw new RuntimeException('Callback option must be a callable.');
		}
		$this->init($args);
		$callback = $args['callback'];
		while ($this->_progress < $this->_total) {
			$callback($this);
			$this->draw();
		}
		$this->_consoleOutput->write('');
	}

/**
 * Initialize the progress bar for use.
 *
 * - `total` The total number of items in the progress bar. Defaults
 *   to 100.
 * - `width` The width of the progress bar. Defaults to 80.
 *
 * @param array $args The initialization data.
 * @return void
 */
	public function init(array $args = array()) {
		$args += array('total' => 100, 'width' => 80);
		$this->_progress = 0;
		$this->_width = $args['width'];
		$this->_total = $args['total'];
	}

/**
 * Increment the progress bar.
 *
 * @param int $num The amount of progress to advance by.
 * @return void
 */
	public function increment($num = 1) {
		$this->_progress = min(max(0, $this->_progress + $num), $this->_total);
	}

/**
 * Render the progress bar based on the current state.
 *
 * @return void
 */
	public function draw() {
		$numberLen = strlen(' 100%');
		$complete = round($this->_progress / $this->_total, 2);
		$barLen = ($this->_width - $numberLen) * ($this->_progress / $this->_total);
		$bar = '';
		if ($barLen > 1) {
			$bar = str_repeat('=', $barLen - 1) . '>';
		}
		$pad = ceil($this->_width - $numberLen - $barLen);
		if ($pad > 0) {
			$bar .= str_repeat(' ', $pad);
		}
		$percent = ($complete * 100) . '%';
		$bar .= str_pad($percent, $numberLen, ' ', STR_PAD_LEFT);
		$this->_consoleOutput->overwrite($bar, 0);
	}
}