// Evaluation is MW, VCrew, Aerospace, MechTech, MedTech
        public static int CompareByType(CrewDetails details1, CrewDetails details2)
        {
            // Check nullity
            if (details1 == null && details2 == null)
            {
                return(0);
            }
            else if (details1 != null && details2 == null)
            {
                return(-1);
            }
            else if (details1 == null && details2 != null)
            {
                return(1);
            }

            // Check type
            int detailsType1 = details1.TypeSortPriority();
            int detailsType2 = details2.TypeSortPriority();

            if (detailsType1 > detailsType2)
            {
                return(1);
            }
            else if (detailsType2 > detailsType1)
            {
                return(-1);
            }

            return(0);
        }
        public static PilotDef UpgradeRonin(StarSystem starSystem, PilotDef baseRoninDef)
        {
            CrewDetails details = ReadRoninTags(baseRoninDef);

            ModState.UpdateOrCreateCrewDetails(baseRoninDef, details);
            return(baseRoninDef);
        }
        // -- COMPARISON FUNCTIONS --

        // Evaluation is highest skills == lowest

        // Value represents either all combined skills -or- the support points generated
        public static int CompareByValue(CrewDetails details1, CrewDetails details2)
        {
            // Check nullity
            if (details1 == null && details2 == null)
            {
                return(0);
            }
            else if (details1 != null && details2 == null)
            {
                return(1);
            }
            else if (details1 == null && details2 != null)
            {
                return(-1);
            }

            // Check skill
            if (details1.Value > details2.Value)
            {
                return(-1);
            }
            else if (details2.Value > details1.Value)
            {
                return(1);
            }

            return(0);
        }
        public static CrewDetails GenerateDetailsForVanillaMechwarrior(PilotDef basePilotDef, bool isFounder = false)
        {
            (FactionValue favored, FactionValue hated) = GenerateCrewFactions(null);
            CrewDetails details = new CrewDetails(basePilotDef, CrewType.MechWarrior, favored, hated);

            ModState.UpdateOrCreateCrewDetails(basePilotDef, details);
            Mod.Log.Info?.Write($" -- pilotDef associated with GUID: {details.GUID}");

            return(details);
        }
        // === COMBAT CREWS ===
        public static PilotDef GenerateMechWarrior(StarSystem starSystem)
        {
            PilotDef crew = GenerateSkilledCrew(starSystem, true);

            Mod.Log.Debug?.Write($"CREATED MECHWARRIOR CREW");

            (FactionValue favored, FactionValue hated) = GenerateCrewFactions(starSystem);
            CrewDetails details = new CrewDetails(crew, CrewType.MechWarrior, favored, hated);

            ModState.UpdateOrCreateCrewDetails(crew, details);
            return(crew);
        }
        public static int UsedBerths(IEnumerable <Pilot> pilots)
        {
            int used = 0;

            foreach (Pilot pilot in pilots)
            {
                CrewDetails details = ModState.GetCrewDetails(pilot.pilotDef);
                used += details.Size;
            }

            return(used);
        }
        public static PilotDef GenerateVehicleCrew(StarSystem starSystem)
        {
            PilotDef crew = GenerateSkilledCrew(starSystem, false);

            Mod.Log.Debug?.Write($"CREATED VEHICLE CREW");

            (FactionValue favored, FactionValue hated) = GenerateCrewFactions(starSystem);
            CrewDetails details = new CrewDetails(crew, CrewType.VehicleCrew, favored, hated);

            ModState.UpdateOrCreateCrewDetails(crew, details);

            return(crew);
        }
        public static PilotDef GenerateAerospaceCrew(StarSystem starSystem)
        {
            int    callsignIdx = Mod.Random.Next(0, Mod.CrewNames.Aerospace.Count - 1);
            string newCallsign = Mod.CrewNames.Aerospace[callsignIdx];

            PilotDef def = GenerateSkillessCrew(starSystem, newCallsign, out int crewSize, out int crewSkill);

            // Before returning, initialize the cache value
            (FactionValue favored, FactionValue hated) = GenerateCrewFactions(starSystem);
            CrewDetails details = new CrewDetails(def, CrewType.AerospaceWing, favored, hated, crewSize, crewSkill);

            ModState.UpdateOrCreateCrewDetails(def, details);

            return(def);
        }
        public static bool CanHireMoreCrewOfType(CrewDetails details)
        {
            CrewOpts  crewOpt  = null;
            Statistic crewStat = null;

            if (details.IsAerospaceCrew)
            {
                crewOpt  = Mod.Config.HiringHall.AerospaceWings;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_Aerospace);
            }
            if (details.IsMechTechCrew)
            {
                crewOpt  = Mod.Config.HiringHall.MechTechCrews;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_MechTechs);
            }
            if (details.IsMechWarrior)
            {
                crewOpt  = Mod.Config.HiringHall.MechWarriors;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_MechWarriors);
            }
            if (details.IsMedTechCrew)
            {
                crewOpt  = Mod.Config.HiringHall.MedTechCrews;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_MedTechs);
            }
            if (details.IsVehicleCrew)
            {
                crewOpt  = Mod.Config.HiringHall.VehicleCrews;
                crewStat = ModState.SimGameState.CompanyStats.GetStatistic(ModStats.CrewCount_VehicleCrews);
            }

            int countOfType = crewStat == null ? 0 : crewStat.Value <int>();

            Mod.Log.Debug?.Write($"Comparing countOfType: {countOfType} vs. limit: {crewOpt.MaxOfType}");

            return(crewOpt.MaxOfType == -1 || countOfType < crewOpt.MaxOfType);
        }
        private static CrewDetails ReadRoninTags(PilotDef roninDef)
        {
            //CrewDetails crewDetails = new CrewDetails(); BAD, SERIALIZATION ONLY!
            Mod.Log.Debug?.Write($"Building CrewDetails for Ronin: {roninDef.Description.Name}_{roninDef.GUID}");

            CrewType     type = CrewType.MechWarrior;
            FactionValue favored = null, hated = null;
            int          skill = 0, size = 0;
            SalaryConfig salaryCfg = new SalaryConfig();
            bool         isFounder = false;

            foreach (string tag in roninDef.PilotTags)
            {
                // Check types
                if (tag.Equals(ModTags.Tag_CrewType_Aerospace, StringComparison.InvariantCultureIgnoreCase))
                {
                    Mod.Log.Debug?.Write($" -- found type == Aerospace");
                    type = CrewType.AerospaceWing;
                }
                if (tag.Equals(ModTags.Tag_CrewType_MechTech, StringComparison.InvariantCultureIgnoreCase))
                {
                    Mod.Log.Debug?.Write($" -- found type == MechTech");
                    type = CrewType.MechTechCrew;
                }
                if (tag.Equals(ModTags.Tag_CrewType_MedTech, StringComparison.InvariantCultureIgnoreCase))
                {
                    Mod.Log.Debug?.Write($" -- found type == MedTech");
                    type = CrewType.MedTechCrew;
                }
                if (tag.Equals(ModTags.Tag_CU_Vehicle_Crew, StringComparison.InvariantCultureIgnoreCase))
                {
                    Mod.Log.Debug?.Write($" -- found type == Vehicle");
                    type = CrewType.VehicleCrew;
                }

                // Check factions
                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Faction_Favored, StringComparison.InvariantCultureIgnoreCase))
                {
                    string factionDefId = tag.Substring(ModTags.Tag_Prefix_Ronin_Faction_Favored.Length);
                    Mod.Log.Debug?.Write($" -- found favored faction defId: {factionDefId}");

                    foreach (FactionValue factionVal in Mod.Config.Attitude.FavoredFactionCandidates)
                    {
                        if (factionVal.FactionDefID.Equals(factionDefId, StringComparison.InvariantCultureIgnoreCase))
                        {
                            favored = factionVal;
                        }
                    }

                    if (favored == null)
                    {
                        Mod.Log.Warn?.Write($"Could not map favored factionDefId: {factionDefId} to a configured faction! Skipping!");
                    }
                }

                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Faction_Hated, StringComparison.InvariantCultureIgnoreCase))
                {
                    string factionDefId = tag.Substring(ModTags.Tag_Prefix_Ronin_Faction_Hated.Length);
                    Mod.Log.Debug?.Write($" -- found hated faction defId: {factionDefId}");

                    foreach (FactionValue factionVal in Mod.Config.Attitude.HatedFactionCandidates)
                    {
                        if (factionVal.FactionDefID.Equals(factionDefId, StringComparison.InvariantCultureIgnoreCase))
                        {
                            hated = factionVal;
                        }
                    }

                    if (hated == null)
                    {
                        Mod.Log.Warn?.Write($"Could not map hated factionDefId: {factionDefId} to a configured faction! Skipping!");
                    }
                }

                // Check size and skill tags
                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Support_Size, StringComparison.InvariantCultureIgnoreCase))
                {
                    string tagS = tag.Substring(ModTags.Tag_Prefix_Ronin_Support_Size.Length);
                    try
                    {
                        size = Int32.Parse(tagS, CultureInfo.InvariantCulture);
                        Mod.Log.Debug?.Write($" -- found size: {size}");
                    }
                    catch (Exception e)
                    {
                        Mod.Log.Warn?.Write(e, $"Failed to read size: {tagS} as an integer value, skipping!");
                    }
                }
                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Support_Skill, StringComparison.InvariantCultureIgnoreCase))
                {
                    string tagS = tag.Substring(ModTags.Tag_Prefix_Ronin_Support_Skill.Length);
                    try
                    {
                        skill = Int32.Parse(tagS, CultureInfo.InvariantCulture);
                        Mod.Log.Debug?.Write($" -- found skill: {skill}");
                    }
                    catch (Exception e)
                    {
                        Mod.Log.Warn?.Write(e, $"Failed to read skill: {tagS} as an integer value, skipping!");
                    }
                }

                // Check salary & bonus values
                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Salary_Multi, StringComparison.InvariantCultureIgnoreCase))
                {
                    string tagS = tag.Substring(ModTags.Tag_Prefix_Ronin_Salary_Multi.Length);
                    try
                    {
                        salaryCfg.Multi = Int32.Parse(tagS, CultureInfo.InvariantCulture);
                        Mod.Log.Debug?.Write($" -- found salaryMulti: {salaryCfg.Multi}");
                    }
                    catch (Exception e)
                    {
                        Mod.Log.Warn?.Write(e, $"Failed to read salaryMulti: {tagS} as an integer value, skipping!");
                    }
                }

                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Salary_Exp, StringComparison.InvariantCultureIgnoreCase))
                {
                    string tagS = tag.Substring(ModTags.Tag_Prefix_Ronin_Salary_Exp.Length);
                    try
                    {
                        salaryCfg.Exponent = float.Parse(tagS, CultureInfo.InvariantCulture);
                        Mod.Log.Debug?.Write($" -- found salaryExp: {salaryCfg.Exponent}");
                    }
                    catch (Exception e)
                    {
                        Mod.Log.Warn?.Write(e, $"Failed to read salaryExp: {tagS} as an integer value, skipping!");
                    }
                }

                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Salary_Variance, StringComparison.InvariantCultureIgnoreCase))
                {
                    string tagS = tag.Substring(ModTags.Tag_Prefix_Ronin_Salary_Variance.Length);
                    try
                    {
                        salaryCfg.Variance = float.Parse(tagS, CultureInfo.InvariantCulture);
                        Mod.Log.Debug?.Write($" -- found salaryVariance: {salaryCfg.Variance}");
                    }
                    catch (Exception e)
                    {
                        Mod.Log.Warn?.Write(e, $"Failed to read salaryVariance: {tagS} as an integer value, skipping!");
                    }
                }

                if (tag.StartsWith(ModTags.Tag_Prefix_Ronin_Bonus_Variance, StringComparison.InvariantCultureIgnoreCase))
                {
                    string tagS = tag.Substring(ModTags.Tag_Prefix_Ronin_Bonus_Variance.Length);
                    try
                    {
                        salaryCfg.BonusVariance = float.Parse(tagS, CultureInfo.InvariantCulture);
                        Mod.Log.Debug?.Write($" -- found bonusVariance: {salaryCfg.BonusVariance}");
                    }
                    catch (Exception e)
                    {
                        Mod.Log.Warn?.Write(e, $"Failed to read bonusVariance: {tagS} as an integer value, skipping!");
                    }
                }

                // Check founder
                if (tag.Equals(ModTags.Tag_Founder, StringComparison.InvariantCultureIgnoreCase))
                {
                    Mod.Log.Debug?.Write($" -- found founder tag");
                    isFounder = true;
                }
            }

            // Normalize size and skill to index values
            CrewDetails details = new CrewDetails(roninDef, type, favored, hated, size - 1, skill - 1, isFounder,
                                                  salaryCfg.IsDefault() ? null : salaryCfg);

            return(details);
        }