using Friflo.Engine.ECS;
using LittleSim.Content;

namespace LittleSim.Sim;

/// <summary>
/// Диплоидный геном растения: по паре аллелей на ген. Числовые гены экспрессируются гибридно —
/// фенотип есть среднее двух аллелей; дискретный ген морфы — по Менделю (рецессив виден только в
/// гомозиготе). Аллели наследуются (правятся в инспекторе); фенотип-свойства читают системы роста и
/// жизненного цикла. <see cref="Breed"/> моделирует мейоз и мутацию.
/// </summary>
public struct PlantGenome : IComponent
{
    // --- Числовые гены (аллель A/B), фенотип = среднее ------------------------------------------
    public float OptimalLightA;
    public float OptimalLightB;
    public float LightToleranceA;
    public float LightToleranceB;
    public float OptimalTemperatureA;
    public float OptimalTemperatureB;
    public float TemperatureToleranceA;
    public float TemperatureToleranceB;
    public float OptimalFertilityA;
    public float OptimalFertilityB;
    public float FertilityToleranceA;
    public float FertilityToleranceB;
    public float VigorA;
    public float VigorB;
    public float LifespanA;
    public float LifespanB;
    public float DispersalRangeA;
    public float DispersalRangeB;
    public float ReproduceIntervalA;
    public float ReproduceIntervalB;
    public float SelfPollinationA;
    public float SelfPollinationB;
    public float MutationRateA;
    public float MutationRateB;

    // --- Дискретный ген морфы (0 — доминантный, 1 — рецессивный) --------------------------------
    public int MorphA;
    public int MorphB;

    /// <summary>Фенотип: оптимальная освещённость.</summary>
    public readonly float OptimalLight => (OptimalLightA + OptimalLightB) * 0.5f;

    /// <summary>Фенотип: толерантность по свету.</summary>
    public readonly float LightTolerance => (LightToleranceA + LightToleranceB) * 0.5f;

    /// <summary>Фенотип: оптимальная температура.</summary>
    public readonly float OptimalTemperature => (OptimalTemperatureA + OptimalTemperatureB) * 0.5f;

    /// <summary>Фенотип: толерантность по температуре.</summary>
    public readonly float TemperatureTolerance =>
        (TemperatureToleranceA + TemperatureToleranceB) * 0.5f;

    /// <summary>Фенотип: оптимальная плодородность.</summary>
    public readonly float OptimalFertility => (OptimalFertilityA + OptimalFertilityB) * 0.5f;

    /// <summary>Фенотип: толерантность по плодородности.</summary>
    public readonly float FertilityTolerance => (FertilityToleranceA + FertilityToleranceB) * 0.5f;

    /// <summary>Фенотип: бодрость роста.</summary>
    public readonly float Vigor => (VigorA + VigorB) * 0.5f;

    /// <summary>Фенотип: продолжительность жизни (дней).</summary>
    public readonly float Lifespan => (LifespanA + LifespanB) * 0.5f;

    /// <summary>Фенотип: дальность расселения (клеток).</summary>
    public readonly float DispersalRange => (DispersalRangeA + DispersalRangeB) * 0.5f;

    /// <summary>Фенотип: интервал размножения (дней).</summary>
    public readonly float ReproduceInterval => (ReproduceIntervalA + ReproduceIntervalB) * 0.5f;

    /// <summary>Фенотип: способность к самоопылению (0..1).</summary>
    public readonly float SelfPollination => (SelfPollinationA + SelfPollinationB) * 0.5f;

    /// <summary>Фенотип: темп мутаций (0..1).</summary>
    public readonly float MutationRate => (MutationRateA + MutationRateB) * 0.5f;

    /// <summary>Фенотип морфы: рецессивный вариант проявляется только в гомозиготе (1,1).</summary>
    public readonly bool IsVariant => MorphA == 1 && MorphB == 1;

