// UpdateToNewSkiaVersion runs a series of commands to update the fuzzer to a new Skia Version.
// It will stop the Generator, pause the Aggregator, update to the
// new version, re-scan all previous fuzzes and then start the Generator and the Aggregator
// again.  It re-uses the Aggregator pipeline to do the re-analysis.
func (v *VersionUpdater) UpdateToNewSkiaVersion(newHash string) (*vcsinfo.LongCommit, error) {
	oldHash := config.Generator.SkiaVersion.Hash
	// stop afl-fuzz
	StopBinaryGenerator()

	// sync skia to version, which sets config.Generator.SkiaVersion
	if err := common.DownloadSkia(newHash, config.Generator.SkiaRoot, &config.Generator); err != nil {
		return nil, fmt.Errorf("Could not sync skia to %s: %s", newHash, err)
	}

	// download all bad and grey fuzzes
	badFuzzNames, greyFuzzNames, err := v.downloadAllBadAndGreyFuzzes(oldHash, config.Aggregator.BinaryFuzzPath)
	if err != nil {
		return nil, fmt.Errorf("Problem downloading all previous fuzzes: %s", err)
	}
	glog.Infof("There are %d badFuzzNames and %d greyFuzzNames to rescan.", len(badFuzzNames), len(greyFuzzNames))
	// This is a soft shutdown, i.e. it waits for aggregator's queues to be empty
	v.agg.ShutDown()
	if err := ClearBinaryGenerator(); err != nil {
		return nil, fmt.Errorf("Could not remove previous afl-fuzz results: %s", err)
	}

	if err := v.agg.RestartAnalysis(); err != nil {
		return nil, fmt.Errorf("Had problem restarting analysis/upload chain: %s", err)
	}
	// Reanalyze and reupload the fuzzes, making a bug on regressions.
	glog.Infof("Reanalyzing bad fuzzes")
	v.agg.MakeBugOnBadFuzz = false
	v.agg.UploadGreyFuzzes = true
	for _, name := range badFuzzNames {
		v.agg.ForceAnalysis(name)
	}
	v.agg.WaitForEmptyQueues()
	glog.Infof("Reanalyzing grey fuzzes")
	v.agg.MakeBugOnBadFuzz = true
	for _, name := range greyFuzzNames {
		v.agg.ForceAnalysis(name)
	}
	v.agg.WaitForEmptyQueues()
	v.agg.MakeBugOnBadFuzz = false
	v.agg.UploadGreyFuzzes = false
	glog.Infof("Done reanalyzing")

	// redownload samples (in case any are new)
	if err := DownloadBinarySeedFiles(v.storageClient); err != nil {
		return nil, fmt.Errorf("Could not download binary seed files: %s", err)
	}
	// change GCS version to have the current be up to date (fuzzer-fe will need to see that with its polling)
	if err := v.replaceCurrentSkiaVersionWith(oldHash, config.Generator.SkiaVersion.Hash); err != nil {
		return nil, fmt.Errorf("Could not update skia error: %s", err)
	}

	// restart afl-fuzz
	return config.Generator.SkiaVersion, StartBinaryGenerator()
}
// HandleCurrentVersion sets the current version of Skia to be the specified value and calls
// LoadFreshFromGoogleStorage.  If there is an AST still being generated, it will block until
// that completes.
func (v *VersionUpdater) HandleCurrentVersion(currentHash string) (*vcsinfo.LongCommit, error) {
	// Make sure skia version is at the proper version.  This also sets config.Frontend.SkiaVersion.
	if err := common.DownloadSkia(currentHash, config.FrontEnd.SkiaRoot, &config.FrontEnd); err != nil {
		return nil, fmt.Errorf("Could not update Skia to current version %s: %s", currentHash, err)
	}
	// Block until finder is built.
	v.finderBuilding.Lock()
	defer v.finderBuilding.Unlock()
	v.gsLoader.SetFinder(v.finders[currentHash])
	if err := v.gsLoader.LoadFreshFromGoogleStorage(); err != nil {
		return nil, fmt.Errorf("Had problems fetching new fuzzes from GCS: %s", err)
	}
	v.syncer.Refresh()
	return config.FrontEnd.SkiaVersion, nil
}
// HandlePendingVersion updates the frontend's copy of Skia to the specified pending version
// and begins building the AST for the pending version on a background goroutine.
func (v *VersionUpdater) HandlePendingVersion(pendingHash string) (*vcsinfo.LongCommit, error) {
	pending := versionHolder{}
	if err := common.DownloadSkia(pendingHash, config.FrontEnd.SkiaRoot, &pending); err != nil {
		return nil, fmt.Errorf("Could not update Skia to pending version %s: %s", pendingHash, err)
	}

	// start generating AST in the background.
	go func() {
		v.finderBuilding.Lock()
		defer v.finderBuilding.Unlock()
		if finder, err := functionnamefinder.NewSync(); err != nil {
			glog.Errorf("Error building FunctionNameFinder at version %s: %s", pendingHash, err)
			return
		} else {
			glog.Infof("Successfully rebuilt AST for Skia version %s", pendingHash)
			v.finders[pendingHash] = finder
		}
	}()

	return pending.version, nil
}