<?

// Выводит статистику в интерфейс программы

class Stat {
	
	// поля статистики помимо 
	static $all_fields = array(
	);

	// последнее имя дополнительного параметра
	static $last_add_param = false;
	static $last_operation = false;

	// какие поля надо очищать на каждом этапе
	// нужно указывать только поля которые реально использует поток
	// последовательность шагов в массиве важна

	// ВНИМАНИЕ! Имена графиков должны начинаться с graph_ иначе они будут некорректно обработаны в Stat::clear_step_blocks_stat();

	static $step_clear_fields = array(
		'select_disc'=>array(),
		
		'create_blocks'=>array(BLK_slow_write, BLK_create_err), // эти BLK_ поля надо добавлять в исключения в read_blocks.php , там где плюсуется статистика ($INFO[$result] ++;)

		'read_blocks'=>array(BLK_fast, BLK_read_err, BLK_slow_read, BLK_slow_auto,
								BLK_antifrag_cancel, BLK_antifrag, 
								
								//нельзя сюда. Потому что перед анализом, эти поля очищатся. А на м надо чтобы оно сохранилось. В самом потоке очистится.
								//UP. Хрен знает, вроде в анализе мы его сами и очищаем. Включим, посмотрим
								'read_blocks_fast',  

								'graph_speed', 'graph_frags'/*BLK_fast, BLK_read_err, BLK_slow_read, BLK_slow_auto, BLK_antifrag_cancel, BLK_antifrag,*/
							),

		// анализ блоков заново пересчитывает все поля.
		// Из-за BLK_slow_write, BLK_create_err, нельзя добавлять все поля чтобы сбросить их. Вместо этого в ui_analize_blocks вручную чистим поля
		'analize_blocks'=>array(BLK_frag, BLK_slow_analize, 
								'graph_frags', 

								/*'rspeed_average_good', 'rspeed_average_all',*/ 
								/*'graph_speed'  график скоростей бессмысленно  пересчитывать, сильно экономим на нем время*/
								), 

		'final_operations'=>array(),
		'final'=>array(),
	);

	// отмечаем поля статистики кототорые мы правим в этом этапе
	static function enabled_up_step() {
		//foreach(self::$step_clear_fields
	}

	// очищаем статистику до указанного этапа
	static function clean_up_step($step) {
		GLOBAL $INFO, $log;

		if(!$step) {
			Error::view("Не передан параметр step", 'Stat::clean_up_step()');
			return false;
		}

		$mas = array_reverse(self::$step_clear_fields);
		
		foreach($mas as $cur_step=>$fields) {
			if($cur_step == $step) break;

			self::clear_step($cur_step);
		}
	}


	// очистка статистики блоков для указанного этапа
	static function clear_blocks_stat_step($step) {
		GLOBAL $INFO;

		foreach (Stat::$step_clear_fields[$step] as $name) {
			if(preg_match('#^graph_#', $name)) // графики не трогаем
				continue;

			$INFO[$name] = 0;
		}

	}

	static function clear_info_fields($step) {
		GLOBAL $GOP, $OP, $INFO, $log, $Graphs;

		$fields = self::$step_clear_fields[$step];
		if(!$fields) return false; //оказывается пустой массив приравнивается к false

		//$log->line("очищаем этап $step");
		foreach($fields as $name) {
			if($name == 'graph_frags') {
				// надо полностью пересоздавать массив для надежности
				$INFO['graph_frags'] = array_fill_keys($GOP['graph_frags']['keys'], 0); 
				// также сбрасываем максимальное значение поля
				$Graphs['frags']->set_max_value(5);
			}
			elseif($name == 'graph_speed') {
				$INFO['graph_speed'] = array_fill_keys($GOP['graph_speed']['keys'], 0);
				$Graphs['speed']->set_max_value(5);
			}
			else
				$INFO[$name] = 0;
		}

		return true;
	}


