<?

// всякие действия связанные с полями этого этапа.

class Ui_create_blocks {

	static $windows_drive_min_free_space = 2; // в Gb
	static $prog_drive_min_free_space = 0.2; // Gb

	// действия при активации этапа
	static function active() {
		GLOBAL $GOP, $Graphs, $INFO;
		
		// сбрасываем отметку об отправке отчета
		$INFO['sended_report'] = false;

		$INFO['site_report_id'] = '';

		// надо прописывать во всех этапах!
		Step_activator::graphs_enabled(0);

		$Graphs['disk']->legend->visible(GRD_good_write, 1);
		$Graphs['disk']->legend->visible(GRD_create_err, 1);
		
		$Graphs['disk']->legend->visible(GRD_slow_auto, 0);
		$Graphs['disk']->legend->visible(GRD_read_err, 0);
		$Graphs['disk']->legend->visible(GRD_frag, 0);
		$Graphs['disk']->legend->visible(GRD_all_slow, 0);
		$Graphs['disk']->legend->visible(GRD_result_space, 0);

		// подсчет сколько на диске пользовательских файлов
		Stat::calc_stat_userfiles();

		// здесь нужно вызвать, потому что все предыдущие разы она вызывалась когда основная панель была скрыта и это вызывало глюк с свойством visible у сообщения
		Ui_create_blocks::info_right_panel();
	}


	// ===== Дополнительные функции ====

	// это кнопка "пропустить все сектора"
	static function skip_all_sect($is_bad_sect=false) {
		GLOBAL $INFO, $OP, $GOP, $Ui_thread, $Bt_run, $setting, $Paths,  $Disc_info, $Paths, $Ui_fields, $log, $Forms;

		// пока не используем!

		// надо проверить, уменьшил ли человек раздел или надо включать режим часть диска
		// надо пересчитать кол-во записанных блоков и дополнитель Blocks если надо. Иначе у нас будет лишнее занятое пространство по итогу

		// проверяем, есть ли свободное пространство
		// если нет, то спокойной запускаем второй этап
		// если есть, предлагаем включить часть диска и начать чтение
		// 		либо жмете отмена и обрезаете диск

		/*
		варианты:
			1. человек заебался ждать и хочет результат
				1. можем включить режим часть диска 
				или пусть он обрежет диск и потом уже обработка продолжится

			2. программа наткнулась на битые сектора

		*/


		// $text ="Уверены что хотите пропустить все оставшиеся сектора и завершить этап?\n Они будут заблокированы.";
		// $res = messageDlg($text, mtWarning, MB_OKCANCEL);
		// if($res == 2)
		// 	return false;


		if($Ui_thread->check_run()) {
			$Bt_run->stop($INFO['step']);
			sleep(1);
		}

		$num_blocks = 0;
		$blocks_file = $Paths->data_file('create_blocks.txt');
		if(file_exists($blocks_file)) {
			$Blocks = new Blocks();
			$Blocks->set_fpath($blocks_file);
			$Blocks->set_mode_add(true);
			$Blocks->load();

			$start = $Blocks->end_index_element()  +1;

			clearstatcache(true);
			// а теперь допишем в blocks все записнные но не учтенные файлы.
			for($fname = $start; $fname <= 1000000000; $fname ++)  {
				// эти выражения есть еще и ниже
				$folder_name = floor($fname/$GOP['files_in_folder']);
				$fpath = $Paths->blocks()."/$folder_name/$fname";
				// если файла нет, значит скорее всего дальше мы не писали, выходим
				if(!file_exists($fpath)) {
				//	$log->line("Blocks. $fname файл не найден");
					break;
				}

				// если размер не совпадает, значит ниже попробуем его переписать и скорее всего это последний файл который записывали
				// if(filesize($fpath) != $OP['block_size']*1024)  {
				// //	$log->line("Blocks. $fname размер не совпадает");
				// 	break;
				// }

				//$log->line("Blocks. $fname добавляем");

				// файл уже есть,
				$result = BLK_write_ok;
				$Blocks->set($fname, $result, -1);
				//$log->line("Пересчет. Добавили $fname");
			}

			$num_blocks = $end_block = $Blocks->end_index_element();

			// определяем размер последнего блока
			$folder_name = floor($end_block/$GOP['files_in_folder']);
			$fpath = $Paths->blocks()."/$folder_name/$end_block";
			n(); $len = filesize($fpath); y();
			if(!$len) $len = $OP['block_size']*1024; // просто чтобы потом ошибки не возникло при проверке

			$Blocks->head_info['end_block_size'] = $len;
			$Blocks->save_head();
			$Blocks->save();
		}

		// нельзя так блокировать, потому что если бедблок оказался на первом файле, то num_blocks будет нулевым
		// if(!$num_blocks) {
		// 	messageDlg("Сначала запустите текущий этап.", mtWarning, MB_OK);
		// 	return false;
		// }

		$new_part_sizeKB = $num_blocks*$OP['block_size'];
		$new_part_sizeMB = $new_part_sizeKB / 1024;

		//$log->line("кол-во блоков: $num_blocks; $new_part_size MB");

		$disc_size = $Disc_info->size($Paths->disc_name, 'MB', 5, 'noformat');
		// если свободное место больше, значит человек еще не обрезал
		// либо часть диск и человек еще не нажимал укоротить часть
		if($disc_size['free'] >= $GOP['create_blocks.free_disk_cut_bad_block']
			OR ($OP['use_part'] AND $OP['part_size'] > $new_part_sizeMB)) { //part_size в MB измеряется

			// если режим части диска не включен, выводим диалог
			// UP. Выводить диалог надо в любом случае, а то человек не поймет, почему параметр "размер части диска" изменился. А в случае если 
			//if(!$OP['use_part']) {
			$res = $Forms['fmSkip_write_blocks']->open($is_bad_sect);

			// если человек просто закрыл окно, то выходим
			if($res != mrOk) 
				return false;
			//}

			$Ui_fields['use_part']->set(1);

			// на случай если проблема в первом файле, тогда размер части не трогаем
			if($new_part_sizeMB) {
				$Ui_fields['part_size']->set($new_part_sizeMB); // part_size в MB измеряется
			}
			// my_message_dialog($text, "Как обрезать диск\r\nО параметре часть диска\r\nВкл часть диска и продолжить", array('buttonsWidth'=>150));
			// $GLOBALS['Forms']['fmHelp']->open_img('help/cutting_disk.png');
			// //$res = messageDlg($text, mtWarning, MB_OKCANCEL);
		}

		// если включили часть диска, переходим на следующих этап
		if($OP['use_part']) {
			if(!$num_blocks) {
				$msg = "Нельзя перейти на следующий этап, потому что не записан ни один файл";
				$log->line($msg);
				my_message_dialog($msg);
				return false;
			}

			$INFO['step'] = Steps_info::get_next($INFO['step']);
			
			Ui_read_blocks::$autorun = true;

			// очищаем статистику
			Stat::clean_up_step($INFO['step']);
			Stat::clear_step($INFO['step']);

			Ui_timers::tick_root();
		}
		// иначе запускаем запись, пусть добьет свободное пространство
		// а потом автоматом запуститься второй этап
		else {
			$Bt_run->start(CREATE_BLOCKS);
		}
	}

