@inject IEconomyService economyService
@inject IBuildOrderService buildOrderService
@inject ITimingService timingService
@inject IJSRuntime jsRuntime;
@implements IDisposable

@if (lastRequestedRefreshIndex != requestedRefreshIndex)
{
    <LoadingComponent/>
}
else
{
    <div class="chartsContainer">
        @foreach (var chart in charts)
        {
            <div style="width: @chart.IntervalDisplayMax.ToString()px; height: @chart.ValueDisplayMax.ToString()px">
                <div style="position: relative; border: 2px solid gray; border-radius:2px; width: @chart.IntervalDisplayMax.ToString()px; height: @chart.ValueDisplayMax.ToString()px">
                    @foreach (var point in chart.Points)
                    {
                        <div style="position: absolute;
							bottom:@point.GetValue(chart.HighestValuePoint, chart.ValueDisplayMax)px;
							left:@point.GetInterval(chart.HighestIntervalPoint, chart.IntervalDisplayMax)px;
							width: 0px;
							height: 0px;">
                            <div style="width:1px; height: 1px; border-top-right-radius:10px; border-top-left-radius:10px; border: 2px solid @chart.ChartColor; background-color:@chart.ChartColor">
                            </div>
                        </div>
                    }
                </div>
            </div>
        }
    </div>


    <style>
    .chartsContainer {
        position: relative;
        display: flex;
        gap: 20px;
        flex-wrap: wrap;
        justify-content: center;
        margin-bottom: 20px;
    }
    
</style>

    <FormLayoutComponent>
        <FormDisplayComponent Label="Highest Alloy">
            <Display>@highestAlloyPoint</Display>
        </FormDisplayComponent>
        <FormDisplayComponent Label="Highest Ether">
            <Display>@highestEtherPoint</Display>
        </FormDisplayComponent>
        <DevOnlyComponent>
            <FormDisplayComponent Label="Highest Pyre">
                <Display>@highestEtherPoint</Display>
            </FormDisplayComponent>
        </DevOnlyComponent>
        <FormDisplayComponent Label="Highest Army">
            <Display>@highestArmyPoint</Display>
        </FormDisplayComponent>
    </FormLayoutComponent>
}