	// очищаем статистику указанного этапа
	static function clear_step($step) {
		GLOBAL $GOP, $OP, $INFO, $log, $Paths, $Graphs, $setting, $Lang, $Ui_steps_story;

		$fields = self::$step_clear_fields[$step];
		if(!$fields) return false; //оказывается пустой массив приравнивается к false

		self::clear_info_fields($step);

		self::process_clear();

		$INFO['cur_fname'] = 0;

		$INFO[$step.'_full_time'] = 0;
		$INFO['recovery_fname'] = 0; // на всякий случай

		$setting->del_recovery();

		// удаляем файл блоков на всякий случай
		if($step == CREATE_BLOCKS or $step == READ_BLOCKS) {
			$blocks_fname = $Paths->data_file("$step.txt");
			n(); unlink($blocks_fname); y();
		}

		c('Fm->bt_run')->caption = $Lang->get('bt_run.start');
		//c('Fm->bt_step_reset')->enabled = false;

		// включаем настройки
		$Ui_steps_story->steps[$step]->fields_enabled(1, true);
	}

	static function redraw_times() {
		GLOBAL $OP, $INFO, $log, $Measure_convert, $Ui_thread;
		
		//$log->line('start' . $INFO['start_time_thread']);

		$label_time_from_start = 'Fm->stat_time_from_start';
		$label_ostalos_time = 'Fm->stat_ostalos_time';
		
		// если время скрываем, то пока просто тире туда пишем
		if(!empty($INFO['hide_times'])) {
			cCache_text($label_time_from_start, '---');
			cCache_text($label_ostalos_time, '---');
			return true;
		}

		$step = $INFO['step'];

		// время с начала
		$time_from_start = 0;
		$pause_time = $Ui_thread->get_pause_sumtime();

		if($INFO['start_time_thread']) {
			$time_from_start = time() - $INFO['start_time_thread'];
			$time_from_start -= $pause_time;
		}

		if(isset($INFO["{$step}_full_time"]))
			$time_from_start += $INFO["{$step}_full_time"];

		//$log->line('full time: ' . $INFO["{$step}_full_time"] . ', ' . 'start_thread: ' . $start_thread);

		cCache_text($label_time_from_start, $Measure_convert->format_time($time_from_start));

		// время осталось
		// можно вычислить только если есть хотя бы один процент
		if($INFO['procent'])
			 $ostalos_time = ($time_from_start) * 100 / $INFO['procent'] - ($time_from_start);	
		else $ostalos_time = 0;

		cCache_text($label_ostalos_time, $Measure_convert->format_time($ostalos_time));
	}


	// обнуление общей статистики которая персональная для каждого запуска процесса
	// хотя часть информации имеет отношение ко всему этапу, а не только текущему процессу.
	static function process_clear($is_ui=true) {
		GLOBAL $INFO;
		
		$INFO['start_time_thread'] = 0;
		//$INFO[$INFO['step'].'_full_time'] = 0; нельзя здесь обнулять, потому что process_clear вызывается после успешного завершения этапа

		$INFO['procent'] = 0;
		if($is_ui)
			Progress_bar::$last_procent = -1; 

		$INFO['operation'] = 'Обработано'; // каждый процесс сам управляет что здесь будет написано
		$INFO['operation_count'] = 0;
		$INFO['operation_max'] = 0;
		$INFO['add_param'] = false;
		$INFO['add_param_count'] = 0;
		
		$INFO['cur_frag_num'] = 0;
	}

	// статистика для отладки
	static function redraw_dev_info() {
		GLOBAL $Ui_thread, $INFO, $OP, $Measure_convert;

		/*
		if($Ui_thread->real_sum_write_time) {
			if($field = get_ui('Fm->real_write_time')) {
				$field->text = $Measure_convert->format_time($Ui_thread->real_sum_write_time);
			}
		}*/
	}