	// высчитывание размера пространства которое будем тестировать
	// Берет данные из полей use_part и part_size 
	static function change_part_size() {
		GLOBAL $INFO, $log, $OP, $Graphs, $Paths, $Disc_info, $Ui_fields, $Measure_convert;

		// ее нельзя вызывать за пределами этапа create_blocks. Не правильно посчитает свободное место.  Но что тогда делать если человек вернулся к обработке диска позже и оказался на чтении или ниже?
		
		//	return false;

		// поскольку эта функция вызывается по onChange, то при загрузке приложения, она сработает до того как все будет инициилизированно. Поэтому вызовем ее отдельно при активации этапа.
		if(empty($INFO['step']))
			return;


		// подсчет сколько на диске пользовательских файлов
		// подсчет должен срабатывать на любом этапе. Да там и подсчет одно название.
		// забыл, почему подсчет должен идти во всех этапах. Но на фишине он глючит и точно его надо там отключить
		if($INFO['step'] != FINAL_OPERATIONS AND $INFO['step'] != FINAL_BLOCKS) 
			Stat::calc_stat_userfiles();

		if($INFO['step'] == 'create_blocks') {
			$disc_size = $Disc_info->size($Paths->disc_name, 'KB', 5, 'noformat');

			$use_part = $OP['use_part']; //$Ui_fields['use_part']->get();
			$part_size_mb = $OP['part_size']; //$Ui_fields['part_size']->get();

			// ТЕСТИРОВАНИЕ!!!!
			/*
			if(file_exists('THIS_DEWELOPER')) {
				if($Paths->disc_name == 'D') {
					$gb50 = 512*2*1000*50;
					$gb1000 = 512*2*1000*1000;
					$disc_size = array('full'=>$gb1000, 'free'=>$gb1000);
				}
			}*/

			if($disc_size['full'] < 0) {
				// если размер диска определить не удалось (скорее всего диск тупо не выбран)
				$sizeKb = 0;
			}
			else {
				// если тестовые файлы
				if($OP['is_test']) {
					$test_files = include "test_blocks.php";
					$sizeKb = (sizeof($test_files) - 1)* $OP['block_size']; // -1 потому что нулевой файл мы не используем
				}
				// стоит галочка использовать часть пространства
				elseif($use_part) {
					$sizeKb =  $part_size_mb* 1024; // потому что там указаны мегабайты
					
					//$log->line("change_part_size проверка выхода лимита");

					// проверка чтобы не выйти за пределы размера диска
					// проверка нужна только если обработка этапа еще не началась
					// для определения начала используем процент выполнения
					if(empty($INFO['procent']) and $sizeKb > $disc_size['free']) {
						//$log->line("вышел за лимит");
						$sizeKb = $disc_size['free'];
					}
				}
				// используем все пространство диска
				else {
					$sizeKb = $disc_size['free']; //; использовать $disc_size['full'] - это не правильно, потому что не учитываются файлы пользователя. Но если указать только свободное место, интерфейс начинает глючить. Поэтому в analize_choice костыль вычитающий из testing_size файлы пользователя

					//$log->line("свободное место: $sizeKb");
				}
			}
			// это очередной костыль. Мы не можем записать в INFO потому что в это время выполняется create_blocks и он перезатрет.
			$OP['stat_testing_size'] = $sizeKb;
		}

		// костыль, влом разбираться почему тут stat_testing_size иногда отсутствует
		if(isset($OP['stat_testing_size'])) $v = $OP['stat_testing_size'];
		else $v = 0;
		$Disc_info->testing_size = $v;
		
		if(!$Disc_info->testing_size)
			return;

		// Информация о диске рядом с графиком
		c('Fm->disk_title')->text = "Диск " . $Paths->disc_name;

		if($OP['use_part']) {
			// прячем поле "Пользовательские файлы"
			$Graphs['disk']->legend->visible(GRD_userfiles, 0);
			// надпись часть диска
			c('Fm->disk_label_part')->visible = 1;

			$sizeMb = $Disc_info->testing_size / 1024;
			
		}
		else {
			// показываем поле "Пользовательские файлы"
			// его значение обновиться само по таймеру через Stat
			$Graphs['disk']->legend->visible(GRD_userfiles, 1);
			// надпись часть диска
			c('Fm->disk_label_part')->visible = 0;

			$disc_size = $Disc_info->size($Paths->disc_name, 'MB', 10, 'noformat');
			$sizeMb = $disc_size['full'];
		}

		//$log->line("Устанавливаем размер диска в графике: $testing_size_mb");
		c('Fm->disk_short_size')->text = $Measure_convert->pretty_size($sizeMb, 'MB', false, 5);

		//$log->line('тестовый размер ' . $testing_size_mb);

		// устанавливаем максимальный размер столбца
		// UP. не надо, графики сами себе лимит поднимают когда новые данные поступают
		if(!$sizeMb) $sizeMb = 5; // при пропадении диска из системы, тут 0 и set_max_value выдает ошибку
		$Graphs['disk']->set_max_value($sizeMb);
		
		// бар общего размера хорошего пространства
		/*
		if($bar_size_2 = get_ui('Fm->stat_result_size_bar_2')) {
			$size = $Disc_info->testing_size;
			$bar_size_2->max = $size;
		}*/
	}


