// currently, this method checks if there if a file already exists, and if so, assumes it is valid.
// ideally, a check should be made to ensure the saved SSD state matches with the state of the current global parameters
void Experiment::calibrate_and_save(Workload_Definition* workload, string name, int num_IOs, bool force) {
	//string file_name = base_folder + "calibrated_state.txt";
	string file_name = base_folder + name;
	std::ifstream ifile(file_name.c_str());
	if (ifile && !force) {
		return; // file exists
	}
	StatisticsGatherer::set_record_statistics(false);
	//StatisticsGatherer::get_global_instance()->init();
	Thread::set_record_internal_statistics(false);
	VisualTracer::init();
	//Free_Space_Meter::init();
	//Free_Space_Per_LUN_Meter::init();
	printf("Creating calibrated SSD state.\n");
	OperatingSystem* os = new OperatingSystem();
	os->set_num_writes_to_stop_after(num_IOs);
	vector<Thread*> init_threads = workload->generate_instance();
	os->set_threads(init_threads);
	os->set_progress_meter_granularity(1000);

	os->run();
	os->get_ssd()->execute_all_remaining_events();
	save_state(os, file_name);
	//StatisticsGatherer::get_global_instance()->print();
	//Free_Space_Meter::print();
	//Free_Space_Per_LUN_Meter::print();
	delete os;
}
OperatingSystem* Experiment::load_state(string name) {
	string file_name = base_folder + name;
	printf("loading calibration file:  %s\n", file_name.c_str());
	std::ifstream file(file_name.c_str());
	boost::archive::text_iarchive ia(file);
	ia.register_type<FtlImpl_Page>( );
	ia.register_type<Block_manager_parallel>();
	ia.register_type<Sequential_Locality_BM>( );
	ia.register_type<File_Manager>( );
	ia.register_type<Simple_Thread>( );
	ia.register_type<Random_IO_Pattern>( );
	ia.register_type<Sequential_IO_Pattern>( );
	ia.register_type<WRITES>( );
	ia.register_type<TRIMS>( );
	ia.register_type<READS>( );
	ia.register_type<READS_OR_WRITES>();
	ia.register_type<Asynchronous_Random_Writer>();
	ia.register_type<Asynchronous_Random_Reader>();

	ia.register_type<MTRand>();
	ia.register_type<MTRand_closed>();
	ia.register_type<MTRand_open>();
	ia.register_type<MTRand53>();

	OperatingSystem* os;
	ia >> os;
	vector<Thread*> threads;
	ia >> threads;
	Individual_Threads_Statistics::init();
	for (auto t : threads) {
		//Individual_Threads_Statistics::register_thread(t, "");
	}
	os->set_threads(threads);
	//os->init_threads();
	IOScheduler* scheduler = os->get_ssd()->get_scheduler();
	scheduler->init();
	Block_manager_parent* bm = Block_manager_parent::get_new_instance();
	bm->copy_state(scheduler->get_bm());
	delete scheduler->get_bm();
	scheduler->set_block_manager(bm);
	Migrator* m = scheduler->get_migrator();
	m->set_block_manager(bm);
	Garbage_Collector* gc = m->get_garbage_collector();
	gc->set_block_manager(bm);

	return os;
}