	// статистику по текущему процессу собираем в одну строку. 
	static function process_to_str() {
		GLOBAL $INFO, $Measure_convert;

		// если время старта не указано, выходим
		if(!$INFO['start_time_thread']) {
			return '';
		}

		// времени потрачено
		$time = $Measure_convert->format_time(time() - $INFO['start_time_thread']);

		$str = '';
		$str .= "$INFO[operation]: $INFO[operation_count] \n";
		if($INFO['add_param']) {
			$str .= "$INFO[add_param]: $INFO[add_param_count] \n";
		}
		$str .= "Время, затраченное на этап: $time \n";

		return $str;
	}

	// настраиваемая статистика по текущей операции (кроме времени и процентов)
	static function redraw_process() {
		GLOBAL $OP, $INFO, $log;

		// == текущая операция ==
		$operation = c('Fm->stat_operation_name');
		$operation_count = c('Fm->stat_operation_count');

		// если имя изменилось, переименовываем, показываем/скрываем
		if($INFO['operation'] != self::$last_operation) {
			cCache_text($operation,  $INFO['operation'].':');
			self::$last_operation = $INFO['operation'];
		}
		// плюсуем счетчик
		cCache_text($operation_count, $INFO['operation_count']);

		// == дополнительный параметр ==
		// использовался только в одной незначительной операции
		// а поскольку меcта в gui мало, отключаем. Но сохраним, вдруг потом понадобится
		/*
		$add_param = с('stat_add_param_name');
		$add_param_count = с('stat_add_param_count');

		// если имя параметра изменилось
		//изменяем название, скрываем/показываем
		if($INFO['add_param'] != self::$last_add_param) {
			if($INFO['add_param']) {
				// отключаем потому что нет места где его размещать
				$add_param->visible = 1;
				$add_param_count->visible = 1;
				cCache_text($add_param, $INFO['add_param']);
			}
			else {
				// скрываем если не указан
				$add_param->visible = 0;
				$add_param_count->visible = 0;
			}

			self::$last_add_param = $INFO['add_param'];
		}
		
		// плюсуем счетчик
		if($INFO['add_param']) {
			cCache_text($add_param_count, $INFO['add_param_count']);
		}
		*/
	}

	// подсчет размер пользовательских файлов
	// надо высчитывать с учетом наших записанных файлов, поэтому не так все просто
	// TODO не учитывает что после этапа финальные операции, тестовые файлы модифицируются и частично удаляются.
	static function calc_stat_userfiles() {
		GLOBAL $Paths, $INFO, $log, $Disc_info;

		// можно выполнять только до начала записи create_blocks
		if(!empty($INFO['recovery_fname']))
			return false;

		$size = $Disc_info->size($Paths->disc_name, 'KB', 0, false);

		//$log->line('calc_stat_userfiles');

		// возможно тестовые файлы удалены руками
		// достаточно проверить что папка пуста или отсутствует, значит человек удалил файлы руками
		if($INFO['stat_testfiles']) {
			//$log->line('счетчик есть. значит запускаем проверку');

			$is_testfiles = true;
			if(file_exists($Paths->blocks())) {
				//$log->line('папка есть');

				if(!$handle = opendir($Paths->blocks())) 
					throw new Error_def("Не открывается папка: " . $Paths->blocks());
				
				// проверяем, есть ли что-то в папке
				$i = 0;
				while (false !== ($entry = readdir($handle))) {
					if($entry == '.' or $entry == '..') continue;
					$i = 1;
					break;
				}
				closedir($handle);

				if($i) {
					$is_testfiles = true;
					//$log->line('файлы есть');
				}
				else $is_testfiles = false;
			}
			else $is_testfiles = false;

			if(!$is_testfiles) {
				$INFO['stat_testfiles'] = 0;
				//$log->line('тестовые файлы удалены');
			}
		}

		//$log->line("$size[full] - $size[free] - $INFO[stat_testfiles]");

		$INFO['stat_userfiles'] = $size['full'] - $size['free'] - $INFO['stat_testfiles'];

		// если пользователь удалил тестовые файлы не до конца или еще почему то
		// это всего лишь счетчик, который кроме графика ни на что не влияет
		// поэтому точность не очень важна
		if($INFO['stat_userfiles'] < 0) $INFO['stat_userfiles'] = 0;

	}
	
