// returns true if data is valid (was valid from the start or successfull repair was performed)
        public bool Perform(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken)
        {
            _lowestVersionWithContentId = Context.App.GetLowestVersionWithContentId(cancellationToken);

            for (int attempt = 1; attempt <= RepeatCount; ++attempt)
            {
                DebugLogger.Log("Running integrity check, attempt " + attempt + " of " + RepeatCount);

                if (PerformInternal(cancellationToken))
                {
                    return(true);
                }
            }

            // retry count reached, let's check for the last time if data is ok, but without repairing
            int installedVersionId   = Context.App.GetInstalledVersionId();
            VersionIntegrity results = CheckIntegrity(cancellationToken, installedVersionId);
            var filesNeedFixing      = FilesNeedFixing(results);

            if (filesNeedFixing.Count() == 0)
            {
                DebugLogger.Log("No missing or invalid size files.");
                return(true);
            }


            DebugLogger.LogError("Still have corrupted files after all fixing attempts");
            return(false);
        }
        // returns true if there was no integrity errors, false if there was and repair was performed
        private bool PerformInternal(PatchKit.Unity.Patcher.Cancellation.CancellationToken cancellationToken)
        {
            int installedVersionId = Context.App.GetInstalledVersionId();

            VersionIntegrity results = CheckIntegrity(cancellationToken, installedVersionId);
            var filesNeedFixing      = FilesNeedFixing(results);

            if (filesNeedFixing.Count() == 0)
            {
                DebugLogger.Log("No missing or invalid size files.");
                return(true);
            }

            // need to collect some data about the application to calculate the repair cost and make decisions

            int latestVersionId = Context.App.GetLatestVersionId(true, cancellationToken);

            AppContentSummary installedVersionContentSummary
                = Context.App.RemoteMetaData.GetContentSummary(installedVersionId, cancellationToken);

            AppContentSummary latestVersionContentSummary
                = Context.App.RemoteMetaData.GetContentSummary(latestVersionId, cancellationToken);

            bool isNewVersionAvailable = installedVersionId < latestVersionId;

            long contentSize = isNewVersionAvailable
                ? latestVersionContentSummary.Files.Sum(f => f.Size)
                : installedVersionContentSummary.Files.Sum(f => f.Size);

            double repairCost = CalculateRepairCost(installedVersionContentSummary, filesNeedFixing);

            // increasing repair costs that reinstallation will be done for 1/3 of the content size
            repairCost *= IncreaseRepairCost;


            if (_lowestVersionWithContentId > installedVersionId)
            {
                DebugLogger.Log(
                    "Repair is impossible because lowest version with content id is "
                    + _lowestVersionWithContentId +
                    " and currently installed version id is "
                    + installedVersionId +
                    ". Reinstalling.");

                ReinstallContent(cancellationToken);
            }
            else if (repairCost < contentSize)
            {
                DebugLogger.Log(string.Format("Repair cost {0} is smaller than content cost {1}, repairing...", repairCost, contentSize));
                IAppUpdaterStrategy repairStrategy = _strategyResolver.Create(StrategyType.Repair, Context);
                repairStrategy.Update(cancellationToken);
            }
            else
            {
                DebugLogger.Log(string.Format("Content cost {0} is smaller than repair {1}. Reinstalling.", contentSize, repairCost));
                ReinstallContent(cancellationToken);
            }

            return(false);
        }
        private IEnumerable <FileIntegrity> FilesNeedFixing(VersionIntegrity results)
        {
            var missingFiles     = results.Files.Where(f => f.Status == FileIntegrityStatus.MissingData);
            var invalidSizeFiles = results.Files.Where(f => f.Status == FileIntegrityStatus.InvalidSize);

            return(missingFiles.Concat(invalidSizeFiles));
        }