public static int GetNumOfUnfoundProjsByRecipe(RecipeDef recipe)
        {
            int result = 0;

            if (recipe.HasModExtension <ResearchDefModExtension>())
            {
                var temp = UndiscoveredResearchList.MainResearchDict.Where(item => item.Value.ResearchSize == recipe.GetModExtension <ResearchDefModExtension>().ResearchSize &&
                                                                           item.Value.ResearchTypes.Any(x => x == recipe.GetModExtension <ResearchDefModExtension>().researchTypes[0]));

                foreach (KeyValuePair <string, ImmersiveResearchProject> p in temp)
                {
                    if (p.Value.IsDiscovered == false)
                    {
                        result++;
                    }
                }
            }

            return(result);
        }
        public override void DoWindowContents(Rect inRect)
        {
            string selectedExp          = "";
            string selectedExpType      = "";
            string recipeDescription    = "Invalid Recipe Combination.";
            bool   isExperimentSelected = false;

            //title
            Rect rect1 = new Rect(inRect.center.x - 80f, inRect.yMin + 35f, 200f, 74f);

            Text.Font = GameFont.Medium;
            Widgets.Label(rect1, "Experiment Setup");

            // Exit button
            Rect exitRect = new Rect(inRect.xMax - 50f, inRect.yMin + 5f, 50f, 30f);

            if (Widgets.ButtonText(exitRect, "Exit"))
            {
                this.Close();
            }

            // explain text
            Rect rect2 = new Rect(inRect);

            rect2.yMin  = rect1.yMax;
            rect2.yMax -= 38f;
            Text.Font   = GameFont.Small;
            Widgets.Label(rect2, "You can perform different types and sizes of experiments here, determining what kind of research you can unlock. The size of the research helps increase your chances of obtaining better research.");

            // 'select experiment' list
            Rect AddExpRect = new Rect(rect2);

            AddExpRect.width   = 550f;//275f;
            AddExpRect.height /= 2;
            AddExpRect.y      += 70f;
            AddExpRect.x      += 370f;

            //ExpList.DrawTextList(AddExpRect, _experimentNames, "Experiment Types");
            selectedExp = ExpList.SelectedEntry != null ? ExpList.SelectedEntry.EntryLabel : "None Selected";
            ExpList.DrawImageList(AddExpRect, "Experiment Types");

            // 'select type' list
            Rect AddExpTypeRect = new Rect(AddExpRect);

            AddExpTypeRect.x -= 310f;
            //AddExpTypeRect.ContractedBy(20f);
            AddExpTypeRect.width = 550f;//275f;
            //AddExpTypeRect.y += 20f;

            //ExpTypeList.DrawTextList(AddExpTypeRect, _experimentTypes, "Experiment Sizes");
            selectedExpType = ExpTypeList.SelectedEntry != null ? ExpTypeList.SelectedEntry.EntryLabel : "";
            ExpTypeList.DrawImageList(AddExpTypeRect, "Experiment Sizes");

            // need to get defName of recipe from this point
            _selectedExperimentDefName = selectedExpType + selectedExp;

            if (!(from t in DefDatabase <RecipeDef> .AllDefsListForReading where t.defName == _selectedExperimentDefName select t).TryRandomElement(out RecipeDef finalDef))
            {
                //Log.Error("Def not found");
                isExperimentSelected = false;
            }
            else
            {
                isExperimentSelected = true;
                _selectedRecipe      = finalDef;
                recipeDescription    = _selectedRecipe.description;
            }

            // text explaining selection, e.g. 'Small Biological Research Project - will help unlock biological research'
            Rect rect3 = new Rect(inRect.position, rect2.size);

            rect3.x   = inRect.center.x - 150f;
            rect3.y   = inRect.yMax - 100f;
            Text.Font = GameFont.Medium;
            Widgets.Label(rect3, _selectedExperimentDefName);

            Text.Font = GameFont.Small;
            Rect rect4 = rect3;

            rect4.x = inRect.x;
            rect4.y = inRect.yMax - 210f;
            Widgets.Label(rect4, recipeDescription);

            Rect rect5 = rect4;

            rect5.x = inRect.x;
            rect5.y = inRect.yMax - 160f;
            if (_selectedRecipe.HasModExtension <ResearchDefModExtension>())
            {
                Widgets.Label(rect5, "Potential Research Projects left to discover: " + LoreComputerHarmonyPatches.GetNumOfUnfoundProjsByRecipe(_selectedRecipe));
            }

            // confirm button
            Rect rect6 = new Rect(inRect.center.x - 100f, inRect.yMax - 35f, 150f, 29f);

            if (Widgets.ButtonText(rect6, "Confirm Experiment"))
            {
                if (isExperimentSelected == true)
                {
                    // TODO: change the assumption above to something less naive
                    Bill       newExpBill = (Bill_ProductionWithUft)_selectedRecipe.MakeNewBill();
                    Experiment newExp     = MakeNewExperiment();
                    _selectedTable.ExpStack.AddExperiment(newExp);
                    _selectedTable.ExpStack.AddExperimentWithBill(newExpBill);
                    isExperimentSelected = false;
                    this.Close();
                }
                else
                {
                    Dialog_MessageBox window = Dialog_MessageBox.CreateConfirmation("No Experiment Selected", delegate
                    {
                    }, destructive: true);
                    Find.WindowStack.Add(window);
                }
            }
            // exit button?
        }
        /// <summary>
        /// Generates a copy of a given recipe with the provided ingredient replaced with another, at the given ratio.
        ///  Will return null if recipe requires none of the original ingredient.
        /// </summary>
        private RecipeDef TryMakeRecipeVariant(RecipeDef recipeOriginal, RecipeVariantType variant, ThingDef originalIngredient, ThingDef replacementIngredient, float replacementRatio, float workAmountMultiplier)
        {
            // check original recipe for the replaced ingredient, copy other ingredients
            var resourceCountRequired = 0f;
            var newIngredientList     = new List <IngredientCount>(recipeOriginal.ingredients);

            foreach (var ingredientCount in newIngredientList)
            {
                if (ingredientCount.filter.Allows(originalIngredient))
                {
                    resourceCountRequired = ingredientCount.GetBaseCount();
                    newIngredientList.Remove(ingredientCount);
                    break;
                }
            }
            if (resourceCountRequired < float.Epsilon)
            {
                return(null);
            }

            var recipeCopy = CloneObject(recipeOriginal);

            recipeCopy.defName   = $"{recipeOriginal.defName}_{replacementIngredient.defName}";
            recipeCopy.shortHash = 0;
            InjectedDefHasher.GiveShortHashToDef(recipeCopy, typeof(RecipeDef));
            // clone our extension to avoid polluting the original def
            recipeCopy.modExtensions = recipeCopy.modExtensions?.Select(e => e is ICloneable i ? (DefModExtension)i.Clone() : e).ToList();
            if (!recipeOriginal.HasModExtension <MakeRecipeVariants>())
            {
                // mark original as a variant, as well
                recipeOriginal.modExtensions = recipeOriginal.modExtensions ?? new List <DefModExtension>();
                recipeOriginal.modExtensions.Add(new MakeRecipeVariants());
            }

            // mark the copy as variant
            var variantExtension = recipeCopy.GetModExtension <MakeRecipeVariants>();

            if (variantExtension == null)
            {
                variantExtension         = new MakeRecipeVariants();
                recipeCopy.modExtensions = recipeCopy.modExtensions ?? new List <DefModExtension>();
                recipeCopy.modExtensions.Add(variantExtension);
            }
            variantExtension.Variant |= variant;

            // copy non-replaced ingredients over to the new filter
            var newFixedFilter = new ThingFilter();

            foreach (var allowedThingDef in recipeOriginal.fixedIngredientFilter.AllowedThingDefs)
            {
                if (allowedThingDef != originalIngredient)
                {
                    newFixedFilter.SetAllow(allowedThingDef, true);
                }
            }
            newFixedFilter.SetAllow(replacementIngredient, true);
            recipeCopy.fixedIngredientFilter   = newFixedFilter;
            recipeCopy.defaultIngredientFilter = null;

            // add the replacement ingredient
            var replacementIngredientFilter = new ThingFilter();

            replacementIngredientFilter.SetAllow(replacementIngredient, true);
            var replacementCount = new IngredientCount {
                filter = replacementIngredientFilter
            };

            replacementCount.SetBaseCount(Mathf.Round(resourceCountRequired * replacementRatio));
            newIngredientList.Add(replacementCount);
            recipeCopy.ingredients = newIngredientList;

            // multiply work amount
            recipeCopy.workAmount = recipeOriginal.workAmount * workAmountMultiplier;

            recipeCopy.ResolveReferences();
            return(recipeCopy);
        }