	// динамический пересчет, учитывающий перемещение ползунков на графиках
	// может работать не совсем точно
	static function realtime_result_space() {
		GLOBAL $OP, $INFO;

		// итоговое пространство. Не меняется при перемещении ползунка фрагментов.
		$mb = 
			(
				$INFO['read_blocks_fast']
				//+ $INFO[BLK_antifrag] если отключаем то график фрагментов на максимальном значении  не досчитается блока. А если включаем то график скоростей выдает лиший блок
				- $INFO[BLK_slow_analize]
				- $INFO[BLK_frag]
			)
			* $OP['block_size'] / 1024;
		
		// если мы двигаем ползунок скорости вправо, то часть фрагментов становится медленными (но это будет только после пересчета). А пока же получаем отрицательные значения. Поэтому так хак.
		if($mb < 0 ) $mb = 0;

		return $mb;
	}


	// итоговое пространство в килобайтах
	static function calc_result_space() {
		GLOBAL $OP, $INFO, $log, $BLK_const, $Disc_info, $Measure_convert;

		$good = 0;
		
		// считаем хорошее и плохое пространство
		$mas = $BLK_const->all_name_to_value();
		foreach($mas as $const_name=>$const_value) {
			$num_blocks =  isset($INFO[$const_value])
								 ? $INFO[$const_value] : 0;

			if($BLK_const->is_GOOD($const_value))
				$good += $num_blocks;
		}

		return $good * $OP['block_size'];
	}

	// статистика по блокам
	static function redraw_block_status() {
		GLOBAL $OP, $INFO, $log, $Graphs, $BLK_const, $Measure_convert;

		// занятое пространство
		if($Graphs['disk']->legend->get_visible(GRD_userfiles))
			$Graphs['disk']->set_col(GRD_userfiles,  $INFO['stat_userfiles'] / 1024);

		// удачно записанные файлы
		if($Graphs['disk']->legend->get_visible(GRD_good_write)) {
			$good_write_kb = $INFO['stat_testfiles'] - ($INFO[BLK_slow_write] + $INFO[BLK_create_err])*$OP['block_size'];
			$Graphs['disk']->set_col(GRD_good_write,  $good_write_kb / 1024);
		}

		// итоговое пространство. Не меняется при перемещении ползунка фрагментов.
		if($Graphs['disk']->legend->get_visible(GRD_result_space)) {
			$mb = self::realtime_result_space();
			$Graphs['disk']->set_col(GRD_result_space,  $mb);
		}

		// === определяем, нужно ли пересчитывать график диска ===
		$result_change =  $speed_change = $frag_change = false;
		
		// без этого if, в других этапах может работать не адекватно
		// видимо в начале step еще не установлен
		if(isset($INFO['step']) AND ($INFO['step'] == 'read_blocks' or $INFO['step'] == 'analize_blocks')) {

			if($INFO["graph_speed_relevance_col_nomer"] != $Graphs['speed']->select_col_nomer)
				$speed_change = true;

			if($INFO["graph_frags_relevance_col_nomer"] != $Graphs['frags']->select_col_nomer)
				$frag_change = true;

			if($speed_change or $frag_change)
				$result_change = true;
		}

		//$log->line("$result_change =  $speed_change = $frag_change");

		// сообщение о пересчете.
		// Не нужно, потому что и так кнопка пересчета подсвечивается
		 $Graphs['disk']->view_msg_recalc('disk', $result_change); 

		// метки что данные приблизительные
		$Graphs['disk']->legend->about(GRD_result_space, $result_change);
		$Graphs['disk']->legend->about(GRD_frag, $speed_change); // скорость устанавливает фрагменты и наоборот
		$Graphs['disk']->legend->about(GRD_all_slow, $frag_change);
		// ===========

		// all_slow и frag не надо здесь обновлять
		// они автоматом обновляются по таймеру у трекбаров refresh_pos
		//$log->line("redraw_block_status() slow_analize: $INFO[BLK_slow_analize]; slow_read: $INFO[BLK_slow_read]; antifrag_cancel  $INFO[BLK_antifrag_cancel]");
		//$mb = ($INFO[BLK_slow_read] + $INFO[BLK_slow_analize] + $INFO[BLK_antifrag_cancel]) * $OP['block_size'] / 1024;
		// $log->line('redraw_block_status() mb: ' . $mb);
		//$Graphs['disk']->set_col(GRD_all_slow,  $mb);

		// Статусы блоков
		$mas = $BLK_const->all_name_to_value();
		foreach($mas as $const_name=>$const_value) {
			if($const_name == 'BLK_write_ok') continue;

			$field_name = str_replace('BLK_', '', $const_name);
			$num_blocks =  $INFO[$const_value];

			// эти значения отправляем в график диска
			$grd_const = "GRD_$field_name";
			if(defined($grd_const)) {
				$col_key = constant($grd_const);
				$mb = $num_blocks * $OP['block_size'] / 1024;
				//$log->line('redraw_block_status() ' . "$grd_const " . '  ' .$mb);
				$Graphs['disk']->set_col($col_key,  $mb);
			}
			else {
				if($num_blocks)
					 $size_with_measure = $Measure_convert->pretty_size($num_blocks*$OP['block_size'], 'KB');
				else $size_with_measure = 0;

				//$log->line('BLK_' . $field_name);				

				// поскольку мы может выводить не все статусы
				if($obj_field = get_ui('Fm->stat_' . $field_name)) {
					cCache_text($obj_field, $size_with_measure);
				}
			}
		}
	}