@code {

    private readonly int width = 250;

    List<int> valueList = new();

    readonly List<ChartModel> charts = new();


    float highestAlloyPoint;
    float highestEtherPoint;
    float highestPyrePoint;
    float highestArmyPoint;

    private Timer ageTimer = null!;


    protected override void OnInitialized()
    {
        buildOrderService.Subscribe(OnBuilderOrderChanged);
        timingService.Subscribe(OnBuilderOrderChanged);

        ageTimer = new Timer(1000);
        ageTimer.Elapsed += OnAge!;
        ageTimer.Enabled = true;


        GenerateChart();
    }


    int lastRequestedRefreshIndex = 0;

    void OnAge(object? sender, ElapsedEventArgs elapsedEventArgs)
    {
        if (requestedRefreshIndex > 0)
        {
            if (requestedRefreshIndex == lastRequestedRefreshIndex)
            {
                GenerateChart();
                requestedRefreshIndex = 0;
                lastRequestedRefreshIndex = 0;
            }

            lastRequestedRefreshIndex = requestedRefreshIndex;
        }

        ageTimer.Enabled = true;
    }


    void IDisposable.Dispose()
    {
        buildOrderService.Unsubscribe(OnBuilderOrderChanged);
        timingService.Unsubscribe(OnBuilderOrderChanged);
    }


    int requestedRefreshIndex = 0;

    void OnBuilderOrderChanged()
    {
        requestedRefreshIndex++;
        StateHasChanged();
    }


    protected override bool ShouldRender()
    {
#if DEBUG
        jsRuntime.InvokeVoidAsync("console.time", "ChartComponent");
#endif

        return true;
    }

    protected override void OnAfterRender(bool firstRender)
    {
#if DEBUG
        jsRuntime.InvokeVoidAsync("console.timeEnd", "ChartComponent");
#endif
    }

    void GenerateChart()
    {
        var economyOverTime = economyService.GetOverTime();

        charts.Clear();

        var alloyChart = new ChartModel
        {
            IntervalDisplayMax = width,
            ValueDisplayMax = 100,
            ChartColor = "Cyan"
        };
        var etherChart = new ChartModel
        {
            Offset = width,
            IntervalDisplayMax = width,
            ValueDisplayMax = 100,
            ChartColor = "LightGreen"
        };

        var pyreChart = new ChartModel
        {
            Offset = width * 2,
            IntervalDisplayMax = width,
            ValueDisplayMax = 100,
            ChartColor = "Red"
        };

        var armyChart = new ChartModel
        {
            Offset = width * 3,
            IntervalDisplayMax = width,
            ValueDisplayMax = 100,
            ChartColor = "White"
        };

        highestAlloyPoint = 0;
        highestEtherPoint = 0;
        highestPyrePoint = 0;
        highestArmyPoint = 0;

        for (var interval = 0; interval < economyOverTime.Count(); interval++)
        {
            var army = from unit in buildOrderService.GetCompletedBefore(interval)
                where unit.EntityType == EntityType.Army
                select unit;

            var armyValue = 0;
            foreach (var unit in army)
            {
                armyValue += unit.Production().Alloy + unit.Production().Ether;
            }


            highestArmyPoint = Math.Max(highestArmyPoint, armyValue);

            armyChart.Points.Add(new PointModel { Interval = interval, Value = armyValue });
        }


        for (var interval = 0; interval < economyOverTime.Count(); interval++)
        {
            var alloyPoint = new PointModel { Interval = interval };
            var etherPoint = new PointModel { Interval = interval };
            var pyrePoint = new PointModel { Interval = interval };

            var economyAtSecond = economyOverTime[interval];

            var alloyWorkerHarvesters = from harvester in economyAtSecond.HarvestPoints
                where harvester.Harvest() != null
                where harvester.Harvest().RequiresWorker
                where harvester.Harvest().Resource == ResourceType.Alloy
                select harvester;

            var alloyAutomaticHarvesters = from harvester in economyAtSecond.HarvestPoints
                where harvester.Harvest() != null
                where harvester.Harvest().RequiresWorker == false
                where harvester.Harvest().Resource == ResourceType.Alloy
                select harvester;

            var etherAutomaticHarvesters = from harvester in economyAtSecond.HarvestPoints
                where harvester.Harvest() != null
                where harvester.Harvest().RequiresWorker == false
                where harvester.Harvest().Resource == ResourceType.Ether
                select harvester;

            float autoAlloy = 0;
            float workerSlots = 0;
            float workerAlloy = 0;
            float autoEther = 0;

            float economySpending = 0;

            foreach (var alloyAutoHarvester in alloyAutomaticHarvesters)
            {
                autoAlloy += alloyAutoHarvester.Harvest().Slots * alloyAutoHarvester.Harvest().HarvestedPerInterval;
                var production = alloyAutoHarvester.Production();
                if (production != null)
                {
                    economySpending += production.Alloy;
                }
            }

            foreach (var alloyWorkerHarvester in alloyWorkerHarvesters)
            {
                workerSlots += alloyWorkerHarvester.Harvest().Slots;
                var production = alloyWorkerHarvester.Production();
                if (production != null)
                {
                    economySpending += production.Alloy;
                }
            }

            foreach (var etherWorkerHarvester in etherAutomaticHarvesters)
            {
                autoEther += etherWorkerHarvester.Harvest().Slots * etherWorkerHarvester.Harvest().HarvestedPerInterval;
                var production = etherWorkerHarvester.Production();
                if (production != null)
                {
                    economySpending += production.Alloy;
                }
            }

            economySpending += (economyAtSecond.WorkerCount - 6) * 50;

            workerAlloy = Math.Min(economyAtSecond.WorkerCount - economyAtSecond.BusyWorkerCount, workerSlots);


            alloyPoint.TempValue = workerAlloy + autoAlloy;
            etherPoint.Value = autoEther;


            if (interval > 0)
            {
                alloyPoint.TempValue += alloyChart.Points.Last().TempValue;
                etherPoint.Value += etherChart.Points.Last().Value;
                pyrePoint.Value = pyreChart.Points.Last().Value + 1;
            }

            alloyPoint.Value = alloyPoint.TempValue - economySpending;

            highestAlloyPoint = Math.Max(highestAlloyPoint, alloyPoint.Value);
            highestEtherPoint = Math.Max(highestEtherPoint, etherPoint.Value);

            alloyChart.Points.Add(alloyPoint);
            etherChart.Points.Add(etherPoint);
            pyreChart.Points.Add(pyrePoint);
        }

        alloyChart.HighestValuePoint = (int)Math.Max(highestAlloyPoint, 5000.0f);
        etherChart.HighestValuePoint = (int)Math.Max(highestEtherPoint, 2000.0f);
        pyreChart.HighestValuePoint = (int)Math.Max(highestPyrePoint, 2000.0f);

        alloyChart.HighestIntervalPoint = economyOverTime.Count();
        etherChart.HighestIntervalPoint = economyOverTime.Count();
        pyreChart.HighestIntervalPoint = economyOverTime.Count();

        armyChart.HighestValuePoint = (int)Math.Max(highestArmyPoint, 2000.0f);
        armyChart.HighestIntervalPoint = economyOverTime.Count();


        charts.Add(alloyChart);
        charts.Add(etherChart);


    //TODO WIP
    //charts.Add(pyreChart);

        charts.Add(armyChart);

        StateHasChanged();
    }

}