	// проверяем диск чтобы на нем не стояла винда или программа
	// и если так, то ограничеваем обрабатываемое пространство
	// так как отталкиваемся от реального свободного пространства на диске, запускать имеет смысл только при пересчете на этапе запись файлов.
	static function check_drive() {
		GLOBAL $Ui_fields, $Disc_info, $Paths, $OP, $log, $Lang;

		$need_thread_stop = false;
		$msg = '';

		// если параметр часть диска превышает свободное пространство, убираем его и используем весь диск
		// нужно вызывать именно здесь а не в Ui_create_blocks::change_part_size или в before_run
		$disc_size = $Disc_info->size($Paths->disc_name, 'KB', 5, 'noformat');
		if($OP['use_part']) {
			if($OP['part_size'] >= $disc_size['free']/1024) {
				$log->line('превышает, берем весь диск');
				$Ui_fields['use_part']->set(0);
			}
		}


		// если windows на этом диске
		// определяем диск с которого загружены, потому то windows на другом диске в этом случае можно проверять полностью а не частично
		if($tpath = trim(winLocalPath(CSIDL_PROGRAMS)))
			 $load_system_disc = substr($tpath, 0, 1);
		else $load_system_disc = 'c'; // на случай если CSIDL_PROGRAMS не сработает

		if(strtolower($load_system_disc) == strtolower($Paths->disc_name) and file_exists($Paths->disc_name.':\\Windows')) {
			$type = 'windows';
			$need_free_space = self::$windows_drive_min_free_space;
		}
		// если программа запущена на этом диске
		elseif(stripos($GLOBALS['progDir'], $Paths->disc_name) === 0) {
			$type = 'prog';
			$need_free_space = self::$prog_drive_min_free_space;
		}
		else {
			// все окей, 
			return true;
		}

		$disc_size = $Disc_info->size($Paths->disc_name, 'BT', 5, 'noformat');

		$max_part_size = ($disc_size['free'] - 1024*1024*1024*$need_free_space) / 1024 / 1024; // 1024/1024 это перевод байт к мегабайты

		$log->line("$type. max_part_size: $max_part_size");
			
		if($OP['use_part'] == 0 OR $OP['part_size'] > $max_part_size ) {
			if($max_part_size <= 0) {
				$msg = $Lang->get("{$type}_drive_nospace");
				$msg = str_replace('[space]', $need_free_space, $msg);
				$need_thread_stop = true;
			}
			else {
				$msg = $Lang->get("{$type}_drive_use_part");
			}
		}
		else
			return true;

		messageDlg($msg, mtWarning, MB_OK);

		if($need_thread_stop) {
			// снаружи тормознем поток
			return false;
		}

		$max_part_size = round_nobug($max_part_size);
		$Ui_fields['part_size']->set($max_part_size);
		$Ui_fields['use_part']->set(1);

		//print $OP['part_size'];

		return true;
	}