	// скорость чтения до и после
	// данные примерные потому что не учитывают autobad blocks
	static function redraw_read_speed() {
		GLOBAL $OP, $INFO;

		$type_mas = array('all', 'good');
		foreach($type_mas as $type) {

			$count = 0;
			if(!empty($INFO['read_time_sum'][$type]))
				$count = ($INFO['files_done'][$type] * ($OP['block_size']/1024)) / $INFO['read_time_sum'][$type];
			
			$count = round_nobug($count, 2);
			
			cCache_text("Fm->stat_rspeed_$type", $count . ' MB/s');
		}

		if(isset($INFO['testspeed_1']))
			get_ui('Fm->stat_testspeed_1')->text = $INFO['testspeed_1'] ;
					

		if(isset($INFO['testspeed_2']))
			get_ui('Fm->stat_testspeed_2')->text = $INFO['testspeed_2'] ;
	}

	// счетчики ошибок
	static function redraw_count_errors() {
		GLOBAL $INFO;
		STATIC $mas = array(
			'readBlocks_open_error',
			'readBlocks_read_error',
			'finalOperations_rename_error',
			'finalOperations_del_error',
			//'read_blocks.error_open',
		);

		foreach($mas as $name) {
			if(isset($INFO[$name]))
				cCache_text('Fm->'.$name, $INFO[$name]);	
		}

	}

	// итоговое хорошее пространство
	static function redraw_result_size() {
		GLOBAL $OP, $INFO, $log, $BLK_const, $Disc_info, $Measure_convert;
		
		// обновляем бар
		//$bar2 = c('stat_result_size_bar_2');
		//$bar2->position = $good_size_kb;

		$good_size_kb = self::calc_result_space();

		$size = $Measure_convert->pretty_size($good_size_kb, 'KB');
		cCache_text('Fm->stat_result_size_count_2', $size);
	}



	// не используем потому что не нужна оказалась такая статистика
	static function redraw_cur_frag() {
		GLOBAL $OP, $INFO, $Measure_convert;
		
		if(isset($INFO['cur_frag_num'])) {
			$size_kb = $INFO['cur_frag_num'] * $OP['block_size'];
			$size_with_measure = $Measure_convert->pretty_size($size_kb, 'KB');
			cCache_text('Fm->stat_cur_frag_size', $size_with_measure);
		}
	}
}


?>