    /// <summary>
    /// Генерирует особь из базового генома вида: числовые аллели — база ± относительный разброс
    /// (<see cref="GenomeDef.Spread"/>); аллель морфы = рецессивный (1) с вероятностью
    /// <see cref="GenomeDef.VariantChance"/>. Сидируется <paramref name="random"/> — детерминизм.
    /// </summary>
    public static PlantGenome FromDef(GenomeDef def, Random random)
    {
        float Allele(float baseValue) =>
            baseValue + (random.NextSingle() * 2f - 1f) * def.Spread * baseValue;

        int MorphAllele() => random.NextSingle() < def.VariantChance ? 1 : 0;

        return new PlantGenome
        {
            OptimalLightA = Allele(def.OptimalLight),
            OptimalLightB = Allele(def.OptimalLight),
            LightToleranceA = Allele(def.LightTolerance),
            LightToleranceB = Allele(def.LightTolerance),
            OptimalTemperatureA = Allele(def.OptimalTemperature),
            OptimalTemperatureB = Allele(def.OptimalTemperature),
            TemperatureToleranceA = Allele(def.TemperatureTolerance),
            TemperatureToleranceB = Allele(def.TemperatureTolerance),
            OptimalFertilityA = Allele(def.OptimalFertility),
            OptimalFertilityB = Allele(def.OptimalFertility),
            FertilityToleranceA = Allele(def.FertilityTolerance),
            FertilityToleranceB = Allele(def.FertilityTolerance),
            VigorA = Allele(def.Vigor),
            VigorB = Allele(def.Vigor),
            LifespanA = Allele(def.Lifespan),
            LifespanB = Allele(def.Lifespan),
            DispersalRangeA = Allele(def.DispersalRange),
            DispersalRangeB = Allele(def.DispersalRange),
            ReproduceIntervalA = Allele(def.ReproduceInterval),
            ReproduceIntervalB = Allele(def.ReproduceInterval),
            SelfPollinationA = Allele(def.SelfPollination),
            SelfPollinationB = Allele(def.SelfPollination),
            MutationRateA = Allele(def.MutationRate),
            MutationRateB = Allele(def.MutationRate),
            MorphA = MorphAllele(),
            MorphB = MorphAllele(),
        };
    }

    /// <summary>
    /// Потомок двух родителей (мейоз): аллель A — случайный из родителя <paramref name="a"/>, аллель
    /// B — из <paramref name="b"/>; затем мутация (с вероятностью среднего темпа мутаций родителей —
    /// сдвиг числового аллеля на малую долю / переключение морфы). Самоопыление = <c>Breed(self, self)</c>.
    /// </summary>
    public static PlantGenome Breed(in PlantGenome a, in PlantGenome b, Random rng)
    {
        var rate = (a.MutationRate + b.MutationRate) * 0.5f;

        float Mut(float v) =>
            rng.NextSingle() < rate ? v + (rng.NextSingle() * 2f - 1f) * 0.12f * v : v;
        float FromA(float x, float y) => Mut(rng.NextSingle() < 0.5f ? x : y);
        int MutMorph(int m) => rng.NextSingle() < rate ? 1 - m : m;

        return new PlantGenome
        {
            OptimalLightA = FromA(a.OptimalLightA, a.OptimalLightB),
            OptimalLightB = FromA(b.OptimalLightA, b.OptimalLightB),
            LightToleranceA = FromA(a.LightToleranceA, a.LightToleranceB),
            LightToleranceB = FromA(b.LightToleranceA, b.LightToleranceB),
            OptimalTemperatureA = FromA(a.OptimalTemperatureA, a.OptimalTemperatureB),
            OptimalTemperatureB = FromA(b.OptimalTemperatureA, b.OptimalTemperatureB),
            TemperatureToleranceA = FromA(a.TemperatureToleranceA, a.TemperatureToleranceB),
            TemperatureToleranceB = FromA(b.TemperatureToleranceA, b.TemperatureToleranceB),
            OptimalFertilityA = FromA(a.OptimalFertilityA, a.OptimalFertilityB),
            OptimalFertilityB = FromA(b.OptimalFertilityA, b.OptimalFertilityB),
            FertilityToleranceA = FromA(a.FertilityToleranceA, a.FertilityToleranceB),
            FertilityToleranceB = FromA(b.FertilityToleranceA, b.FertilityToleranceB),
            VigorA = FromA(a.VigorA, a.VigorB),
            VigorB = FromA(b.VigorA, b.VigorB),
            LifespanA = FromA(a.LifespanA, a.LifespanB),
            LifespanB = FromA(b.LifespanA, b.LifespanB),
            DispersalRangeA = FromA(a.DispersalRangeA, a.DispersalRangeB),
            DispersalRangeB = FromA(b.DispersalRangeA, b.DispersalRangeB),
            ReproduceIntervalA = FromA(a.ReproduceIntervalA, a.ReproduceIntervalB),
            ReproduceIntervalB = FromA(b.ReproduceIntervalA, b.ReproduceIntervalB),
            SelfPollinationA = FromA(a.SelfPollinationA, a.SelfPollinationB),
            SelfPollinationB = FromA(b.SelfPollinationA, b.SelfPollinationB),
            MutationRateA = FromA(a.MutationRateA, a.MutationRateB),
            MutationRateB = FromA(b.MutationRateA, b.MutationRateB),
            MorphA = MutMorph(rng.NextSingle() < 0.5f ? a.MorphA : a.MorphB),
            MorphB = MutMorph(rng.NextSingle() < 0.5f ? b.MorphA : b.MorphB),
        };
    }
}