	static function after_run_complete() {
		GLOBAL $INFO, $log;
 
		Ui_read_blocks::$autorun = true;
	}


	static function before_run() {
		GLOBAL $INFO, $Paths, $Disc_unid, $setting, $Disc_info, $OP, $Ui_fields;

		if(!Ui_create_blocks::check_setting())
			return false;

		// нужно и здесь запустить, вдруг настройки не правильные, тогда программа встанет на этапе чтения
		if(!Ui_read_blocks::check_setting())
			return false;

		// генерируем unid
		if(!$Paths->disc_unid)
			$Paths->disc_unid = $Disc_unid->generate();
		
		// создаем папки если не созданы
		// важно это сделать здесь потому что create_foldes использует unid
		// эта проверка нужна для совместимости с дисками у которых файлы находятся прямо в основной папке. Это для старых версий.
		if(empty($INFO['create_blocks.mode_onli_cleaning'])) 
			if(!$Paths->create_folders())
				return false;

		// после созданий директорий на диске, иначе если диск нулевый, сохранится в пустоту
		//$setting->save();

		// если режим восстановления, но файл с записями о блоках не найден, режим восстановления отключаем
		if(!empty($INFO['recovery_fname'])) {
			if(!file_exists($Paths->data_file('create_blocks.txt'))) {
				print "Файла с информацией о тестовых файлах не найден, начинаем все заново.";
				//$INFO['recovery_fname'] = 0;
			}

			return true;
		}

		return true;
	}

	// вызывается так же на этапе создания, чтобы посреди обработки программа не остановилась
	static function check_setting() {
		GLOBAL $log, $GOP, $OP, $Ui_fields, $Lang, $Graphs, $Disc_info, $Analize_autochoice;

		// проверяем чтобы скорости были установленны правильно
		$block_size = $Ui_fields['block_size'];
		$sect_size = $Ui_fields['sect_size'];
		$use_part = $Ui_fields['use_part'];

		// block_size 
		$max_block_size_kb = 100*1024;
		$error  = '';

		if($block_size->get() > $max_block_size_kb) {
			$error .= "Параметр $block_size->title не может быть больше " . ($max_block_size_kb/1024) . " MB\n\n";
			$block_size->set($max_block_size_kb);
		}

		// sect_size должна быть меньше block_size
		// проверку на остаток делаем в самом низу
		if($sect_size->get() > $block_size->get()) {
			$error .= "Параметр $sect_size->title не может быть больше $block_size->title \n\n";
			$sect_size->set($block_size->get());
		}

		if($error) {
			$text = $error . "Программа подкорректировала неправильные настройки.";
			messageDlg($text, mtInformation, MB_OK);
			return false;
		}	

		// после все проверок, потому что мы не можем его подобрать сами
		// sect_size должно делить block_size без остатка
		if($block_size->get() % $sect_size->get() != 0) {
			$text = "Параметр $sect_size->title должно делить $block_size->title без остатка. \n\n";
			messageDlg($text, mtInformation, MB_OK);
			return false;
		}

		// кривая но хоть какая то проверка чтобы размер блока был меньше тестируемого пространства
		if($OP['use_part'] && $OP['part_size']*1024 < $block_size->get()) {
			$text = "Параметр $block_size->title должно быть меньше $use_part->title. \n\n";
			messageDlg($text, mtInformation, MB_OK);
			return false;
		}


		return true;
	}



	static function info_right_panel($init=false) {
		GLOBAL $Lang, $INFO, $OP, $log;

		$text =c('Fm->info_create_blocks_text');
		$text_head = c('Fm->info_create_blocks_head');
		//$text_panel =c('info_create_blocks_text_panel');
		$panel = c('Fm->info_create_blocks');
		$msg_use_part = c('Fm->msg_create_blocks_use_part');

		if($init) {
			$text->w = $panel->w - 15 - 10;
			$text->caption = $Lang->get('create_blocks_tips');

			$text_head->x = 0;
			$text_head->bevelOuter = bvNone;

			// $text_panel->w = $text->w;
			// $text_panel->h = $text->h;

			$panel->h = $text->h + $text->y + 5;
			$panel->bevelOuter = bvNone;
			$panel->x = 0;
			$panel->toFront(); // чтобы перекрыл графики наверняка, иначе ссылки не работают

			$msg_use_part->visible = false;
			$msg_use_part->bevelOuter = bvNone;
			$msg_use_part->y = 0;
			$msg_use_part->x = 0;

			return true;
		}

		//$log->line('info_right_panel start');

		if(!isset($OP['use_part']))
			return true;

		//$log->line('info_right_panel worked');

		if($OP['use_part']) {
			//$log->line('info_right_panel use_part true');

			$text_head->y = $msg_use_part->h +5;

			$msg_use_part->visible = false; // баг DS
			$msg_use_part->visible = true;
			
			$panel->h = $text->h + $text->y + 5 + $msg_use_part->h;
		}
		else {
			//$log->line('info_right_panel use_part false');
			$text_head->y = 0;
			

			// баг DS
			// Из-за того что основная панель скрыта в момент когда первый раз устанавливаем visible = false, а при последующих установка visible = false не срабатывают потому что уже и так там false
			// поэтому мы сначала в true, а потом false чтобы точно сработало
			$msg_use_part->visible = true;
			$msg_use_part->visible = false;

			//$log->line('visible: ' . var_dump_str($msg_use_part->visible));

			$panel->h = $text->h + $text->y + 5;
		}

		$text->y = $text_head->y + $text_head->h;


	}




	// === Не используем потому что нет необходимости ===
	// проверка перед запуском потока
	static function NOT_USE_before_run() {
		GLOBAL $Is_demo, $OP, $Paths, $Disc_info;
		
		$disc_size = $Disc_info->size($Paths->disc_name, 'MB', 2, 'noformat');
		$small_disk = 10*1024;
		// если демо, диск большой и не стоит галочка "использовать часть диска"
		if($Is_demo && $disc_size['full'] > $small_disk && !$OP['use_part']) {
			$title = "Можно сократить время на записи файлов!";
			$message = "$title

У вас демо версия, выбран диск большого размера, но не стоит галочка
'использовать часть диска'. Эта функция позволяет избежать создания
лишних тестовых файлов. Все равно, в демо версии будет найден только
1 GB хорошего пространства.

Рекомендуется выставить ограничение в $small_disk MB.
			";

			$dialog = c("Fm->custom_dialog");
			$dialog->buttons = "Вернуться \n и изменить \r\n Использовать \n текущие настройки";
			$dialog->buttonsHeight = 37;
			$dialog->buttonsWidth = 115;
			$dialog->formCaption = $title;
			$dialog->title = $message; // да, title это текст в диалоге :)
			$selected = $dialog->execute();

			if($selected == 1) return true; // нажата кнопка использовать текущие настройки
			else return false; // нажата кнопка исправить или просто закрыта крестиком
		}

		return true;
	}

}

?>